/*
 *      aps.c from Access Point SNMP Utils for Linux
 *
 * Copyright (c) 2002 Roman Festchook <roma at polesye dot net>
 * Copyright (c) 2004 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 <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include "ap-utils.h"

#define KNOWN_APS _("Known Access Points")

extern WINDOW *main_win, *main_sub /* *win_for_menu */;
extern short ap_type, ap_vendorext;
extern int sockfd, atmel410_filter;
extern struct in_addr ap_ip;

int ap_viewtype = 1;

void APs()
{
    /*
     * Structure of single record in the KnownAP table.
     */
    struct ap {
	unsigned char mac[6];
	unsigned char q1;
	unsigned char q2;
	unsigned char channel;
	unsigned char x2;
	unsigned char options;
	unsigned char x3[5];
	unsigned char essid[32];
    } *app = NULL;

    /*
     * Structure used to preserve important values of AP records learnt
     * by KnownAP scan. Used to connect to a specific AP.
     */
    struct known_ap {
	char mac[6];
	int essid_len;
	char channel;
	char essid[32];
	enum { Infrastructure, AdHoc } nettype;
    } ap[16];

    /* this one is ATMEL12350 specific */
    char SiteSurveyCommand[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0xE0, 0x3E, 0x01, 0x01, 0x01, 0x07, 0x00
    };

    char bridgeOperationalMode[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x01, 0x00
    };
    char bridgeRemoteBridgeBSSID[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x02, 0x00
    };

    char operChannelID[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x01, 0x00
    };
    char operESSIDLength[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x02, 0x00
    };
    char operESSID[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x01, 0x03, 0x00
    };
    char KnownAP[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x07, 0x01, 0x00
    };

    char value[] = "", c, bridge_mode;
    char essid[33], bssid[13], message[65];
    int i, j, errno, aps_num = 0, needs_opmode_restore = 0;
    varbind varbinds[4];
   
   if (ap_type == ATMEL12350) {
	operESSIDLength[5] = 0xE0;
	operESSIDLength[6] = 0x3E;
	operESSID[5] = 0xE0;
	operESSID[6] = 0x3E;
	operChannelID[5] = 0xE0;
	operChannelID[6] = 0x3E;
	KnownAP[5] = 0xE0;
	KnownAP[6] = 0x3E;
	bridgeOperationalMode[5] = 0xE0;
	bridgeOperationalMode[6] = 0x3E;
	bridgeRemoteBridgeBSSID[5] = 0xE0;
	bridgeRemoteBridgeBSSID[6] = 0x3E;
    }

    noecho();
    print_help(WAIT_RET);

    varbinds[0].oid = bridgeOperationalMode;
    varbinds[0].len_oid = sizeof(bridgeOperationalMode);
    varbinds[0].value = bridgeOperationalMode;
    varbinds[0].len_val = 0;
    varbinds[0].type = NULL_VALUE;
    if (snmp(varbinds, 1, GET) <= 0) {
	print_helperr(ERR_RET);
	goto exit;
    }
    bridge_mode = *(varbinds[0].value);

    /* ATMEL410 firmware KnownAP scan is active in AP Client mode only */
    if (ap_type == ATMEL410 && bridge_mode != 3) {
	mvwaddstr(main_sub, 3, 1,
	    _
	    ("Your Access Point is not in \"AP client\" mode => getting"));
	mvwaddstr(main_sub, 4, 1,
	    _
	    ("up-to-date \"Known APs\" info requires your AP to be"));
	mvwaddstr(main_sub, 5, 1,
	    _
	    ("temporarily configured into \"AP client\" mode and rebooted."));
	mvwaddstr(main_sub, 6, 1,
	    _
	    ("Your AP will be reconfigured back to original mode by this"));
	mvwaddstr(main_sub, 7, 1,
	    _
	    ("utility once you quit the \"KnownAP\" view. This, in turn, may"));
	mvwaddstr(main_sub, 8, 1,
	    _
	    ("cause loss of Access Point's current configuration."));
	wattrset(main_sub, A_BOLD);
	mvwaddstr(main_sub, 9, 1,
	    _
	    ("Do NOT answer \"Yes\" if you're connected to the Access Point"));
	mvwaddstr(main_sub, 10, 1,
	    _
	    ("via its wireless port."));
	wattrset(main_sub, A_NORMAL);
	mvwaddstr(main_sub, 12, 20, _("Do you want to continue? "));
	wrefresh(main_sub);
	if (help_ysn())
	    goto quit;

	needs_opmode_restore = 1;
	print_help(WAIT_RET);
	varbinds[0].oid = bridgeOperationalMode;
	varbinds[0].len_oid = sizeof(bridgeOperationalMode);
	varbinds[0].type = INT_VALUE;
	c = 3;
	varbinds[0].value = &c;
	varbinds[0].len_val = 1;
	if (snmp(varbinds, 1, SET) <= 0) {
	    print_helperr(ERR_RET);
	    goto exit;
	}

	if (SysUpload()) {
	    print_helperr(ERR_RET);
	    goto restore_before_exit;
	}
	sleep(7);
    }

    sprintf(message, "%s%c", VIEW, ap_viewtype + '0');
    print_top(message, KNOWN_APS);

get_stats:
    varbinds[0].oid = KnownAP;
    varbinds[0].len_oid = sizeof(KnownAP);
    varbinds[0].type = NULL_VALUE;
    varbinds[0].value = value;
    varbinds[0].len_val = sizeof(value);

    if (snmp(varbinds, 1, GET) <= 0) {
	print_helperr(ERR_RET);
	goto restore_before_exit;
    }


show_stats:
    /* display column info in main_sub */
    mvwin(main_sub, 0, 0);
    wattrset(main_sub, COLOR_PAIR(13));
    for (i = 0; i < 64; i++)
	waddch(main_sub, ' ');
    switch (ap_viewtype) {
	case 1:
	case 2:
	    mvwaddstr(main_sub, 0, 0, "#");
	    mvwaddstr(main_sub, 0, 2, "ESSID");
	    mvwaddstr(main_sub, 0, 18, "BSSID (MAC)");
	    mvwaddstr(main_sub, 0, 31, "CN");
	    mvwaddstr(main_sub, 0, 34, _("NetworkType"));
	    mvwaddstr(main_sub, 0, 49, "P");
	    mvwaddstr(main_sub, 0, 51, "WEP");
	    mvwaddstr(main_sub, 0, 55, "RSSI");
	    if (ap_type == ATMEL410) mvwaddstr(main_sub, 0, 62, "LQ");
	    break;
	case 3:
	    mvwaddstr(main_sub, 0, 0, "#");
	    mvwaddstr(main_sub, 0, 2, "ESSID");
	    mvwaddstr(main_sub, 0, 35, "BSSID (MAC)");
	    mvwaddstr(main_sub, 0, 48, "CN");
	    mvwaddstr(main_sub, 0, 51, "P");
	    mvwaddstr(main_sub, 0, 53, "WEP");
	    mvwaddstr(main_sub, 0, 57, "RSSI");
	    if (ap_type == ATMEL410) mvwaddstr(main_sub, 0, 62, "LQ");
	    break;
	case 4:
	    mvwaddstr(main_sub, 0, 0, "#");
	    mvwaddstr(main_sub, 0, 2, "BSSID (MAC)");
	    mvwaddstr(main_sub, 0, 15, "Manufacturer OUI");
	    break;
    }
    wattrset(main_sub, A_NORMAL);

    /* main loop */
    i = aps_num = 0;
    while (1) {
	app = (struct ap *) &varbinds[0].value[i];

	j = app->mac[0] | app->mac[1] | app->mac[2] |
	    app->mac[3] | app->mac[4] | app->mac[5];

	if (!app->channel || app->channel>14 || app->mac[0] == 0xFF
	    || j == 0 || aps_num == 16)
	    break;

	i += sizeof(struct ap);
	aps_num++;

	/* display number */
	sprintf(message, "%c", (aps_num < 10) ? aps_num + '0' : aps_num + 55);
	mvwaddstr(main_sub, aps_num+1, 0, message);

	/* get BSSID */
	sprintf(bssid, "%02X%02X%02X%02X%02X%02X",
		app->mac[0] & 0xFF, app->mac[1] & 0xFF,
		app->mac[2] & 0xFF, app->mac[3] & 0xFF,
		app->mac[4] & 0xFF, app->mac[5] & 0xFF);
	bssid[13] = '\0';

	/* get ESSID and its length */
	for (j = 0; j < 32 && app->essid[j]; j++) {
	    essid[j] = app->essid[j];
	}
	essid[j] = '\0';

	/*
	 * fill up ap[] field member to preserve ESSID, BSSID, Channel and
	 * Network type of current AP record, so we may use it later
	 * to connect to this very AP
	 */
	memcpy(ap[aps_num-1].mac, app->mac, 6);
	memcpy(ap[aps_num-1].essid, essid, j);
	ap[aps_num-1].essid_len = j;
	ap[aps_num-1].channel = app->channel;
	ap[aps_num-1].nettype = (app->options & 1) ? Infrastructure : AdHoc;

	/* display ESSID, BSSID, Channel, NetworkType, Preambule and WEP */
	switch (ap_viewtype) {
	    case 1:
	    case 2:
		mvwaddnstr(main_sub, aps_num+1, 2, essid, 15);
		mvwaddstr(main_sub, aps_num+1, 18, bssid);
		sprintf(message, "%2u", app->channel);
		mvwaddstr(main_sub, aps_num+1, 31, message);
		mvwaddstr(main_sub, aps_num+1, 34,
		    (app->options & 1) ? _("Infrastructure") : "Ad-Hoc");
		mvwaddstr(main_sub, aps_num+1, 49,
		    (app->options & 32) ? "S" : "L");
		mvwaddstr(main_sub, aps_num+1, 51,
		    (app->options & 16) ? ON : OFF);
		break;
	    case 3:
		mvwaddstr(main_sub, aps_num+1, 2, essid);
		mvwaddstr(main_sub, aps_num+1, 35, bssid);
		sprintf(message, "%2u", app->channel);
		mvwaddstr(main_sub, aps_num+1, 48, message);
		mvwaddstr(main_sub, aps_num+1, 51,
		    (app->options & 32) ? "S" : "L");
		mvwaddstr(main_sub, aps_num+1, 53,
		    (app->options & 16) ? ON : OFF);
		break;
	    case 4:
		mvwaddstr(main_sub, aps_num+1, 2, bssid);
		mvwaddnstr(main_sub, aps_num+1, 15,
		    oui2manufacturer(app->mac), 48);
		break;
	}

	/* display RSSI and LQ indicators */
	switch (ap_viewtype) {
	    case 1:
		sprintf(message, "%3d%%",
		    (int)((minimum (app->q1, 40)) * (float)2.5));
		mvwaddstr(main_sub, aps_num+1, 55, message);
		if (ap_type == ATMEL410) {
		    sprintf(message, "%3d%%",
			(int)(100 - (minimum (app->q2, 40)) * (float)2.5) );
		    mvwaddstr(main_sub, aps_num+1, 60, message);
		}
		break;
	    case 2:
		sprintf(message, "%3d", dbmconv(app->q1));
		mvwaddstr(main_sub, aps_num+1, 56, message);
		if (ap_type == ATMEL410) {
		    sprintf(message, "%3d%%",
			(int)(100 - (minimum (app->q2, 40)) * (float)2.5) );
		    mvwaddstr(main_sub, aps_num+1, 60, message);
		}
		break;
	    case 3:
		sprintf(message, "%3d", app->q1);
		mvwaddstr(main_sub, aps_num+1, 57, message);
		if (ap_type == ATMEL410) {
		    sprintf(message, "%3d", app->q2);
		    mvwaddstr(main_sub, aps_num+1, 61, message);
		}
		break;
	}

    }

    /* display legend in main_sub */
    switch (ap_viewtype) {
	case 1:
	    mvwaddstr(main_sub, LINES - 6, 0,
		_("CN: Channel Name; P: Preambule Type (S: Short; L: Long);"));
	    mvwaddstr(main_sub, LINES - 5, 0,
		_("RSSI: Radio Signal Strength Indicator [%]"));
	    if (ap_type == ATMEL410)
		waddstr(main_sub, _("; LQ: Link Quality [%]"));
	    break;
	case 2:
	    mvwaddstr(main_sub, LINES - 6, 0,
		_("CN: Channel Name; P: Preambule Type (S: Short; L: Long);"));
	    mvwaddstr(main_sub, LINES - 5, 0,
		_("RSSI: Radio Signal Strength Indicator [dBm]"));
	    if (ap_type == ATMEL410)
		waddstr(main_sub, _("; LQ: Link Quality [%]"));
	    break;
	case 3:
	    mvwaddstr(main_sub, LINES - 6, 0,
		_("CN: Channel Name; P: Preambule Type (S: Short; L: Long);"));
	    mvwaddstr(main_sub, LINES - 5, 0,
		_("RSSI: Radio Signal Strength Indicator [raw]"));
	    if (ap_type == ATMEL410)
		waddstr(main_sub, _("; LQ: Link Q. [raw]"));
	    break;
    }
    wrefresh(main_sub);

    if (ap_type == ATMEL410)
	print_help(_("# con. to AP #; R refresh with reset; T toggle; Q quit; Other = refr. w/o reset"));
    else /* ATMEL12350 */
	print_help(_("# con. to AP #; R initiate AP scan; T toggle view; Q quit; Other = refresh view"));

	while (1)
           switch (i = getch()) {
            case 'Q':
            case 'q':
		goto restore_before_exit;
	    case '1':
            case '2':
	    case '3':
            case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
	    case 'a':
	    case 'A':
	    case 'b':
	    case 'B':
	    case 'c':
	    case 'C':
	    case 'd':
	    case 'D':
	    case 'e':
	    case 'E':
	    case 'f':
	    case 'F':
	    case 'g':
	    case 'G':
		    if	    (i >= 'a')
			j = i - 87;
		    else if (i >= 'A')
			j = i - 55;
		    else
			j = i - '0';

		    if(j > aps_num)
			    break;

		    /* don't attempt to connect to remote AP in Ad-Hoc net */
		    if(ap[j-1].nettype == AdHoc)
			    break;

		    print_help(WAIT_SET);
		    varbinds[3].oid = operChannelID;
		    varbinds[3].len_oid = sizeof(operChannelID);
		    varbinds[3].value = &ap[j-1].channel;
		    varbinds[3].len_val = 1;
		    varbinds[3].type = INT_VALUE;
		    varbinds[2].oid = bridgeRemoteBridgeBSSID;
		    varbinds[2].len_oid = sizeof(bridgeRemoteBridgeBSSID);
		    varbinds[2].value = ap[j-1].mac;
		    varbinds[2].len_val = 6;
		    varbinds[2].type = STRING_VALUE;
	            varbinds[1].oid = operESSIDLength;
		    varbinds[1].len_oid = sizeof(operESSIDLength);
		    varbinds[1].value = (char *) &ap[j-1].essid_len;
	            varbinds[1].len_val = 1;
	            varbinds[1].type = INT_VALUE;
	            varbinds[0].oid = operESSID;
		    varbinds[0].len_oid = sizeof(operESSID);
		    varbinds[0].value = ap[j-1].essid;
		    varbinds[0].len_val = ap[j-1].essid_len;
		    varbinds[0].type = STRING_VALUE;
                    if (snmp(varbinds, 4, SET) <= 0) {
                        print_helperr(ERR_SET);
                        goto restore_before_exit;
                    }

		    if (bridge_mode != 3) {
			bridge_mode = 3;
			needs_opmode_restore = 1;
		    }

		    if (SysUpload() < 0 ) {
			    print_helperr(ERR_SET);
			    getch();
			    goto restore_before_exit;
		    }
		    print_help(DONE_SET);
		    getch();
		    goto restore_before_exit;
	    case 'R':
	    case 'r':
		    if (ap_type == ATMEL410) {
			/* force KnownAP scan by reset */
			print_help(WAIT_RET);
			if (SysReset() < 0) {
			    print_helperr(ERR_RET);
			    getch();
			    goto restore_before_exit;
			}
			clear_main(0);
			sleep(7);
		    } else { /* ATMEL12350 */
			/*
			 * Init KnownAP scan by issuing SiteSurveyCommand.
			 * Unfortunately, we may not use snmp() here becouse
			 * it would time out, as buggy firmware in ATMEL12350
			 * devices does not confirm our query reception
			 * received via wireless port, in a reasonable
			 * time frame. So we rather use sendto() here.
			 */
			struct sockaddr_in toip;
			struct timeval tv;
			fd_set rfd;

			varbinds[0].oid = SiteSurveyCommand;
			varbinds[0].len_oid = sizeof(SiteSurveyCommand);
			c = 1;
			varbinds[0].value = &c;
			varbinds[0].len_val = 1;
			varbinds[0].type = INT_VALUE;

			memset(&toip, 0, sizeof toip);
			toip.sin_family = AF_INET;
			toip.sin_port = htons(161);
			toip.sin_addr.s_addr = ap_ip.s_addr;
/*
			if (snmp(varbinds, 1, SET) <= 0) {
			    print_helperr(ERR_SET);
			    goto exit;
			}
*/
			errno = 0;
			i = ber(message, varbinds, 1, SET);
			if (sendto(sockfd, message, i, 0,
			    (struct sockaddr *) &toip, SIZE) == -1) {
			    sprintf(message, _("Failure in sendto(): %s. "
				"Press any key."), strerror(errno));
			    print_helperr(message);
			    goto exit;
			}

			clear_main(0);
			mvwaddstr(main_sub, 3, 1,
			    _
			    ("You have just initiated the AP scan. Be advised that it may"));
			mvwaddstr(main_sub, 4, 1,
			    _
			    ("take a few seconds for your Access Point to find out some"));
			mvwaddstr(main_sub, 5, 1,
			    _
			    ("values, so expect finishing the scan in about 5 seconds."));
			mvwaddstr(main_sub, 6, 1,
			    _
			    ("Also note that your Access Point stops forwarding the network"));
			mvwaddstr(main_sub, 7, 1,
			    _
			    ("traffic while the scan is in progress, but restores itself"));
			mvwaddstr(main_sub, 8, 1,
			    _
			    ("to normal operation in time ranging up to 1 minute."));
			mvwaddstr(main_sub, 9, 1,
			    _
			    ("Hence, if you are connected to target Access Point via its"));
			mvwaddstr(main_sub, 10, 1,
			    _
			    ("wireless port, you need to wait a bit longer"));
			mvwaddstr(main_sub, 11, 1,
			    _
			    ("after pressing 'S'."));
			wrefresh(main_sub);
			print_help("");

			/*
			 * Eat the confirmation packet from socket, if it
			 * has been sent by the AP. This only happens
			 * if we issued KnownAP scan via its ethernet
			 * interface.
			 */
			tv.tv_sec = 2;
			tv.tv_usec = 0;
			FD_ZERO(&rfd);
			FD_SET(sockfd, &rfd);
			if (select(sockfd + 1, &rfd, NULL, NULL, &tv) > 0)
			    recv(sockfd, message, 65, 0);

			print_help("Now, you can try pressing 'S' repeatedly "
			    "to watch the progress.");
			getch();
		    }
		    goto get_stats;
	    case 'T':
	    case 't':
		    ap_viewtype += 1;
		    if (ap_viewtype > 4)
			ap_viewtype = 1;
		    clear_main(0);
		    sprintf(message, "%s%c", VIEW, ap_viewtype + '0');
		    print_top(message, KNOWN_APS);
		    goto show_stats;
	    default:
		    print_help(WAIT_RET);
		    clear_main(0);
		    goto get_stats;
            }

  restore_before_exit:
    if (needs_opmode_restore) {
	print_help(WAIT_SET);
	varbinds[0].oid = bridgeOperationalMode;
	varbinds[0].len_oid = sizeof(bridgeOperationalMode);
	varbinds[0].value = (char *) &bridge_mode;
	varbinds[0].len_val = 1;
	varbinds[0].type = INT_VALUE;
	if (snmp(varbinds, 1, SET) <= 0) {
	    print_helperr(ERR_RET);
	    goto exit;
	}

	if (SysUpload()) {
	    print_helperr(ERR_RET);
	    goto exit;
	}
    }
 goto quit;

 exit:
    getch();
 quit:
    print_top(NULL, NULL);
    clear_main(0);
}

