/*
 *      common.c from Access Point SNMP Utils for Linux
 *
 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
 * Copyright (c) 2005 Jan Rafaj <jr-aputils at cedric dot unob dot cz>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 from
 * June 1991 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 <string.h>
#include <menu.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include "ap-utils.h"

#define APIP _("Access Point IP-address: ")
#define APPASS _("Password (community): ")
#define AUTODETECT _("Autodetect AP MIB properties? ")
#define APTYPE _("AP MIB type: ")
#define APVENDOREXT _("AP MIB vendor extensions: ")
#define WANT_SYS_APLABEL _("Do you want to use AP's name as its label? ")
#define APLABEL _("Access Point label: ")
#define SAVESETTINGS _("Save connect-settings: ")
#define POLL_I _("[P] Polling mode interval (tenths of second): ")
#define POLL_HELP _("P - change polling mode interval; Q - quit to menu")

char *ap_types[] = {
    "ATMEL410",
    "NWN",
    "ATMEL12350"
};

char *ap_vendorexts[][3] = {
    { "NONE", "SBRIDGES" },		/* ATMEL410 exts */
    { "NONE" },				/* NWN exts */
    { "NONE", "GEMTEK", "EZYNET" },	/* ATMEL12350 exts */
    { 0 }				/* do not delete! */
};

rdprops regdom_types[] = {
    { 0x10, "FCC (USA)", 1, 11 },
    { 0x20, "DOC (Canada)", 1, 11 },
    { 0x30, "ETSI (Europe)", 1, 13 },
    { 0x31, "Spain", 10, 2 },
    { 0x32, "France", 10, 4 },
    { 0x40, "MKK (Japan)", 14, 1 },
    { 0x41, "MKK1 (Japan)", 1, 14 },
    { 0x42, "Israel", 3, 7 },
    { 0x00, "unknown", 1, 14 }	/* hopefully 0x00 is not taken by any? */
};

char *channels[] = {
    "01", "02", "03", "04", "05", "06", "07",
    "08", "09", "10", "11", "12", "13", "14"
};

extern WINDOW *main_sub;
extern char *community, *prog_title;
extern struct in_addr ap_ip;

short ap_type, ap_vendorext;
int poll_delay = 10;

void about()
{
    int nrow = 14, ncol = 47, brow = (LINES - 5 - nrow) / 2, bcol =
	(COLS - MCOLS - 2 - ncol) / 2, i;
    char message[100];

    noecho();
    curs_set(0);

    print_top(NULL, _("About"));

    /* drawing the box */
    mvwaddch(main_sub, brow, bcol, ACS_ULCORNER);
    mvwaddch(main_sub, brow, bcol + ncol, ACS_URCORNER);
    mvwaddch(main_sub, brow + nrow, bcol, ACS_LLCORNER);
    mvwaddch(main_sub, brow + nrow, bcol + ncol, ACS_LRCORNER);
    for (i = 1; i < ncol; i++) {
	mvwaddch(main_sub, brow, bcol + i, ACS_HLINE);
	mvwaddch(main_sub, brow + nrow, bcol + i, ACS_HLINE);
    }
    for (i = 1; i < nrow; i++) {
	mvwaddch(main_sub, brow + i, bcol, ACS_VLINE);
	mvwaddch(main_sub, brow + i, bcol + ncol, ACS_VLINE);
    }

    mvwaddstr(main_sub, brow + 1, bcol + 2, prog_title);
    sprintf(message, _("From %s"), TITLE);
    mvwaddstr(main_sub, brow + 2, bcol + 2, message);
    sprintf(message, _("Version %s"), VERSION);
    mvwaddstr(main_sub, brow + 3, bcol + 2, message);
    mvwaddstr(main_sub, brow + 5, bcol + 2,
	_("Written by Roman Festchook roma@polesye.net"));
    mvwaddstr(main_sub, brow + 6, bcol + 2,
	_("and Jan Rafaj jr-aputils@cedric.unob.cz"));
    mvwaddstr(main_sub, brow + 7, bcol + 2,
	_("Copyright (c) 2001-2005"));
    mvwaddstr(main_sub, brow + 8, bcol + 2,
	_("Roman Festchook and Jan Rafaj"));
    mvwaddstr(main_sub, brow + 9, bcol + 2, "http://ap-utils.polesye.net/");
    mvwaddstr(main_sub, brow + 11, bcol + 2,
	_("This program is distributed under the terms"));
    mvwaddstr(main_sub, brow +12, bcol + 2,
	_("of the GNU General Public License version 2."));
    mvwaddstr(main_sub, brow + 13, bcol + 2,
	_("See the included COPYING file for details."));

    wrefresh(main_sub);

    print_help(ANY_KEY);
    getch();
    print_help("");
    print_top(NULL, NULL);
    clear_main(0);
    return;
}

