/*
 *      stations.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 <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include "ap-utils.h"

#define MAX_LINES LINES-4

extern int LINES;
extern WINDOW *main_sub;
extern short ap_type, ap_vendorext;
extern int sts_viewtype;

void atmel_stations()
{
    char bridgeOperationalMode[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x01, 0x04, 0x01, 0x00
    };
    char AssociatedSTAsNum[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x05, 0x01, 0x00
    };
    char AssociatedSTAsInfo[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x05, 0x02, 0x00
    };

    /* AP mode: connected APClients info */

    struct AssociatedSTAsInfo_ATMEL410 ap_410;
    struct AssociatedSTAsInfo_ATMEL12350 ap_12350;

    struct MacListStat *first = NULL, *curr = NULL;
    char message[1024];
    int mac_idx, begin, end, total_mac;
    varbind varbinds[1];


    if (ap_type == ATMEL12350) {
        bridgeOperationalMode[5] = 0xE0;
	bridgeOperationalMode[6] = 0x3E;
	AssociatedSTAsNum[5] = 0xE0;
	AssociatedSTAsNum[6] = 0x3E;
	AssociatedSTAsInfo[5] = 0xE0;
	AssociatedSTAsInfo[6] = 0x3E;
    }	

    /* find out mode the device is currently in */
    varbinds[0].oid = bridgeOperationalMode;
    varbinds[0].len_oid = sizeof(bridgeOperationalMode);
    varbinds[0].value = bridgeOperationalMode;
    varbinds[0].len_val = 0;
    varbinds[0].type = NULL_VALUE;
    print_help(WAIT_RET);
    if (snmp(varbinds, 1, GET) <= 0) {
	  print_helperr(ERR_RET);
	  goto exit;
    }

    /* Rule out all modes except AP(2). */
    if (*(varbinds[0].value) != 2) {
	mvwaddstr(main_sub, 1, 1, _("Not available (device not in AP mode)."));
	print_help(ANY_KEY);
	wrefresh(main_sub);
	goto exit;
    }

    noecho();

    wattrset(main_sub, COLOR_PAIR(13));
    mvwaddstr(main_sub, 0, 0,
	_("  #     MAC       Parent MAC  RSSI   LQ Sts MACn      IP        "));
    wattrset(main_sub, A_NORMAL);

