// This is a personal academic project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>

#include "args.h"

static unsigned short port(const char *s) {
     char *end = 0;
     const long sl = strtol(s, &end, 10);

     if (end == s || '\0' != *end || ((LONG_MIN == sl || LONG_MAX == sl) && ERANGE == errno) || sl < 0 || sl > USHRT_MAX) {
         fprintf(stderr, "Port should be in the range of 1-65536: %s\n", s);
         exit(1);
         return 1;
     }
     return (unsigned short) sl;
}

static void user(char * s, user_t * user) {
    char *p = strchr(s, ':');
    if (p == NULL) {
        fprintf(stderr, "Password not found\n");
        exit(1);
    } else {
        *p = 0;
        p++;
        char * uname = malloc(strlen(s) + 1);
        if (uname == NULL)
            return;
        strcpy(uname, s);
        char * pass = malloc(strlen(p) + 1);
        if (pass == NULL) {
            free(uname);
            return;
        }
        strcpy(pass, p);
        user->name = uname;
        user->pass = pass;
        user->ulen = strlen(uname) + 1;
    }
}

static void version(void) {
    fprintf(stderr, "BProxy v1.0\nITBA Protocolos de Comunicación 2022/1C -- Group 7\nSee LICENSE.md\n");
}

static void usage(const char *progname) {
    fprintf(stderr,
        "Usage: %s [OPTION]\n\n"
        "   -h               Prints help.\n"
        "   -l <SOCKS addr>  Address where proxy SOCKS will be served.\n"
        "   -L <conf addr>   Address where management service will be served.\n"
        "   -p <SOCKS port>  Port for incoming SOCKS connections.\n"
        "   -P <conf port>   Port for incoming configuration connections.\n"
        "   -u <name>:<pass> Username and password of proxy user. Up to 1024.\n"
        "   -v               Prints version information.\n\n",
        progname);
    exit(EXIT_SUCCESS);
}

static void print_logo() {
    printf("\033[0;36m");
    printf("$$\\\n"                                                         
    "$$ |\n"                                                        
    "$$ |\n"                                                        
    "$$$$$$$\\   $$$$$$\\   $$$$$$\\   $$$$$$\\  $$\\   $$\\ $$\\   $$\\ \n"
    "$$  __$$\\ $$  __$$\\ $$  __$$\\ $$  __$$\\ \\$$\\ $$  |$$ |  $$ |\n"
    "$$ |  $$ |$$ /  $$ |$$ |  \\__|$$ /  $$ | \\$$$$  / $$ |  $$ |\n"
    "$$ |  $$ |$$ |  $$ |$$ |      $$ |  $$ | $$  $$<  $$ |  $$ |\n"
    "$$$$$$$  |$$$$$$$  |$$ |      \\$$$$$$  |$$  /\\$$\\ \\$$$$$$$ |\n"
    "\\_______/ $$  ____/ \\__|       \\______/ \\__/  \\__| \\____$$ |\n"
    "          $$ |                                    $$\\   $$ |\n"
    "          $$ |                                    \\$$$$$$  |\n"
    "          \\__|                                     \\______/\n");
    printf("\033[0m\n");
}

void parse_args(const int argc, char ** argv, struct socks5args * args) {
    memset(args, 0, sizeof(*args));

    args->socks_addr = NULL;
    args->socks_port = 1080;

    args->mng_addr   = NULL;
    args->mng_port   = 8080;

    args->dissector_enabled = true;

    int c;
    int nusers = 0;
    while (true) {
        c = getopt(argc, argv, "hl:L:p:P:u:v");
        if (c == -1)
            break;

        switch (c) {
            case 'h':
                usage(argv[0]);
                break;
            case 'l':
                args->socks_addr = optarg;
                break;
            case 'L':
                args->mng_addr = optarg;
                break;
            case 'p':
                args->socks_port = port(optarg);
                break;
            case 'P':
                args->mng_port = port(optarg);
                break;
            case 'u':
                if (nusers >= MAX_USERS) {
                    fprintf(stderr, "Maximum number of command line users reached: %d.\n", MAX_USERS);
                    exit(1);
                } else {
                    user(optarg, args->users + nusers);
                    nusers++;
                }
                break;
            case 'v':
                version();
                exit(EXIT_SUCCESS);
            default:
                fprintf(stderr, "Unknown argument %d.\n", c);
                exit(1);
        }
    }

    args->nusers = nusers;

    if (optind < argc) {
        fprintf(stderr, "Argument not accepted: ");
        while (optind < argc) {
            fprintf(stderr, "%s ", argv[optind++]);
        }
        fprintf(stderr, "\n");
        exit(1);
    }

    print_logo();
}