void connect_options(unsigned long int ip, int type)
{
    /*
     * operAccessPointName OIDs used to retrieve AP NAME [in order of
     * corresponding AP MIB types: ATMEL410, NWN, ATMEL12350]
     */
    char operAccessPointName[3][12] = {
      {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x0A, 0x00},
      {0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x05, 0x00},
      {0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x02, 0x01, 0x0A, 0x00}
    };

    extern int atmel410_filter; /* boolean; = 1 if we call from ap-gl utility */
    unsigned char message[256];
    unsigned char label[17];
    int fd, i;
    char *home_dir;
    char save_settings=1;
    varbind varbinds[1];

    if (reopen_sockfd() == -1) {
	print_helperr(ERR_SOCKET);
	getch();
	goto exit;
    }

    noecho();
    curs_set(0);

    print_top(NULL, _("Connect options"));

get_all_again:
    mvwaddstr(main_sub, 1, 1, APIP);
    if (ip) {
	ap_ip.s_addr = ip;
	mvwaddstr(main_sub, 1, 1 + strlen(APIP), inet_ntoa(ap_ip));
    } else {
	print_help(_("Enter IP address of your Access Point."));
	get_ip(&ap_ip, 1, 1 + strlen(APIP), NULL);
    }

    mvwaddstr(main_sub, 2, 1, APPASS);

    i = 0;
    print_help("");

    print_help(_("Entered characters will not be displayed "
		 "for security reason."));
    get_pass(message, 2, 1 + strlen(APPASS), 16);
    if (community)
	free(community);
    i = strlen(message) + 1;
    community = (char *) malloc(i);
    strncpy(community, message, i);

    if (type && !atmel410_filter) {
	/* entered from ap_search() */

	/* community already exists */
/*	wattrset(main_sub, A_BOLD);
	for(i = 0; community[i++]; waddch(main_sub, '*'));
	wattrset(main_sub, A_NORMAL);
*/
	ap_type = --type;

	print_menusel(3, 1, APTYPE, ap_types[ap_type]);

	/* well, we already know the MIB type, but lets find out MIB ext */
	get_mib_details();

	/* marks that we want offer AP name -> AP label later */
	i = 1;

	mvwaddstr(main_sub, 4, 1, APVENDOREXT);
    } else {
	if (atmel410_filter) {
	    ap_type = --type;
	    print_menusel(3, 1, APTYPE, ap_types[ap_type]);
	    ap_vendorext = NONE; /* no need to choose here, really */
	    /* marks that we want offer AP name -> AP label later */
	    i = 1;
	    mvwaddstr(main_sub, 4, 1, APVENDOREXT);
	} else {
	    mvwaddstr(main_sub, 3, 1, AUTODETECT);
	    wrefresh(main_sub);
	    i = yes_no(3, 1 + strlen(AUTODETECT));
	    clear_main_new(3, 4);
	    mvwaddstr(main_sub, 3, 1, APTYPE);
	    if (i == 2) { /* NO */
		wrefresh(main_sub);
		ap_type = menu_choose(3, 1 + strlen(APTYPE), ap_types, 3);
		print_bold(main_sub, ap_types[ap_type]);
		mvwaddstr(main_sub, 4, 1, APVENDOREXT);
		wrefresh(main_sub);
		for (i = 0; ap_vendorexts[ap_type][i]; i++);
		if (i == 1)
		    ap_vendorext = NONE; /* no need to choose here, really */
		else
		    ap_vendorext = menu_choose(4, 1 + strlen(APVENDOREXT),
		    ap_vendorexts[ap_type], i);
	    } else { /* YES */
		if (get_mib_details() == -1) {
		    clear_main_new(1, 4);
		    goto get_all_again;
		}
		print_bold(main_sub, ap_types[ap_type]);
		mvwaddstr(main_sub, 4, 1, APVENDOREXT);
	    }
	}
    }
    print_bold(main_sub, ap_vendorexts[ap_type][ap_vendorext]);

    print_help(
	_("This label will be stored on HDD (independently on AP name!)."));
    if (i == 1) {
	/*
	 * (being called from ap_search()) or (YES for AP type autodetection)
	 * => offer possibility to use AP's name as the label
	 */
	mvwaddstr(main_sub, 5, 1, WANT_SYS_APLABEL);
	wrefresh(main_sub);
	if (yes_no(5, 1 + strlen(WANT_SYS_APLABEL)) == 1) { /* YES */
	    varbinds[0].oid = operAccessPointName[ap_type];
	    varbinds[0].len_oid = (ap_type == NWN ?
		8 : sizeof(operAccessPointName[ap_type]));
	    varbinds[0].type = NULL_VALUE;
	    varbinds[0].len_val = 0;

	    print_help(WAIT_RET);
	    if (snmp(varbinds, i, GET) <= 0) {
		print_helperr(ERR_RET);
		getch();
	    } else {
		clear_main_new(5, 6);
		mvwaddstr(main_sub, 5, 1, APLABEL);
		wattrset(main_sub, A_BOLD);
		for (i = 0; i < varbinds[0].len_val && i < 16 &&
		     *(varbinds[0].value + i); i++) {
		    label[i] = *(varbinds[0].value + i);
		    waddch(main_sub, label[i]);
		}
		label[i] = '\0';
		wattrset(main_sub, A_NORMAL);
		if (strlen(varbinds[0].value) > 16) {
		    print_helperr("Warning! AP LABEL truncated to first 16 characters of AP NAME. Press any key.");
		    getch();
		}
	    }
	    print_help("");
	    i = -1;
	}
    }

    if (i != -1) { /* we have NOT been asked with WANT_SYS_APLABEL */
	clear_main_new(5, 6);
	mvwaddstr(main_sub, 5, 1, APLABEL);
	get_value(message, 5, 1 + strlen(APLABEL), -sizeof(label), ANY_STRING,
	    0, 0, NULL);
	strncpy(label, message, strlen(message) + 1);
    }

    mvwaddstr(main_sub, 6, 1, SAVESETTINGS);
    wrefresh(main_sub);
    save_settings = on_off(6, 1 + strlen(SAVESETTINGS));
    clear_main_new(6, 7);
    print_menusel(6, 1, SAVESETTINGS, (save_settings == 1) ? ON : OFF);

    print_bottom(inet_ntoa(ap_ip));

    if(save_settings == 1) {
	if ((home_dir = getenv("HOME"))) {
	    sprintf(message, "%s/.ap-config", home_dir);
	    if ((fd = open(message, O_CREAT|O_WRONLY|O_APPEND, 0600)) != -1) {
		sprintf(message, "%s:%s:%s:%d:%d\n", inet_ntoa(ap_ip),
		    community, label, ap_type, ap_vendorext);
		write(fd, message, strlen(message));
		close(fd);
		wbkgd(main_sub, A_NORMAL);
		wrefresh(main_sub);
		print_help(DONE_WRITING_APCONF);
	    } else
		print_helperr(ERR_WRITING_APCONF);

	    getch();
	}
    }
  exit:
    print_help("");
    print_top(NULL, NULL);
    clear_main(0);
    return;
}