refresh:
    /* find out how many STAtions is in the list */
    varbinds[0].oid = AssociatedSTAsNum;
    varbinds[0].len_oid = sizeof(AssociatedSTAsNum);
    varbinds[0].value = AssociatedSTAsNum;
    varbinds[0].type = NULL_VALUE;
    varbinds[0].len_val = 0;

    print_help(WAIT_RET);

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

    total_mac = *(varbinds[0].value);
    print_help(WAIT_SET);
    mac_idx = 1;

    while (mac_idx <= total_mac) {
	/*
	 * Tell the AP we want mac_idx-th AssociatedSTAsInfo structure.
	 * Note: since:
	 * - position of 'Index' member in ap_* struct is always the same
	 * - only the 'Index' member is actually to be used
	 * - both structures have same size (24)
	 * we may pick *any* ap_* struct here.
	 */
	varbinds[0].oid = AssociatedSTAsInfo;
	varbinds[0].len_oid = sizeof(AssociatedSTAsInfo);
	varbinds[0].type = INT_VALUE;
	ap_410.Index = swap2(mac_idx);
	varbinds[0].value = (char *)&ap_410;
	varbinds[0].len_val = sizeof(ap_410);

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

	if (varbinds[0].len_val != 24) {
	    print_helperr(_("AssociatedSTAsInfo packet error"));
	    goto exit;
	}

	if (first == NULL) {
	    first =
		(struct MacListStat *) calloc(1, sizeof(struct MacListStat));
	    curr = first;
	} else {
	    curr->next =
		(struct MacListStat *) calloc(1, sizeof(struct MacListStat));
	    curr = curr->next;
	}

	/* lets not use black magic (casting) here, ok? */
	if (ap_type == ATMEL410) {
	    memcpy(&ap_410, varbinds[0].value, sizeof(ap_410));

	    memcpy(curr->addr, ap_410.MacAddress, 6);
	    if (ap_vendorext == SBRIDGES) {
		memcpy(curr->ParentMacAddress, ap_410.ParentMacAddress, 6);
		memcpy(&(curr->IP.s_addr), ap_410.IP, 4);
		curr->rssi = ap_410.RSSI;
		curr->quality = ap_410.LinkQuality;
		curr->Status = ap_410.Status;
		curr->Port = ap_410.Port;
	    }
	} else { /* ap_type == ATMEL12350 */
	    memcpy(&ap_12350, varbinds[0].value, sizeof(ap_12350));

	    memcpy(curr->addr, ap_12350.MacAddress, 6);
	    if (ap_vendorext == EZYNET) {
		memcpy(curr->ParentMacAddress, ap_12350.ParentMacAddress, 6);
		memcpy(&(curr->IP.s_addr), ap_12350.IP, 4);
		curr->rssi = ap_12350.RSSI;
		/* curr->quality stays empty */
		curr->Status = ap_12350.Status;
		curr->Port = ap_12350.Port;
	    }
	}

	curr->next = NULL;
	mac_idx++;
    }

    begin = 1;
    end = (MAX_LINES < mac_idx) ? MAX_LINES : mac_idx;

    sprintf(message, "%s: %d", ST_TITLE, total_mac);
    while (1) {
	if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
	    (ap_type == ATMEL12350 && ap_vendorext == EZYNET)) {
	    print_top_rssi(message);
	    print_help(_("Arrows - scroll; S - save to file; Q - return; "
			 "T - toggle view; Other - refresh"));
	} else {
	    print_top(NULL, message);
	    print_help(_("Arrows - scroll; S - save to file; Q - return; "
			 "Other key - refresh"));
	}
	scroll_rows(first, begin, end, 1, 2);

	switch (getch()) {
	    case 'S':
	    case 's':
		save_Stations(first);
		continue;
	    case KEY_RIGHT:
	    case KEY_DOWN:
		if (end < mac_idx) {
		    begin++;
		    end++;
		}
		continue;
	    case KEY_UP:
	    case KEY_LEFT:
		if (begin > 1) {
		    begin--;
		    end--;
		}
		continue;
	    case 'Q':
	    case 'q':
		goto quit;
	    case 'T':
	    case 't':
		if ((ap_type == ATMEL410 && ap_vendorext == SBRIDGES) ||
		    (ap_type == ATMEL12350 && ap_vendorext == EZYNET)) {
		    sts_viewtype += 1;
		    if (sts_viewtype == 3)
			sts_viewtype = 0;
		}
		continue;
	    default:
		while ((curr = first)) {
		    first = curr->next;
		    free(curr);
		}
		first = curr = NULL;
		goto refresh;
	}
    }

exit:
    getch();
quit:
    while ((curr = first)) {
	first = curr->next;
	free(curr);
    }
    print_top(NULL, NULL);
    clear_main(0);
}

