/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright (C) 2023 Eneas Ulir de Queiroz
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "uencrypt.h"

static void check_enc_dec(const int enc)
{
    if (enc == -1)
	return;
    fprintf(stderr, "Error: both -d and -e were specified.\n");
    exit(EXIT_FAILURE);
}

static void show_usage(const char* name)
{
    fprintf(stderr, "Usage: %s: [-d | -e] [-n] -k key [-i iv] [-c cipher]\n"
		    "-d = decrypt; -e = encrypt; -n = no padding\n", name);
}

static void uencrypt_clear_free(void *ptr, size_t len)
{
    if (ptr) {
	memset(ptr, 0, len);
	free(ptr);
    }
}

int main(int argc, char *argv[])
{
    int enc = -1;
    unsigned char *iv = NULL;
    unsigned char *key = NULL;
    long keylen = 0, ivlen = 0;
    int opt;
    int padding = 1;
    const cipher_t *cipher = get_default_cipher();
    ctx_t* ctx;
    int ret = EXIT_FAILURE;

    while ((opt = getopt(argc, argv, "c:dei:k:n")) != -1) {
	switch (opt) {
	case 'c':
	    if (!(cipher = get_cipher_or_print_error(optarg)))
		exit(EXIT_FAILURE);
	    break;
	case 'd':
	    check_enc_dec(enc);
	    enc = 0;
	    break;
	case 'e':
	    check_enc_dec(enc);
	    enc = 1;
	    break;
	case 'i':
	    iv = hexstr2buf(optarg, &ivlen);
	    if (iv == NULL) {
		fprintf(stderr, "Error setting IV to %s. The IV should be encoded in hex.\n",
			optarg);
		exit(EINVAL);
	    }
	    memset(optarg, '*', strlen(optarg));
	    break;
	case 'k':
	    key = hexstr2buf(optarg, &keylen);
	    if (key == NULL) {
		fprintf(stderr, "Error setting key to %s. The key should be encoded in hex.\n",
			optarg);
		exit(EINVAL);
	    }
	    memset(optarg, '*', strlen(optarg));
	    break;
	case 'n':
	    padding = 0;
	    break;
	default:
	    show_usage(argv[0]);
	    exit(EINVAL);
	}
    }
    if (ivlen != get_cipher_ivsize(cipher)) {
	fprintf(stderr, "Error: IV must be %d bytes; given IV is %ld bytes.\n",
		get_cipher_ivsize(cipher), ivlen);
	exit(EXIT_FAILURE);
    }
    if (keylen != get_cipher_keysize(cipher)) {
	fprintf(stderr, "Error: key must be %d bytes; given key is %ld bytes.\n",
		get_cipher_keysize(cipher), keylen);
	exit(EXIT_FAILURE);
    }
    ctx = create_ctx(cipher, key, iv, !!enc, padding);
    if (ctx) {
	ret = do_crypt(stdin, stdout, ctx);
	free_ctx(ctx);
    }
    uencrypt_clear_free(iv, ivlen);
    uencrypt_clear_free(key, keylen);
    return ret;
}
