/*
 *      ap-auth.c from Access Point SNMP Utils for Linux
 *
 * Copyright (c) 2004 Teemu Kiviniemi <teemuki at fotokone.fi>
 *
 * 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 <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#if defined (__GLIBC__)
#include <libgen.h>
#endif
#include "ap-utils.h"

#define ERROR_PACKET _("AuthorizedMacTableString packet error")
#define ERROR_DATA _("Invalid data in source file")
#define ERROR_FILE_OPEN _("Can't open file")
#define ERROR_FILE_WRITE _("Can't write to file")
#define ERROR_FILE_CLOSE _("Error closing file")

short ap_type;
char *community = NULL;
struct in_addr ap_ip;

void usage()
{
    printf(_("\nUsage:\n"));
    printf(_("\tap-auth -i ip -c community -d filename [-h]\n"));
    printf(_("\tap-auth -i ip -c community -u filename [-h]\n\n"));
    printf(_("Change accesspoint's list of authorised MAC addresses\n\n"));
    printf(_("-i ip        - AP ip address\n"));
    printf(_("-c community - SNMP community string\n"));
    printf(_("-d filename  - download list of authorised MAC addresses from AP"
	" to a file\n"));
    printf(_("-u filename  - upload list of authorised MAC addresses from"
	" a file to AP\n"));
    printf(_("-h           - print this help screen\n\n"));
    printf(_("ap-auth %s Copyright (c) 2004 Teemu Kiviniemi\n\n"),
	   VERSION);
}

int write_addr (FILE *f, const struct MacListStat *n)
{
    if (fprintf (f, "%02x%02x%02x%02x%02x%02x\n", n->addr[0], n->addr[1],
                                                  n->addr[2], n->addr[3],
						  n->addr[4], n->addr[5])
						!= 13)
        return 1;

    return 0;
}

int get_addr (struct MacListStat *ml, char *addr)
{
    int i;
    char tmp[3];

    if (strlen (addr) != 12)
        return 1;

    tmp[2] = '\0';

    for (i = 0; i < 6 ; i++) {
        tmp[0] = addr[2 * i];
	tmp[1] = addr[2 * i + 1];
	ml->addr[i] = strtol (tmp, NULL, 16);
    }

    return 0;
}

int main(int argc, char **argv)
{
    extern char *optarg;
    extern int optind;
    extern int opterr;
    extern int optopt;
    int opt = 0;
    int mode = 0; /* 1 = download, 2 = upload */
    FILE *f;
    char *filename = NULL;

    struct AuthorizedMacTableString {
	unsigned int short Action;
	unsigned int short NumOfAllTableAddresses;
	unsigned int short NumOfCurrentAddress;
	unsigned char MacAddress[6];
    } *AuthMac = NULL, get;

    struct MacListStat *first = NULL, *curr = NULL;

    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
    };
    char AutorizedMac_ATMEL[] = {
	0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x1A, 0x01, 0x02, 0x06, 0x02, 0x00
    };

    int total_mac, mac_num = 0;
    varbind varbinds[1];
    char mac_tmp[13];
    char *cp;
    struct MacListStat ml_tmp;
    int i, tmp;

#ifdef HAVE_GETTEXT
    setlocale(LC_ALL, "");
    bindtextdomain("ap-utils", LOCALEDIR);
    textdomain("ap-utils");