/*
 * Determines AP MIB type (fills ap_type global), and AP MIB vendor extensions
 * (fills ap_vendorext global). Returns with -1 on error or 0
 * if everything is OK.
 */
int get_mib_details()
{
    char sysDescr_NWN[] = {
	0x2B, 0x06, 0x01, 0x02, 0x01, 0x01, 0x01, 0x00
    };
    char sysDescr_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x01, 0x01, 0x00
    };
    /*
     * ATMEL410: SMARTBRIDGES MIB define this with length 4 (IpAddress); others
     * (hopefully) do not define this but eventually return (hopefully)
     * different value than 4.
     */
    char AuthRadiusIP_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x06, 0x03, 0x00
    };
    /* ATMEL12350: GEMTEK and EZYNET MIBs define length 160, others 92 or 128 */
    char sysDeviceInfo_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x01, 0x01, 0x05, 0x00
    };
    /* ATMEL12350: EZYNET MIB defines length 104, others 88 */
    char wirelessStatistics_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x02, 0x03, 0x01, 0x00
    };

    varbind varbinds[2];
    int i;

    print_help(_("Trying to probe AP for MIB properties. Please wait..."));

    /* first, determine the private MIB types according to enterprises ID */
    varbinds[0].oid = sysDescr_NWN;
    varbinds[0].len_oid = sizeof(sysDescr_NWN);
    varbinds[0].value = NULL;
    varbinds[0].len_val = 0;
    varbinds[0].type = NULL_VALUE;
    if (snmp(varbinds, 1, GET) > 0) {
	ap_type = NWN;
    } else {
	varbinds[0].oid = sysDescr_ATMEL;
	varbinds[0].len_oid = sizeof(sysDescr_ATMEL);
	varbinds[0].value = NULL;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	if (snmp(varbinds, 1, GET) > 0) {
	    ap_type = ATMEL410;
	} else {
	    sysDescr_ATMEL[5] = 0xE0;
	    sysDescr_ATMEL[6] = 0x3E;
	    varbinds[0].oid = sysDescr_ATMEL;
	    varbinds[0].len_oid = sizeof(sysDescr_ATMEL);
	    varbinds[0].value = NULL;
	    varbinds[0].len_val = 0;
	    varbinds[0].type = NULL_VALUE;
	    if (snmp(varbinds, 1, GET) > 0) {
		ap_type = ATMEL12350;
	    } else {
		print_helperr(_("Unable to determine AP MIB properties "
		    "(no response from AP). Press any key."));
		getch();
		return -1;
	    }
	}
    }

    /*
     * It is best to do the following HERE and FOR ONCE ONLY: find out more
     * about specific MIB vendor extensions - we'll check against them
     * on different places later.
     */

    ap_vendorext = NONE;

    if (ap_type == ATMEL410) {
	varbinds[0].oid = AuthRadiusIP_ATMEL;
	varbinds[0].len_oid = sizeof(AuthRadiusIP_ATMEL);
	varbinds[0].value = NULL;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;

	i = snmp(varbinds, 1, GET);
	if (i < 0) {
	    print_helperr(ERR_RET);
	    getch();
	    return -1;
	}

	/*
	 * i == 0 => 'no such variable in the MIB' returned =>
	 * consider 'ap_vendorext = NONE' too.
	 */

	if (varbinds[0].len_val == 4)
	    ap_vendorext = SBRIDGES;
    }

    if (ap_type == ATMEL12350) {
	varbinds[0].oid = sysDeviceInfo_ATMEL;
	varbinds[0].len_oid = sizeof(sysDeviceInfo_ATMEL);
	varbinds[0].value = NULL;
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	varbinds[1].oid = wirelessStatistics_ATMEL;
	varbinds[1].len_oid = sizeof(wirelessStatistics_ATMEL);
	varbinds[1].value = NULL;
	varbinds[1].len_val = 0;
	varbinds[1].type = NULL_VALUE;

	if (snmp(varbinds, 2, GET) <= 0) {
	    print_helperr(ERR_RET);
	    getch();
	    return -1;
	}

	if (varbinds[0].len_val == 160)
	    ap_vendorext = GEMTEK;

	if (varbinds[1].len_val == 104)
	    ap_vendorext = EZYNET;
    }

    return 0;
}