void nwn_stations()
{
    unsigned char Mac[] = {
	0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x29, 0x03, 0x01, 0x03, 0x01,
	0x02, 0x01, 0x02, 0x80, 0x00 };
    unsigned char Quality[] = {
	0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x29, 0x03, 0x01, 0x03, 0x01,
	0x02, 0x01, 0x03, 0x80, 0x00 };
    unsigned char Age[] = {
	0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x29, 0x03, 0x01, 0x03, 0x01,
	0x02, 0x01, 0x04, 0x80, 0x00 };
    unsigned char RSSI[] = {
	0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x29, 0x03, 0x01, 0x03, 0x01,
	0x02, 0x01, 0x05, 0x80, 0x00 };

    struct MacListStat *first = NULL, *curr = NULL;
    char null[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, message[1024];
    int mac_idx, begin, end;
    varbind varbinds[4];
    unsigned char next_num;


    mac_idx = 0;
    print_top(NULL, ST_TITLE);
    mvwaddstr(main_sub, 0, 3,
	_("Id       MAC address     Quality  Age  RSSI"));
    noecho();
    print_help(WAIT_RET);

    varbinds[0].oid = Mac;
    varbinds[0].len_oid = sizeof(Mac);
    varbinds[0].len_val = 0;
    varbinds[0].type = NULL_VALUE;
    if (snmp(varbinds, 1, GET_NEXT) <= 0) {
        print_helperr(ERR_RET);
        goto exit;
    }
   next_num = varbinds[0].oid[varbinds[0].len_oid - 1];

   while (memcmp(varbinds[0].oid, Mac, sizeof(Mac) - 2) == 0) {

	Mac[sizeof(Mac) - 1] = next_num;
	Quality[sizeof(Mac) - 1] = next_num;
	Age[sizeof(Mac) - 1] = next_num;
	RSSI[sizeof(Mac) - 1] = next_num;

	if(sizeof(Mac) == varbinds[0].len_oid) {
		Mac[sizeof(Mac) - 2] = varbinds[0].oid[varbinds[0].len_oid - 2];
		Quality[sizeof(Mac) - 2] = varbinds[0].oid[varbinds[0].len_oid - 2];
		Age[sizeof(Mac) - 2]  = varbinds[0].oid[varbinds[0].len_oid - 2];
		RSSI[sizeof(Mac) - 2] = varbinds[0].oid[varbinds[0].len_oid - 2];
	}

	varbinds[0].oid = Mac;
	varbinds[0].len_oid = sizeof(Mac);
	varbinds[0].len_val = 0;
	varbinds[0].type = NULL_VALUE;
	varbinds[1].oid = Quality;
	varbinds[1].len_oid = sizeof(Quality);
	varbinds[1].len_val = 0;
	varbinds[1].type = NULL_VALUE;
	varbinds[2].oid = Age;
	varbinds[2].len_oid = sizeof(Age);
	varbinds[2].len_val = 0;
	varbinds[2].type = NULL_VALUE;
	varbinds[3].oid = RSSI;
	varbinds[3].len_oid = sizeof(RSSI);
	varbinds[3].len_val = 0;
	varbinds[3].type = NULL_VALUE;
	if (snmp(varbinds, 4, GET) < 4) {
	    print_helperr(ERR_RET);
	    getch();
	    goto exit;
	}

	if (memcmp(null, varbinds[0].value, 6)) {
	    if (first == NULL) {
		first =
		    (struct MacListStat *)
		    malloc(sizeof(struct MacListStat));
		curr = first;
	    } else {
		curr->next =
		    (struct MacListStat *)
		    malloc(sizeof(struct MacListStat));
		curr = curr->next;
	    } 
	    memcpy(curr->addr, varbinds[0].value, 6);
	    curr->quality = *varbinds[1].value;
	    curr->idle = *varbinds[2].value;
	    curr->rssi = *varbinds[3].value;
	    curr->next = NULL;
	    mac_idx++;
	}

	varbinds[0].oid = Mac;
        varbinds[0].len_oid = sizeof(Mac);
        varbinds[0].len_val = 0;
        varbinds[0].type = NULL_VALUE;
        if (snmp(varbinds, 1, GET_NEXT) <= 0) {
            print_helperr(ERR_RET);
            goto exit;
        }
	next_num = varbinds[0].oid[varbinds[0].len_oid - 1];
						
    }

   if(mac_idx) {

    begin = 1;
    end = (MAX_LINES < mac_idx + 1) ? MAX_LINES : mac_idx + 1;

    while (1) {
	print_help(_("Arrows - scroll; S - save to file; Q - return; "
	    "T - toggle view; Other - refresh"));

	scroll_rows(first, begin, end, 1, 1);

	sprintf(message, "%s: %d", ST_TITLE, mac_idx);
	print_top_rssi(message);

	switch (getch()) {
	case 'S':
	case 's':
	    save_Stations(first);
	    continue;
	case KEY_DOWN:
	case KEY_RIGHT:
	    if (end < mac_idx+1) {
		begin++;
		end++;
	    }
	    continue;
	case KEY_UP:
	case KEY_LEFT:
	    if (begin > 1) {
		begin--;
		end--;
	    }
	    continue;
	case 'Q':
	case 'q':
	    goto exit;
	case 'T':
	case 't':
	    sts_viewtype += 1;
	    if (sts_viewtype == 3)
		sts_viewtype = 0;

	    continue;
	}
    }
   }
   
   print_help(ANY_KEY);
   getch();
   
  exit:
    while ((curr = first)) {
        first = curr->next;
	free(curr);
    }
    
    print_top(NULL, NULL);
    clear_main(0);
}
