/*
 *  Off-the-Record Messaging Toolkit
 *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits, Chris Alexander,
 *                           Nikita Borisov
 *                           <otr@cypherpunks.ca>
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* system headers */
#include <stdio.h>
#include <stdlib.h>

/* libotr headers */
#include "proto.h"

/* toolkit headers */
#include "readotr.h"
#include "parse.h"

static void parse(const char *msg)
{
    OtrlMessageType mtype = otrl_proto_message_type(msg);
    CommitMsg cmsg;
    KeyMsg kmsg;
    RevealSigMsg rmsg;
    SignatureMsg smsg;
    KeyExchMsg keyexch;
    DataMsg datamsg;

    switch(mtype) {
	case OTRL_MSGTYPE_QUERY:
	    printf("OTR Query:\n\t%s\n\n", msg);
	    break;
	case OTRL_MSGTYPE_DH_COMMIT:
	    cmsg = parse_commit(msg);
	    if (!cmsg) {
		printf("Invalid D-H Commit Message\n\n");
		break;
	    }

	    printf("D-H Commit Message:\n");

	    dump_data(stdout, "\tVersion", &(cmsg->version), 1);
	    if (cmsg->version == 3) {
		dump_int(stdout, "\tSender instance", cmsg->sender_instance);
		dump_int(stdout, "\tReceiver instance",
			cmsg->receiver_instance);
	    }
	    dump_data(stdout, "\tEncrypted Key", cmsg->enckey,
		    cmsg->enckeylen);
	    dump_data(stdout, "\tHashed Key", cmsg->hashkey,
		    cmsg->hashkeylen);
	    printf("\n");
	    free_commit(cmsg);
	    break;
	case OTRL_MSGTYPE_DH_KEY:
	    kmsg = parse_key(msg);
	    if (!kmsg) {
		printf("Invalid D-H Key Message\n\n");
		break;
	    }
	    printf("D-H Key Message:\n");
	    dump_data(stdout, "\tVersion", &(kmsg->version), 1);
	    if (kmsg->version == 3) {
		dump_int(stdout, "\tSender instance", kmsg->sender_instance);
		dump_int(stdout, "\tReceiver instance",
			kmsg->receiver_instance);
	    }
	    dump_mpi(stdout, "\tD-H Key", kmsg->y);
	    printf("\n");
	    free_key(kmsg);
	    break;
	case OTRL_MSGTYPE_REVEALSIG:
	    rmsg = parse_revealsig(msg);
	    if (!rmsg) {
		printf("Invalid Reveal Signature Message\n\n");
		break;
	    }
	    printf("Reveal Signature Message:\n");
	    dump_data(stdout, "\tVersion", &(rmsg->version), 1);
	    if (rmsg->version == 3) {
		dump_int(stdout, "\tSender instance", rmsg->sender_instance);
		dump_int(stdout, "\tReceiver instance",
			rmsg->receiver_instance);
	    }
	    dump_data(stdout, "\tKey", rmsg->key, rmsg->keylen);
	    dump_data(stdout, "\tEncrypted Signature",
		    rmsg->encsig, rmsg->encsiglen);
	    dump_data(stdout, "\tMAC", rmsg->mac, 20);
	    printf("\n");
	    free_revealsig(rmsg);
	    break;
	case OTRL_MSGTYPE_SIGNATURE:
	    smsg = parse_signature(msg);
	    if (!smsg) {
		printf("Invalid Signature Message\n\n");
		break;
	    }
	    printf("Signature Message:\n");
	    dump_data(stdout, "\tVersion", &(smsg->version), 1);
	    if (smsg->version == 3) {
		dump_int(stdout, "\tSender instance", smsg->sender_instance);
		dump_int(stdout, "\tReceiver instance",
			smsg->receiver_instance);
	    }
	    dump_data(stdout, "\tEncrypted Signature",
		    smsg->encsig, smsg->encsiglen);
	    dump_data(stdout, "\tMAC", smsg->mac, 20);
	    printf("\n");
	    free_signature(smsg);
	    break;
	case OTRL_MSGTYPE_V1_KEYEXCH:
	    keyexch = parse_keyexch(msg);
	    if (!keyexch) {
		printf("Invalid Key Exchange Message\n\n");
		break;
	    }
	    printf("Key Exchange Message:\n");
	    dump_int(stdout, "\tReply", keyexch->reply);
	    dump_mpi(stdout, "\tDSA p", keyexch->p);
	    dump_mpi(stdout, "\tDSA q", keyexch->q);
	    dump_mpi(stdout, "\tDSA g", keyexch->g);
	    dump_mpi(stdout, "\tDSA e", keyexch->e);
	    dump_int(stdout, "\tKeyID", keyexch->keyid);
	    dump_mpi(stdout, "\tDH y", keyexch->y);
	    dump_mpi(stdout, "\tSIG r", keyexch->r);
	    dump_mpi(stdout, "\tSIG s", keyexch->s);
	    printf("\n");
	    free_keyexch(keyexch);
	    break;
	case OTRL_MSGTYPE_DATA:
	    datamsg = parse_datamsg(msg);
	    if (!datamsg) {
		printf("Invalid Data Message\n\n");
		break;
	    }
	    printf("Data Message:\n");

	    dump_data(stdout, "\tVersion", &(datamsg->version), 1);
	    if (datamsg->flags >= 0) {
		dump_int(stdout, "\tFlags", datamsg->flags);
	    }

	    if (datamsg->version == 3) {
		dump_int(stdout, "\tSender instance", datamsg->sender_instance);
		dump_int(stdout, "\tReceiver instance",
			datamsg->receiver_instance);
	    }

	    dump_int(stdout, "\tSender keyid", datamsg->sender_keyid);
	    dump_int(stdout, "\tRcpt keyid", datamsg->rcpt_keyid);
	    dump_mpi(stdout, "\tDH y", datamsg->y);
	    dump_data(stdout, "\tCounter", datamsg->ctr, 8);
	    dump_data(stdout, "\tEncrypted message", datamsg->encmsg,
		    datamsg->encmsglen);
	    dump_data(stdout, "\tMAC", datamsg->mac, 20);
	    if (datamsg->mackeyslen > 0) {
		size_t len = datamsg->mackeyslen;
		unsigned char *mks = datamsg->mackeys;
		unsigned int i = 0;
		printf("\tRevealed MAC keys:\n");

		while(len > 19) {
		    char title[20];
		    sprintf(title, "\t\tKey %u", ++i);
		    dump_data(stdout, title, mks, 20);
		    mks += 20; len -= 20;
		}
	    }

	    printf("\n");
	    free_datamsg(datamsg);
	    break;
	case OTRL_MSGTYPE_ERROR:
	    printf("OTR Error:\n\t%s\n\n", msg);
	    break;
	case OTRL_MSGTYPE_TAGGEDPLAINTEXT:
	    printf("Tagged plaintext message:\n\t%s\n\n", msg);
	    break;
	case OTRL_MSGTYPE_NOTOTR:
	    printf("Not an OTR message:\n\t%s\n\n", msg);
	    break;
	case OTRL_MSGTYPE_UNKNOWN:
	    printf("Unrecognized OTR message:\n\t%s\n\n", msg);
	    break;
    }
    fflush(stdout);
}

static void usage(const char *progname)
{
    fprintf(stderr, "Usage: %s\n"
"Read Off-the-Record (OTR) Key Exchange and/or Data messages from stdin\n"
"and display their contents in a more readable format.\n", progname);
    exit(1);
}

int main(int argc, char **argv)
{
    char *otrmsg = NULL;

    if (argc != 1) {
	usage(argv[0]);
    }

    while ((otrmsg = readotr(stdin)) != NULL) {
	parse(otrmsg);
	free(otrmsg);
    }

    return 0;
}