#endif

    do {
	opterr = 0;
	switch (opt = getopt(argc, argv, "i:c:d:u:")) {
	case 'i':
	    for (cp = optarg, i = 0; *cp && (cp = index(cp, '.')); cp++, i++);
	    if (i < 3 || inet_aton(optarg, &ap_ip) == 0) {
		printf(_("Error: invalid IP-address.\n"));
		return 1;
	    }
	    break;
	case 'c':
	    community = malloc(strlen(optarg) + 1);
	    strncpy(community, optarg, strlen(optarg) + 1);
	    break;
	case 'd':
	    filename = malloc(strlen(optarg) + 1);
	    strncpy(filename, optarg, strlen(optarg) + 1);
	    mode = 1;
	    break;
	case 'u':
	    filename = malloc(strlen(optarg) + 1);
	    strncpy(filename, optarg, strlen(optarg) + 1);
	    mode = 2;
	    break;
	case -1:
	    break;
	default:
	    usage();
	    return 1;
	}
    } while (opt != -1);

    if (!community) {
	usage();
	return 1;
    }

    /*
     * Part detecting ap_type (ATMEL AP MIB type) follows.
     * We could use get_mib_details() here with advantage, but it would
     * have to involve 1. putting it to separate file in lib/ and
     * 2. patch it so it would not contain curses-related commands (TODO)
     */

    /* determine private MIB type 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 {
		printf(_("Unable to determine AP MIB type "
		    "(no response from AP)."));
		return 1;
	    }
	}
    }

    if (ap_type == NWN) {
	printf(_("NWN devices are not yet supported."));
	return 1;
    }

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

    switch (mode) {

    case 1: /* download */

	total_mac = 0;
	mac_num = 0;

	while (mac_num <= total_mac) {
	    get.Action = 0x02; rshort(get.Action);
	    get.NumOfAllTableAddresses = total_mac; rshort(get.NumOfAllTableAddresses);
	    get.NumOfCurrentAddress = mac_num; rshort(get.NumOfCurrentAddress);
	    
	    varbinds[0].oid = AutorizedMac_ATMEL;
	    varbinds[0].len_oid = sizeof(AutorizedMac_ATMEL);
	    varbinds[0].value = (char *) &get;
	    varbinds[0].len_val = 12;
	    varbinds[0].type = STRING_VALUE;
	    
	    if (snmp(varbinds, 1, SET) <= 0) {
	        printf(ERR_RET);
		printf("\n");
	        return 1;
	    }

	    if (varbinds[0].len_val == 12) {
	        if (AuthMac)
		    free(AuthMac);
		AuthMac = 
		    (struct AuthorizedMacTableString *) malloc(varbinds[0].
							       len_val);
		memcpy(AuthMac, varbinds[0].value, varbinds[0].len_val);
	    } else {
	        printf(ERROR_PACKET);
		printf("\n");
		return 1;
	    }

	    rshort(AuthMac->NumOfAllTableAddresses);
	    total_mac =
	        (AuthMac->NumOfAllTableAddresses ==
		 65535) ? 0 : AuthMac->NumOfAllTableAddresses;

	    if (mac_num) {
	        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, AuthMac->MacAddress, 6);
		curr->next = NULL;
	    }
	    mac_num++;
	}
	f = fopen(filename, "w");
	if(f == NULL) {
	    perror(ERROR_FILE_OPEN);
	    return 1;
	}
	if (first == NULL) {
	    /* no authorised addresses */
	} else {
	    curr = first;
	    while (curr != NULL) {
	        if(write_addr (f, curr) != 0) {
		    perror(ERROR_FILE_WRITE);
		    return 1;
		}
		curr = curr->next;
	    }
	}
	if(fclose(f) != 0) {
	    perror(ERROR_FILE_CLOSE);
	    return 1;
	}
	break;
    case 2: /* upload */
	f = fopen(filename, "r");
	if (f == NULL) {
	    perror(ERROR_FILE_OPEN);
	    return 1;
	}
	mac_num  = 0;
	while (!feof (f)) {
	    tmp = fread (mac_tmp, 1, sizeof (mac_tmp), f);

	    if (tmp == sizeof (mac_tmp)) {
	        if (mac_tmp[12] != '\n') {
		    printf(ERROR_DATA);
		    printf("\n");
		    return 1;
		}
		mac_tmp[12] = '\0';

		if (get_addr (&ml_tmp, mac_tmp) != 0) {
		    printf(ERROR_DATA);
		    printf("\n");
		    return 1;
		}
		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, &ml_tmp, sizeof (struct MacListStat));
		curr->next = NULL;
		mac_num++;
	    }
	}
	fclose(f);

	curr = first;
	i = 1;
	while (curr != NULL) {
	    get.Action = 0x01;
	    rshort(get.Action);
	    get.NumOfAllTableAddresses = mac_num;
	    rshort(get.NumOfAllTableAddresses);
	    get.NumOfCurrentAddress = i;
	    rshort(get.NumOfCurrentAddress);
	    memcpy(get.MacAddress, curr->addr, 6);
	    varbinds[0].oid = AutorizedMac_ATMEL;
	    varbinds[0].len_oid = sizeof(AutorizedMac_ATMEL);
	    varbinds[0].value = (char *) &get;
	    varbinds[0].len_val = 12;
	    varbinds[0].type = STRING_VALUE;
	    if (snmp(varbinds, 1, SET) <= 0) {
	        printf(ERR_RET);
		printf("\n");
		return 1;
	    }
	    if (varbinds[0].len_val != 12) {
	        printf(ERROR_PACKET);
		printf("\n");
		return 1;
	    }
	    curr = curr->next;
	    i++;
	}
	break;
    default:
	usage();
	return 1;
    }

    curr = first;
    while (curr != NULL) {
        curr = curr->next;
	free (first);
	first = curr;
    }

    if (community)
	free(community);
    if (filename)
	free(filename);
    return 0;
}
