/**
 **	This module lets a user see see the SHA1 fingerprint of their client
 ** SSL-certificate or an oper see the SHA1 fingerprint of any users client
 ** SSL-certificate
 **	.
 **	On load the command /fingerprint is added which takes a nick as a parameter
 **	whose certificate information should be retrieved, if the user is not an oper
 ** he/she can only get his/her own SSL-certificate fingerprint:
 **
 **	/FINGERPRINT <nick>
 **
 **	Changelog:
 **
 **	Version 0.1
 **		- Initial release
 **/

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif

#define MSG_FINGERPRINT	"FINGERPRINT"
#define TOK_FINGERPRINT	"FP"

static int	 m_fingerprint(aClient *, aClient *, int, char *[]);
static Command* cmdFingerprint = NULL;

ModuleHeader MOD_HEADER(m_fingerprint)
  = {
	"m_fingerprint",
	"$Id: m_fingerprint.c,v 0.1 2011/05/11 Jobe Exp $",
	"Shows client's certificate fingerprint (SHA1)",
	"3.2-b8-1",
	NULL 
    };

DLLFUNC int MOD_TEST(m_fingerprint)(ModuleInfo *modinfo)
{
	return MOD_SUCCESS;
}

DLLFUNC int MOD_INIT(m_fingerprint)(ModuleInfo *modinfo)
{
#ifdef USE_SSL
	if (CommandExists(MSG_FINGERPRINT))
    	{
		config_error("Command %s already exists", MSG_FINGERPRINT);
		return MOD_FAILED;
    	}
    	if (CommandExists(TOK_FINGERPRINT))
	{
		config_error("Token %s already exists", TOK_FINGERPRINT);
		return MOD_FAILED;
    	}

	cmdFingerprint = CommandAdd(modinfo->handle, MSG_FINGERPRINT, TOK_FINGERPRINT, m_fingerprint, MAXPARA, 0);

	if (!cmdFingerprint || (ModuleGetError(modinfo->handle) != MODERR_NOERROR)) {
		config_error("Error while loading module.");
		return MOD_FAILED;
	}
#endif

	return MOD_SUCCESS;
}

DLLFUNC int MOD_LOAD(m_fingerprint)(int module_load)
{
	return MOD_SUCCESS;
}

DLLFUNC int MOD_UNLOAD(m_fingerprint)(int module_unload)
{
	if (cmdFingerprint) {
		CommandDel(cmdFingerprint);
		cmdFingerprint = NULL;
	}

	return MOD_SUCCESS;
}

int m_fingerprint(aClient *cptr, aClient *sptr, int parc, char *parv[]) {
#ifdef USE_SSL
	char* nick = NULL;
	aClient* acptr;
	SSL* ssl = NULL;
	X509* x509 = NULL;
	unsigned int n;
	unsigned int i;
	unsigned int j;
	unsigned char md[EVP_MAX_MD_SIZE];
	char hex[EVP_MAX_MD_SIZE * 2];
	char hexchars[16] = "0123456789abcdef";
	const EVP_MD *digest = EVP_sha1();

	if ((parc > 1) && IsOper(sptr) && (hunt_server_token(cptr, sptr, MSG_FINGERPRINT, TOK_FINGERPRINT, "%s", 1, parc, parv) != HUNTED_ISME)) {
		return 0;
	}

	if (IsPerson(sptr)) {
		if (!MyConnect(sptr) && !IsServer(cptr)) {
			return 0;
		}
	} else {
		return 0;
	}

	nick = (IsOper(sptr) && parc > 1 && !BadPtr(parv[1])) ? parv[1] : NULL;
	if (!nick) {
		acptr = sptr;
	}
	else {
		acptr = find_person(nick, NULL);
		if (!acptr) {
			sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, nick);
			return -1;
		}
	}

	if (acptr->ssl) {
		ssl = (SSL*)acptr->ssl;
	}
	if (!ssl) {
		sendto_one(sptr, ":%s %s %s :*** Couldn't retrieve SSL/TLS structure of %s.", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, acptr->name);
		return -1;
	}

	x509 = SSL_get_peer_certificate(ssl);

	if (!x509) {
		sendto_one(sptr, ":%s %s %s :*** Couldn't retrieve certificate of %s.", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, acptr->name);
		return -1;
	}

	if (!X509_digest(x509, digest, md, &n)) {
		sendto_one(sptr, ":%s %s %s :*** Couldn't retrieve certificate fingerprint of %s.", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, acptr->name);
		X509_free(x509);
		return -1;
	}
	
	j = 0;
	for (i=0; i<n; i++) {
		hex[j++] = hexchars[(md[i] >> 4) & 0xF];
		hex[j++] = hexchars[md[i] & 0xF];
	}
	hex[j++] = '\0';

	/* Error checking done.. */
	sendto_one(sptr, ":%s %s %s :*** %s has the certificate fingerprint (SHA1) %s", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", sptr->name, acptr->name, hex);
	X509_free(x509);
#endif
}