void exit_program()
{
    erase();
    refresh();
    endwin();
    exit(0);
}

void exit_shell()
{
    def_prog_mode();
    endwin();
    system("sh");
    curs_set(0);
    refresh();
}

/*
 * Sets different-than-default (1 sec.) polling interval for polling-active
 * modes, from interval <0.1;86400> seconds.
 * poll_delay is natively in tenths of second.
 */
void polling_interval()
{
    unsigned char message[256];
    int c = 0;

    noecho();
    print_help(POLL_HELP);

    while (1) {
	sprintf(message, _("%s%u"), POLL_I, poll_delay);
	mvwaddstr(main_sub, 0, 0, message);
	sprintf(message, _("(%0.1f seconds)"), (float) poll_delay / 10);
	mvwaddstr(main_sub, 1, strlen(POLL_I), message);
	wrefresh(main_sub);
	c = getch();
	switch (c) {
	    case 'P':
	    case 'p':
		get_value(message, 0, strlen(POLL_I), 7, INT_STRING, 1, 864000,
		    POLL_HELP);
		poll_delay = atoi(message);
		clear_main_new(0, 2);
		break;
	    case 'Q':
	    case 'q':
		goto quit;
	}
    }

quit:
    print_help("");
    clear_main(0);
    return;
}

/*
 * Expects regulatory domain code on input and returns index of a corresponding
 * regdom_types[] member, that describes the given regulatory domain properties.
 */
int regdom_idx(char regdom)
{
    int i;

    for (i = 0; &regdom_types[i]; i++)
	if (regdom_types[i].code == regdom)
	    return i;

    return i;
}
