/**
 * A client-side 802.1x implementation 
 *
 * This code is released under both the GPL version 2 and BSD licenses.
 * Either license may be used.  The respective licenses are found below.
 *
 * Copyright (C) 2002 Bryan D. Payne & Nick L. Petroni Jr.
 * All Rights Reserved
 *
 * --- GPL Version 2 License ---
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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.
 *
 * --- BSD License ---
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  - All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *       This product includes software developed by the University of
 *       Maryland at College Park and its contributors.
 *  - Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*******************************************************************
 * The driver function for a Linux application layer EAPOL 
 * implementation
 * File: config.c
 *
 * Authors: Chris.Hessing@utah.edu
 *
 * $Id: config.c,v 1.42 2004/08/19 02:28:06 chessing Exp $
 * $Date: 2004/08/19 02:28:06 $
 * $Log: config.c,v $
 * Revision 1.42  2004/08/19 02:28:06  chessing
 * First piece of WPA patch.  (The patch is growing fast, and this commit is to save what is done so far.)
 *
 * Revision 1.41  2004/08/15 04:29:34  chessing
 *
 * Completed support for scanning and joining wireless networks.  If your interface isn't configured, we will scan to gather information about all known wireless networks.  If a network in the list has a configuration, we will join it.  (Including turning on needed encryption.)  There are still a few rough edges that need to be hammered out.  But the overall user experience should be improved.
 *
 * Revision 1.40  2004/08/12 01:40:06  chessing
 *
 * Added "association" option to the global configuration file sections.  It can be set to either auto, or manual.  Setting "association" to auto means that XSupplicant will attempt to associate to a network with the lowest value in the "priority" setting.  The default for "association" is auto.  The other option added is "priority" which is a numeric value from 1..256.  A value of 1 is the highest priority, and will be the first network chosen.  (Assuming the network can be found in a scan.)  The default value for "priority" is 0, which means that network will be checked last.   --  Although the options are now in the config file, the actual functionality for auto association/priority is not yet complete.  (So, basically these options don't do anything yet. ;)
 *
 * Revision 1.39  2004/07/25 19:36:40  chessing
 * Fixed a few more logical/bitwise nots.  Added rtnetlink support for hot-plugging interfaces.  (Hot unplugging is coming soon. ;)
 *
 * Revision 1.38  2004/07/19 02:43:16  chessing
 *
 * Changed things to get rid of Linux specific pieces in the interface_data struct. Fixed up EAP-SIM and EAP-AKA to skip AVPs that we don't support.  (We print a mesage, and move on.)  Added the --enable-radiator-test option to configure EAP-AKA to use the test vectors included with Radiator's AKA module.  (To use this mode, no SIM card is required, but the PCSC library is still needed to build.  Also, some errors will be displayed.)
 *
 * Revision 1.37  2004/06/29 01:35:34  chessing
 *
 * Added credit to eapleap.c for Gilbert Goodwill (who gave me the clues I needed to figure out the keying piece for LEAP).  Added patch from Toby Collett to make PEAP work for broken implementations of IAS.  (New option ias_quirk has been added.)
 *
 * Revision 1.36  2004/06/22 20:14:22  chessing
 *
 * Fixed some compile problems where SIM/AKA code was trying to compile even when it wasn't supposed to.
 *
 * Revision 1.35  2004/06/21 05:19:12  chessing
 *
 * Added a few minor fixes to EAP-AKA support.  Added "allmulti" as a global configuration option.  (By default, allmulti is now on!)
 *
 * Revision 1.34  2004/06/15 03:35:19  chessing
 *
 * New updates including fixes to LEAP (keying now works with wireless) and adding EAP-AKA.
 *
 * Revision 1.33  2004/06/15 03:22:17  chessing
 *
 * XSupplicant Release 1.0
 *
 *
 *******************************************************************/

#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <stdio.h>

#include "xsup_debug.h"
#include "xsup_err.h"
#include "config.h"
#include "wpa.h"

#ifdef EAP_SIM_ENABLE
#include "winscard.h"
#endif

// there has GOT to be a better way than this...
#include "eap_types/md5/eapmd5.h"
#include "eap_types/tls/eaptls.h"
#include "eap_types/ttls/eapttls.h"
#include "eap_types/mschapv2/eapmschapv2.h"
#include "eap_types/peap/eappeap.h"
#include "eap_types/leap/eapleap.h"
#include "eap_types/sim/eapsim.h"
#include "eap_types/aka/eapaka.h"


struct config_data *config_info = NULL; // the configuration data structure
int config_parse_debug = 1;
extern FILE *yyin;
extern int config_linenum;
extern int yyparse(void);

#define FREE_STRING(x) if (x != NULL) {free(x); x = NULL;}

/****************************************
 *
 * Load all of the configuration information in to memory.  We should
 * set a flag to make sure that we have loaded the config before we
 * call config_build().
 *
 ****************************************/
int config_setup(char *path_to_config)
{
  /* Make sure we got a parameter */
  if (path_to_config == NULL) {
    printf("Doing Debug...\n");
    debug_printf(DEBUG_NORMAL, "Configuration file not given\n");
    debug_printf(DEBUG_NORMAL, "This should NEVER happen!\n");
    return XECONFIGFILEFAIL;
  }

  /* check to see if we can really open this file */
  yyin = fopen(path_to_config, "r");
  if (yyin == NULL) {
    debug_printf(DEBUG_NORMAL, "Failed to open configuration %s\n\n", path_to_config);
    return XECONFIGFILEFAIL;
  }
  
  /* check to see if the configuration is already set */
  if (config_info) {
    debug_printf(DEBUG_NORMAL, "config_setup called, but configuration is already loaded. Call config_destroy\n");
    return XECONFIGFILEFAIL;
  }

  /* parse the file */
  if (config_parse() != XENONE) {
    delete_config_data(&config_info);
    return XECONFIGPARSEFAIL;
  }

  // set the file name
  if (config_info)
    config_info->config_fname = strdup(path_to_config);

  return XENONE;
}


struct config_network *config_find_network(struct config_network *nethead, 
					   char *matchname)
{
  struct config_network *cur;

  cur = nethead;

  if ((nethead == NULL) || (matchname == NULL))
    {
      return NULL;
    }

  while ((cur != NULL) && (strcmp(cur->name, matchname) != 0))
    {
      cur = cur->next;
    }
  
  // If we got a match, return it.
  if (cur != NULL)
    {
      return cur;
    }
  
  // Otherwise, look against the essid.
  cur = nethead;

  if ((cur != NULL) && (cur->ssid != NULL))
    {
      while ((cur != NULL) && (strcmp(cur->ssid, matchname) != 0))
	{
	  cur = cur->next;
	}
  
      // Do we have a match on ssid?
      if (cur != NULL)
	{
	  return cur;
	}
    }

  return NULL;
}

/****************************************
 *
 * Get the priority value based on a network name.
 *
 ****************************************/
int config_get_priority(char *network_name)
{
  struct config_network *result;

  if (config_info == NULL) 
    debug_printf(DEBUG_NORMAL, "No valid configuration information to search!\n");

  if (network_name == NULL)
    debug_printf(DEBUG_NORMAL, "Invalid network name passed to %s.\n",
		 __FUNCTION__);

  result = config_find_network(config_info->networks, network_name);

  if (result == NULL)
    {
      debug_printf(DEBUG_NORMAL, "Couldn't find configuration information for network %s!\n", network_name);
      return 0;
    }

  debug_printf(DEBUG_CONFIG, "Returning Priority of %d\n", 
	       result->priority);

  return result->priority;
}

/****************************************
 *
 * Get configuration information out of memory, and populate the userdata
 * structure.
 *
 ****************************************/
struct config_network *config_build(char *network_name)
{
  struct config_network *result;

  if (config_info != NULL)
    {
      debug_printf(DEBUG_CONFIG, "Working from config file %s.\n",config_info->config_fname);

      // We were passed in a "network name".  First, look through the config
      // to see if it matches any friendly names.
      result = config_find_network(config_info->networks, network_name);

      if (result != NULL) return result;

      // This is not good.  We don't have any configuration information
      // for the requested network.  So, we need to return the default
      // information, and a warning.
      debug_printf(DEBUG_NORMAL, "No configuration information for network \"%s\" found.  Using default.\n", network_name);

      result = config_find_network(config_info->networks, 
				   config_info->globals->default_net);

      if (result != NULL) return result;

      // Uh oh..  We didn't find *anything*.
      debug_printf(DEBUG_NORMAL, "ERROR : No valid network profile could be located!  (Even tried default.)\n");

    } else {
      debug_printf(DEBUG_CONFIG, "config_info == NULL!  No config to update!\n");
    }
  return NULL;
}

/*********************************************
 *
 * Determine if the interface given is in the list of interfaces.
 *
 *********************************************/
int config_int_in_list(char *intName, struct config_string_list *strList)
{
  struct config_string_list *cur;

  if ((!intName) || (!strList))
    {
      debug_printf(DEBUG_NORMAL, "Invalid data passed in to int_in_list()!\n");
      return XEMALLOC;
    }

  // Our check is case sensative!
  cur = strList;

  while ((cur != NULL) && (strcmp(intName, cur->name) != 0))
    {
      cur = cur->next;
    }

  if (cur == NULL)
    {
      return FALSE;    // The interface isn't in our list.
    } 

  return TRUE;
}


/************************************
 *
 * Set statemachine/config related variables for this interface.
 *
 ************************************/
int config_set_globals(struct interface_data *myint)
{

  if (myint == NULL)
    {
      debug_printf(DEBUG_NORMAL, "No configuration information is available!\n");
      return -1;
    }
  // Start by seeing if we need to set any global values.
  if ((CONFIG_GLOBALS_AUTH_PER & config_info->globals->flags) ==
      CONFIG_GLOBALS_AUTH_PER)
    {
      myint->statemachine->authPeriod = config_info->globals->auth_period;
    }

  if ((CONFIG_GLOBALS_HELD_PER & config_info->globals->flags) ==
      CONFIG_GLOBALS_HELD_PER)
    {
      myint->statemachine->heldPeriod = config_info->globals->held_period;
    }

  if ((CONFIG_GLOBALS_MAX_STARTS & config_info->globals->flags) ==
      CONFIG_GLOBALS_MAX_STARTS)
    {
      myint->statemachine->maxStart = config_info->globals->max_starts;
    }
 
  return 0;
}

/************************************
 *
 * Return a list of allowed interfaces.
 *
 ************************************/
struct config_string_list *config_allowed_interfaces()
{
  return config_info->globals->allow_interfaces;
}

/************************************
 *
 * Return a list of denied interfaces.
 *
 ************************************/
struct config_string_list *config_denied_interfaces()
{
  return config_info->globals->deny_interfaces;
}

/************************************
 *
 * Return the startup command we want to use.  The caller should *NOT* free
 * the resulting variable!
 *
 ************************************/
char *config_get_startup_cmd()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      debug_printf(DEBUG_CONFIG, "(startup_cmd) No configuration information available!\n");
      return NULL;
    }

  return config_info->globals->startup_command;
}

char *config_get_first_auth_cmd()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      debug_printf(DEBUG_CONFIG, "(first_auth_cmd) No configuration information available!\n");
      return NULL;
    }
  return config_info->globals->first_auth_command;
}

char *config_get_reauth_cmd()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      debug_printf(DEBUG_CONFIG, "(reauth_cmd) No configuration information available!\n");
      return NULL;
    }
  return config_info->globals->reauth_command;
}

char config_get_allmulti()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      debug_printf(DEBUG_CONFIG, "No configuration information available!  ALLMULTI will return default value!\n");
      return 1;
    }
  return config_info->globals->allmulti;
}

char config_get_association()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      debug_printf(DEBUG_CONFIG, "No configuration information available!  ASSOCIATION will return default value!\n");
      return 0;
    }
  return config_info->globals->association;
}

/**********************************************************************************
 * NOTE: Do *NOT* debug_printf() in this function or you'll cause a recursive loop.
 **********************************************************************************/
char *config_get_logfile()
{
  if ((config_info == NULL) || (config_info->globals == NULL))
    {
      return NULL;
    }
  return config_info->globals->logfile;
}

/************************************
 *
 * Clean up any memory that we have used to store the configuration information
 * 
 ************************************/
void config_destroy()
{
  /* close the input file */
  if (yyin)
    fclose(yyin);

  /* see if there really is something to cleanup */
  delete_config_data(&config_info);
}

/************************************
 *
 * Temporary test function for parsing
 *
 ************************************/
int config_parse()
{
  if (yyparse() != XENONE) {
    return XECONFIGPARSEFAIL;
  }
  return XENONE;
}


//****************************************
// CONFIG QUERIES
//****************************************

/******************************************
 *
 * See if the network config is currently in memory
 *
 ******************************************/
int config_contains_network(char *netname) 
{
  if (!config_info || !config_info->networks)
    return FALSE;
  return config_network_contains_net(config_info->networks, netname);
}

/******************************************
 *
 * See if network config is  allowed
 * 
 ******************************************/
int config_allows_network(struct config_data *conf, char *netname)
{
  struct config_string_list *current;
  // make sure we have a config and globals
  if (!conf || !conf->globals)
    return FALSE;

  current = conf->globals->allowed_nets;
  
  // lack of an allowed list means all nets are allowed
  if (current == NULL) 
    return TRUE;

  if (config_string_list_contains_string(current, netname))
    return TRUE;

  return FALSE;
}


//**********************************************
// Private functions for config parsing. Do 
// not call these from outside config code
//**********************************************

  /*******************/
 /* CONFIG_WPA_PSK  */
/*******************/

void delete_config_wpa_psk(struct config_wpa_psk **tmp_wpa_psk)
{
  if (*tmp_wpa_psk == NULL)
    return;

  FREE_STRING((*tmp_wpa_psk)->key);
  FREE_STRING((*tmp_wpa_psk)->hex_key);

  free(*tmp_wpa_psk);
  *tmp_wpa_psk = NULL;
}

void initialize_config_wpa_psk(struct config_wpa_psk **tmp_wpa_psk)
{
  if (*tmp_wpa_psk != NULL) {
    delete_config_wpa_psk(tmp_wpa_psk);
  }
  *tmp_wpa_psk = 
    (struct config_wpa_psk *)malloc(sizeof(struct config_wpa_psk));
  if (*tmp_wpa_psk)
    memset(*tmp_wpa_psk, 0, sizeof(struct config_wpa_psk));
}

void dump_config_wpa_psk(struct config_wpa_psk *tmp_wpa_psk)
{
  if (!tmp_wpa_psk)
    return;
  debug_printf(DEBUG_NORMAL, "\t---------------wpa-psk--------------\n");
  debug_printf(DEBUG_NORMAL, "\t  ASCII key: \"%s\"\n", tmp_wpa_psk->key);
  debug_printf(DEBUG_NORMAL, "\t  HEX key: \"%s\"\n", tmp_wpa_psk->hex_key);
  debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
}

  /*******************/
 /* CONFIG_TLS      */
/*******************/

/* take a pointer to a config_eap_tls and cleanly delete the structure
   then make it NULL */
void delete_config_eap_tls(struct config_eap_tls **tmp_tls)
{
  if (*tmp_tls == NULL)
    return;

  FREE_STRING((*tmp_tls)->user_cert);
  FREE_STRING((*tmp_tls)->root_cert);
  FREE_STRING((*tmp_tls)->root_dir);  
  FREE_STRING((*tmp_tls)->crl_dir);  
  FREE_STRING((*tmp_tls)->user_key);
  FREE_STRING((*tmp_tls)->user_key_pass);
  FREE_STRING((*tmp_tls)->random_file);
  
  free (*tmp_tls);
  *tmp_tls = NULL;
}

/* take a pointer to a config_eap_tls and put a blank one there */
void initialize_config_eap_tls(struct config_eap_tls **tmp_tls)
{
  if (*tmp_tls != NULL) {
    delete_config_eap_tls(tmp_tls);
  }
  *tmp_tls = 
    (struct config_eap_tls *)malloc(sizeof(struct config_eap_tls));
  if (*tmp_tls)
    memset(*tmp_tls, 0, sizeof(struct config_eap_tls));
}

void dump_config_eap_tls(struct config_eap_tls *tls)
{
  if (!tls)
    return;
  debug_printf(DEBUG_NORMAL, "\t---------------eap-tls--------------\n");
  debug_printf(DEBUG_NORMAL, "\t  TLS Cert: \"%s\"\n", tls->user_cert);
  debug_printf(DEBUG_NORMAL, "\t  TLS Root Cert: \"%s\"\n", tls->root_cert);
  debug_printf(DEBUG_NORMAL, "\t  TLS Root Dir: \"%s\"\n", tls->root_dir);
  debug_printf(DEBUG_NORMAL, "\t  TLS CRL Dir: \"%s\"\n", tls->crl_dir);
  debug_printf(DEBUG_NORMAL, "\t  TLS Key: \"%s\"\n", tls->user_key);
  debug_printf(DEBUG_NORMAL, "\t  TLS Key Pass: \"%s\"\n", tls->user_key_pass);
  debug_printf(DEBUG_NORMAL, "\t  TLS Chunk Size: %d\n", tls->chunk_size);
  debug_printf(DEBUG_NORMAL, "\t  TLS Random Source: \"%s\"\n", 
	       tls->random_file);
  debug_printf(DEBUG_NORMAL, "\t  TLS Session Resumption: ");
  switch (tls->session_resume)
    {
    case RES_UNSET:
      debug_printf_nl(DEBUG_NORMAL, "UNSET\n");
      break;
    case RES_YES:
      debug_printf_nl(DEBUG_NORMAL, "YES\n");
      break;
    case RES_NO:
      debug_printf_nl(DEBUG_NORMAL, "NO\n");
      break;
    }
  debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
}


  /*******************/
 /* CONFIG_MD5      */
/*******************/
void delete_config_eap_md5(struct config_eap_md5 **tmp_md5)
{
  if (*tmp_md5 == NULL)
    return;

  FREE_STRING((*tmp_md5)->username);
  FREE_STRING((*tmp_md5)->password);

  free (*tmp_md5);
  *tmp_md5 = NULL;
}

void initialize_config_eap_md5(struct config_eap_md5 **tmp_md5)
{
  if (*tmp_md5 != NULL) {
    delete_config_eap_md5(tmp_md5);
  }
  *tmp_md5 = 
    (struct config_eap_md5 *)malloc(sizeof(struct config_eap_md5));  
  if (*tmp_md5)
    memset(*tmp_md5, 0, sizeof(struct config_eap_md5));
}

void dump_config_eap_md5(struct config_eap_md5 *md5, int level)
{
  if (!md5)
    return;
  if (level == 0) {
    debug_printf(DEBUG_NORMAL, "\t---------------eap-md5--------------\n");
    debug_printf(DEBUG_NORMAL, "\t  MD5 User: \"%s\"\n", md5->username);
    debug_printf(DEBUG_NORMAL, "\t  MD5 Pass: \"%s\"\n", md5->password);
    debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
  }else {
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  eap-md5  ^ ^ ^\n");
    debug_printf(DEBUG_NORMAL, "\t\t  MD5 User: \"%s\"\n", 
		 md5->username);
    debug_printf(DEBUG_NORMAL, "\t\t  MD5 Pass: \"%s\"\n", 
		 md5->password);
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^\n");    
  }
  
}


  /*******************/
 /* CONFIG_TTLS     */
/*******************/

//--------
// PAP
//--------
void delete_config_pap(struct config_pap **tmp_pap)
{
  if (*tmp_pap == NULL)
    return;

  FREE_STRING((*tmp_pap)->username);
  FREE_STRING((*tmp_pap)->password);

  free (*tmp_pap);
  *tmp_pap = NULL;
}

void initialize_config_pap(struct config_pap **tmp_pap)
{
  if (*tmp_pap != NULL) {
    delete_config_pap(tmp_pap);
  }
  *tmp_pap = 
    (struct config_pap *)malloc(sizeof(struct config_pap));  
  if (*tmp_pap)
    memset(*tmp_pap, 0, sizeof(struct config_pap));
}

void dump_config_pap(struct config_pap *pap)
{
  if (!pap)
    return;
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  pap  ^ ^ ^\n");
  debug_printf(DEBUG_NORMAL, "\t\t  PAP User: \"%s\"\n", pap->username);
  debug_printf(DEBUG_NORMAL, "\t\t  PAP Pass: \"%s\"\n", pap->password);
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^\n");
}

//--------
// CHAP
//--------
void delete_config_chap(struct config_chap **tmp_chap)
{
  if (*tmp_chap == NULL)
    return;

  FREE_STRING((*tmp_chap)->username);
  FREE_STRING((*tmp_chap)->password);

  free (*tmp_chap);
  *tmp_chap = NULL;
}

void initialize_config_chap(struct config_chap **tmp_chap)
{
  if (*tmp_chap != NULL) {
    delete_config_chap(tmp_chap);
  }
  *tmp_chap = 
    (struct config_chap *)malloc(sizeof(struct config_chap));  
  if (*tmp_chap)
    memset(*tmp_chap, 0, sizeof(struct config_chap));
}

void dump_config_chap(struct config_chap *chap)
{
  if (!chap)
    return;
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  chap  ^ ^ ^\n");
  debug_printf(DEBUG_NORMAL, "\t\t  CHAP User: \"%s\"\n", chap->username);
  debug_printf(DEBUG_NORMAL, "\t\t  CHAP Pass: \"%s\"\n", chap->password);
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^\n");
}

//--------
// MSCHAP
//--------
void delete_config_mschap(struct config_mschap **tmp_mschap)
{
  if (*tmp_mschap == NULL)
    return;

  FREE_STRING((*tmp_mschap)->username);
  FREE_STRING((*tmp_mschap)->password);

  free (*tmp_mschap);
  *tmp_mschap = NULL;
}

void initialize_config_mschap(struct config_mschap **tmp_mschap)
{
  if (*tmp_mschap != NULL) {
    delete_config_mschap(tmp_mschap);
  }
  *tmp_mschap = 
    (struct config_mschap *)malloc(sizeof(struct config_mschap));  
  if (*tmp_mschap)
    memset(*tmp_mschap, 0, sizeof(struct config_mschap));
}

void dump_config_mschap(struct config_mschap *mschap)
{
  if (!mschap)
    return;
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  mschap  ^ ^ ^\n");
  debug_printf(DEBUG_NORMAL, "\t\t  MSCHAP User: \"%s\"\n", mschap->username);
  debug_printf(DEBUG_NORMAL, "\t\t  MSCHAP Pass: \"%s\"\n", mschap->password);
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^\n");
}

//--------
// MSCHAPV2
//--------
void delete_config_mschapv2(struct config_mschapv2 **tmp_mschapv2)
{
  if (*tmp_mschapv2 == NULL)
    return;

  FREE_STRING((*tmp_mschapv2)->username);
  FREE_STRING((*tmp_mschapv2)->password);

  free (*tmp_mschapv2);
  *tmp_mschapv2 = NULL;
}

void initialize_config_mschapv2(struct config_mschapv2 **tmp_mschapv2)
{
  if (*tmp_mschapv2 != NULL) {
    delete_config_mschapv2(tmp_mschapv2);
  }
  *tmp_mschapv2 = 
    (struct config_mschapv2 *)malloc(sizeof(struct config_mschapv2));  
  if (*tmp_mschapv2)
    memset(*tmp_mschapv2, 0, sizeof(struct config_mschapv2));
}

void dump_config_mschapv2(struct config_mschapv2 *mschapv2)
{
  if (!mschapv2)
    return;
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  mschapv2  ^ ^ ^\n");
  debug_printf(DEBUG_NORMAL, "\t\t  MSCHAPv2 User: \"%s\"\n", 
	       mschapv2->username);
  debug_printf(DEBUG_NORMAL, "\t\t  MSCHAPv2 Pass: \"%s\"\n", 
	       mschapv2->password);
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^\n");
}


//-------------
// TTLS_PHASE2
//------------
// Be SURE to call config_ttls_phase2_contains_phase2 BEFORE adding.
// no such check will be done here.
void add_config_ttls_phase2(struct config_ttls_phase2 **phase2,
			   ttls_phase2_type phase2_type, void *phase2_data)
{
  struct config_ttls_phase2 *tmp, *newphase2;

  if (!phase2_data)
    return;

  newphase2 = 
    (struct config_ttls_phase2 *)malloc(sizeof(struct config_ttls_phase2));
  if (newphase2 == NULL)
    return;
  memset(newphase2, 0, sizeof(struct config_ttls_phase2));
  newphase2->phase2_type = phase2_type;
  newphase2->phase2_data = phase2_data;
  
  if (*phase2 == NULL) {
    *phase2 = newphase2;
    return;
  }

  tmp = *phase2;

  while (tmp->next != NULL) {
    tmp = tmp->next;
  }
  tmp->next = newphase2;
}

int config_ttls_phase2_contains_phase2(struct config_ttls_phase2 *phase2,
				      ttls_phase2_type new_type)
{
  struct config_ttls_phase2 *tmp;

  if (!phase2)
    return 0;
  
  tmp = phase2;
  while (tmp) {
    if (tmp->phase2_type == new_type)
      return 1;
    tmp = tmp->next;
  }

  return 0;
}

void delete_config_ttls_phase2 (struct config_ttls_phase2 **phase2)
{
  if (*phase2 == NULL)
    return;
  switch ((*phase2)->phase2_type) {
  case TTLS_PHASE2_PAP:
    delete_config_pap((struct config_pap **)&(*phase2)->phase2_data);
    break;
  case TTLS_PHASE2_CHAP: 
    delete_config_chap((struct config_chap **)&(*phase2)->phase2_data);
    break;
  case TTLS_PHASE2_MSCHAP:
    delete_config_mschap((struct config_mschap **)&(*phase2)->phase2_data);
    break;
  case TTLS_PHASE2_MSCHAPV2:
    delete_config_mschapv2((struct config_mschapv2 **)&(*phase2)->phase2_data);
    break;
  default:
    debug_printf(DEBUG_NORMAL, "AAAH! Trying to delete an undefined config"
		 " type.\nNotify developers. Type: 0x%x\n", 
		 (*phase2)->phase2_type);
  }
  if ((*phase2)->next)
    delete_config_ttls_phase2(&(*phase2)->next);
}

void dump_config_ttls_phase2(struct config_ttls_phase2 *phase2) {
  if (phase2 == NULL)
    return;
  switch ((phase2)->phase2_type) {
  case TTLS_PHASE2_PAP:
    dump_config_pap((struct config_pap *)(phase2)->phase2_data);
    break;
  case TTLS_PHASE2_CHAP: 
    dump_config_chap((struct config_chap *)(phase2)->phase2_data);
    break;
  case TTLS_PHASE2_MSCHAP:
    dump_config_mschap((struct config_mschap *)(phase2)->phase2_data);
    break;
  case TTLS_PHASE2_MSCHAPV2:
    dump_config_mschapv2((struct config_mschapv2 *)(phase2)->phase2_data);
    break;
  default:
    debug_printf(DEBUG_NORMAL, "AAAH! Trying to dump an undefined config"
		 " type.\nNotify developers. Type: 0x%x\n", 
		 (phase2)->phase2_type);
  }
  if ((phase2)->next)
    dump_config_ttls_phase2((phase2)->next);
}

void delete_config_eap_ttls(struct config_eap_ttls **tmp_ttls)
{
  if (*tmp_ttls == NULL)
    return;

  FREE_STRING((*tmp_ttls)->user_cert);
  FREE_STRING((*tmp_ttls)->root_cert);
  FREE_STRING((*tmp_ttls)->root_dir);
  FREE_STRING((*tmp_ttls)->crl_dir);
  FREE_STRING((*tmp_ttls)->user_key);
  FREE_STRING((*tmp_ttls)->user_key_pass);
  FREE_STRING((*tmp_ttls)->random_file);  
  FREE_STRING((*tmp_ttls)->cncheck);
  if ((*tmp_ttls)->phase2) 
    delete_config_ttls_phase2(&(*tmp_ttls)->phase2);

  free (*tmp_ttls);
  *tmp_ttls = NULL;
}

void initialize_config_eap_ttls(struct config_eap_ttls **tmp_ttls)
{
  if (*tmp_ttls != NULL) {
    delete_config_eap_ttls(tmp_ttls);
  }
  *tmp_ttls = 
    (struct config_eap_ttls *)malloc(sizeof(struct config_eap_ttls));  
  if (*tmp_ttls == NULL)
    return;
  memset(*tmp_ttls, 0, sizeof(struct config_eap_ttls));
  (*tmp_ttls)->phase2_type = TTLS_PHASE2_UNDEFINED;
}

void dump_config_eap_ttls(struct config_eap_ttls *ttls)
{
  if (!ttls) {
    return;
  }
  debug_printf(DEBUG_NORMAL, "\t---------------eap-ttls--------------\n");
  debug_printf(DEBUG_NORMAL, "\t  TTLS Cert: \"%s\"\n", ttls->user_cert);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Root Cert: \"%s\"\n", ttls->root_cert);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Root Dir: \"%s\"\n", ttls->root_dir);
  debug_printf(DEBUG_NORMAL, "\t  TTLS CRL Dir: \"%s\"\n", ttls->crl_dir);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Key: \"%s\"\n", ttls->user_key);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Key Pass: \"%s\"\n", ttls->user_key_pass);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Chunk Size: %d\n", ttls->chunk_size);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Random Source: \"%s\"\n", 
	       ttls->random_file);
  debug_printf(DEBUG_NORMAL, "\t  TTLS CN to Check : \"%s\"\n", ttls->cncheck);
  debug_printf(DEBUG_NORMAL, "\t  TTLS Exact CN Match : %s\n",  
	       ttls->cnexact ? "yes" : "no"); 
  debug_printf(DEBUG_NORMAL, "\t  TTLS Session Resumption: ");
  switch (ttls->session_resume)
    {
    case RES_UNSET:
      debug_printf_nl(DEBUG_NORMAL, "UNSET\n");
      break;
    case RES_YES:
      debug_printf_nl(DEBUG_NORMAL, "YES\n");
      break;
    case RES_NO:
      debug_printf_nl(DEBUG_NORMAL, "NO\n");
      break;
    }
  switch (ttls->phase2_type) {
  case TTLS_PHASE2_PAP:
    debug_printf(DEBUG_NORMAL, "\t  TTLS phase2: pap\n");    
    break;
  case TTLS_PHASE2_CHAP:
    debug_printf(DEBUG_NORMAL, "\t  TTLS phase2: chap\n");    
    break;
  case TTLS_PHASE2_MSCHAP:
    debug_printf(DEBUG_NORMAL, "\t  TTLS phase2: mschap\n");    
    break;
  case TTLS_PHASE2_MSCHAPV2:
    debug_printf(DEBUG_NORMAL, "\t  TTLS phase2: mschapv2\n");        
    break;
  default:
    debug_printf(DEBUG_NORMAL, "\t  TTLS phase2: UNDEFINED\n");    
    break;
  }
  if (ttls->phase2) dump_config_ttls_phase2(ttls->phase2);
  debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
}

int check_config_eap_ttls(struct config_eap_ttls *tmp_ttls)
{
  int errno = 0;
  
  if (tmp_ttls->phase2_type == TTLS_PHASE2_UNDEFINED || !tmp_ttls->phase2) {
    debug_printf(DEBUG_NORMAL, "No phase2 defined for ttls\n");
    errno = -1;
  }
  
  if (!config_ttls_phase2_contains_phase2(tmp_ttls->phase2, 
					  tmp_ttls->phase2_type)) {
    debug_printf(DEBUG_NORMAL, "Phase2 type chosen, but not defined.\n");
    errno = -1;      
  }
  return errno;
}

  /*******************/
 /* CONFIG_LEAP     */
/*******************/
void delete_config_eap_leap(struct config_eap_leap **tmp_leap)
{
  if (*tmp_leap == NULL)
    return;

  FREE_STRING((*tmp_leap)->username);
  FREE_STRING((*tmp_leap)->password);

  free (*tmp_leap);
  *tmp_leap = NULL;
}

void initialize_config_eap_leap(struct config_eap_leap **tmp_leap)
{
  if (*tmp_leap != NULL) {
    delete_config_eap_leap(tmp_leap);
  }
  *tmp_leap = 
    (struct config_eap_leap *)malloc(sizeof(struct config_eap_leap));  
  if (*tmp_leap)
    memset(*tmp_leap, 0, sizeof(struct config_eap_leap));
}

void dump_config_eap_leap(struct config_eap_leap *leap)
{
  if (!leap)
    return;
  debug_printf(DEBUG_NORMAL, "\t---------------eap-leap--------------\n");
  debug_printf(DEBUG_NORMAL, "\t  LEAP User: \"%s\"\n", leap->username);
  debug_printf(DEBUG_NORMAL, "\t  LEAP Pass: \"%s\"\n", leap->password);
  debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
}

  /*******************/
 /* CONFIG_MSCHAPV2 */
/*******************/
void delete_config_eap_mschapv2(struct config_eap_mschapv2 **tmp_mschapv2)
{
  if (*tmp_mschapv2 == NULL)
    return;

  FREE_STRING((*tmp_mschapv2)->username);
  FREE_STRING((*tmp_mschapv2)->password);

  free (*tmp_mschapv2);
  *tmp_mschapv2 = NULL;
}

void initialize_config_eap_mschapv2(struct config_eap_mschapv2 **tmp_mschapv2)
{
  if (*tmp_mschapv2 != NULL) {
    delete_config_eap_mschapv2(tmp_mschapv2);
  }
  *tmp_mschapv2 = 
    (struct config_eap_mschapv2 *)malloc(sizeof(struct config_eap_mschapv2));  
  if (*tmp_mschapv2)
    memset(*tmp_mschapv2, 0, sizeof(struct config_eap_mschapv2));
}

void dump_config_eap_mschapv2(struct config_eap_mschapv2 *mschapv2, int level)
{
  if (!mschapv2)
    return;
  if (level == 0) {
    debug_printf(DEBUG_NORMAL, "\t---------------eap-mschapv2--------------\n");
    debug_printf(DEBUG_NORMAL, "\t  MSCHAPV2 User: \"%s\"\n", 
		 mschapv2->username);
    debug_printf(DEBUG_NORMAL, "\t  MSCHAPV2 Pass: \"%s\"\n", 
		 mschapv2->password);
    debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
  }else {
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  eap-mschapv2  ^ ^ ^\n");
    debug_printf(DEBUG_NORMAL, "\t\t  MSCHAPV2 User: \"%s\"\n", 
		 mschapv2->username);
    debug_printf(DEBUG_NORMAL, "\t\t  MSCHAPV2 Pass: \"%s\"\n", 
		 mschapv2->password);
  debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^\n");
  }

}


  /*******************/
 /* CONFIG_PEAP     */
/*******************/
void delete_config_eap_peap(struct config_eap_peap **tmp_peap)
{
  if (*tmp_peap == NULL)
    return;

  FREE_STRING((*tmp_peap)->identity);
  FREE_STRING((*tmp_peap)->user_cert);
  FREE_STRING((*tmp_peap)->root_cert);
  FREE_STRING((*tmp_peap)->root_dir);
  FREE_STRING((*tmp_peap)->crl_dir);
  FREE_STRING((*tmp_peap)->user_key)
  FREE_STRING((*tmp_peap)->user_key_pass);
  FREE_STRING((*tmp_peap)->random_file);
  FREE_STRING((*tmp_peap)->cncheck);
  if ((*tmp_peap)->phase2)
    delete_config_eap_method(&(*tmp_peap)->phase2);

  free (*tmp_peap);
  *tmp_peap = NULL;
}

void initialize_config_eap_peap(struct config_eap_peap **tmp_peap)
{
  if (*tmp_peap != NULL) {
    delete_config_eap_peap(tmp_peap);
  }
  *tmp_peap = 
    (struct config_eap_peap *)malloc(sizeof(struct config_eap_peap));  
  if (*tmp_peap)
    memset(*tmp_peap, 0, sizeof(struct config_eap_peap));
}

void dump_config_eap_peap(struct config_eap_peap *peap)
{
  if (!peap)
    return;
  debug_printf(DEBUG_NORMAL, "\t---------------eap-peap--------------\n");
  debug_printf(DEBUG_NORMAL, "\t  PEAP phase2 identity: \"%s\"\n", 
	       peap->identity);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Cert: \"%s\"\n", peap->user_cert);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Root Cert: \"%s\"\n", peap->root_cert);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Root Dir: \"%s\"\n", peap->root_dir);
  debug_printf(DEBUG_NORMAL, "\t  PEAP CRL Dir: \"%s\"\n", peap->crl_dir);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Key: \"%s\"\n", peap->user_key);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Key Pass: \"%s\"\n", peap->user_key_pass);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Chunk Size: %d\n", peap->chunk_size);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Random Source: \"%s\"\n", 
	       peap->random_file);
  debug_printf(DEBUG_NORMAL, "\t  PEAP CN to Check : \"%s\"\n", peap->cncheck);
  debug_printf(DEBUG_NORMAL, "\t  PEAP Exact CN Match : %s\n",  
	       peap->cnexact ? "yes" : "no");  
  debug_printf(DEBUG_NORMAL, "\t  PEAP Session Resumption: ");
  switch (peap->session_resume)
    {
    case RES_UNSET:
      debug_printf_nl(DEBUG_NORMAL, "UNSET\n");
      break;
    case RES_YES:
      debug_printf_nl(DEBUG_NORMAL, "YES\n");
      break;
    case RES_NO:
      debug_printf_nl(DEBUG_NORMAL, "NO\n");
      break;
    }
   debug_printf(DEBUG_NORMAL, "\t  PEAP IAS_QUIRK: \"%s\"\n", 
		 peap->ias_quirk ? "Yes" : "No");
    
  if (TEST_FLAG(peap->flags, CONFIG_PEAP_ALLOW_MSCV2))
    debug_printf(DEBUG_NORMAL,"\t   Allow Phase 2 Type: MSCHAPv2\n");
  if (TEST_FLAG(peap->flags, CONFIG_PEAP_ALLOW_MD5))
    debug_printf(DEBUG_NORMAL,"\t   Allow Phase 2 Type: MD5\n");
  if (TEST_FLAG(peap->flags, CONFIG_PEAP_ALLOW_SIM))
    debug_printf(DEBUG_NORMAL,"\t   Allow Phase 2 Type: SIM\n");
  if (TEST_FLAG(peap->flags, CONFIG_PEAP_ALLOW_GTC))
    debug_printf(DEBUG_NORMAL,"\t   Allow Phase 2 Type: GTC\n");
  if (TEST_FLAG(peap->flags, CONFIG_PEAP_ALLOW_OTP))
    debug_printf(DEBUG_NORMAL,"\t   Allow Phase 2 Type: OTP\n");
  if (peap->phase2) dump_config_eap_method(peap->phase2, 1);
  debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
}


  /*******************/
 /* CONFIG_SIM      */
/*******************/
void delete_config_eap_sim(struct config_eap_sim **tmp_sim)
{
  if (*tmp_sim == NULL)
    return;

  FREE_STRING((*tmp_sim)->username);
  FREE_STRING((*tmp_sim)->password);

  free (*tmp_sim);
  *tmp_sim = NULL;
}

void initialize_config_eap_sim(struct config_eap_sim **tmp_sim)
{
  if (*tmp_sim != NULL) {
    delete_config_eap_sim(tmp_sim);
  }
  *tmp_sim = 
    (struct config_eap_sim *)malloc(sizeof(struct config_eap_sim));  
  if (*tmp_sim)
    memset(*tmp_sim, 0, sizeof(struct config_eap_sim));
}

void dump_config_eap_sim(struct config_eap_sim *sim, int level)
{
  if (!sim)
    return;
  if (level == 0) {
    debug_printf(DEBUG_NORMAL, "\t---------------eap-sim--------------\n");
    debug_printf(DEBUG_NORMAL, "\t  SIM User: \"%s\"\n", sim->username);
    debug_printf(DEBUG_NORMAL, "\t  SIM Pass: \"%s\"\n", sim->password);
    debug_printf(DEBUG_NORMAL, "\t  SIM Auto Realm: %s\n",  
		 sim->auto_realm ? "yes" : "no");  
    debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
  } else {
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  eap-sim  ^ ^ ^\n");
    debug_printf(DEBUG_NORMAL, "\t\t  SIM User: \"%s\"\n", 
		 sim->username);
    debug_printf(DEBUG_NORMAL, "\t\t  SIM Pass: \"%s\"\n", 
		 sim->password);
    debug_printf(DEBUG_NORMAL, "\t\t  SIM Auto Realm: %s\n",  
		 sim->auto_realm ? "yes" : "no");  
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^\n");
  }
}

  /*******************/
 /* CONFIG_AKA      */
/*******************/
void delete_config_eap_aka(struct config_eap_aka **tmp_aka)
{
  if (*tmp_aka == NULL)
    return;

  FREE_STRING((*tmp_aka)->username);
  FREE_STRING((*tmp_aka)->password);

  free (*tmp_aka);
  *tmp_aka = NULL;
}

void initialize_config_eap_aka(struct config_eap_aka **tmp_aka)
{
  if (*tmp_aka != NULL) {
    delete_config_eap_aka(tmp_aka);
  }
  *tmp_aka = 
    (struct config_eap_aka *)malloc(sizeof(struct config_eap_aka));  
  if (*tmp_aka)
    memset(*tmp_aka, 0, sizeof(struct config_eap_aka));
}

void dump_config_eap_aka(struct config_eap_aka *aka, int level)
{
  if (!aka)
    return;
  if (level == 0) {
    debug_printf(DEBUG_NORMAL, "\t---------------eap-aka--------------\n");
    debug_printf(DEBUG_NORMAL, "\t  AKA User: \"%s\"\n", aka->username);
    debug_printf(DEBUG_NORMAL, "\t  AKA Pass: \"%s\"\n", aka->password);
    debug_printf(DEBUG_NORMAL, "\t  AKA Auto Realm: %s\n",  
		 aka->auto_realm ? "yes" : "no");  
    debug_printf(DEBUG_NORMAL, "\t------------------------------------\n");
  } else {
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^  eap-aka  ^ ^ ^\n");
    debug_printf(DEBUG_NORMAL, "\t\t  AKA User: \"%s\"\n", 
		 aka->username);
    debug_printf(DEBUG_NORMAL, "\t\t  AKA Pass: \"%s\"\n", 
		 aka->password);
    debug_printf(DEBUG_NORMAL, "\t\t  AKA Auto Realm: %s\n",  
		 aka->auto_realm ? "yes" : "no");  
    debug_printf(DEBUG_NORMAL, "\t\t^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^\n");
  }
}


  /*********************/
 /* CONFIG_EAP_METHOD */
/*********************/

// Be SURE to call config_eap_method_contains_method BEFORE adding.
// no such check will be done here.
void add_config_eap_method(struct config_eap_method **method,
			   int method_num, void *method_data)
{
  struct config_eap_method *tmp, *newmethod;

  if (!method_data)
    return;

  newmethod = 
    (struct config_eap_method *)malloc(sizeof(struct config_eap_method));
  if (newmethod == NULL)
    return;
  memset(newmethod, 0, sizeof(struct config_eap_method));
  newmethod->method_num = method_num;
  newmethod->method_data = method_data;
  
  if (*method == NULL) {
    *method = newmethod;
    return;
  }

  tmp = *method;

  while (tmp->next != NULL) {
    tmp = tmp->next;
  }
  tmp->next = newmethod;
}

void delete_config_eap_method(struct config_eap_method **method)
{
  if (*method == NULL)
    return;
  switch ((*method)->method_num) {
  case WPA_PSK:
    delete_config_wpa_psk((struct config_wpa_psk **)&((*method)->method_data));
    break;
  case EAP_TYPE_TLS:
    delete_config_eap_tls((struct config_eap_tls **)&((*method)->method_data));
    break;
  case EAP_TYPE_MD5:
    delete_config_eap_md5((struct config_eap_md5 **)&(*method)->method_data);
    break;
  case EAP_TYPE_PEAP:
    delete_config_eap_peap((struct config_eap_peap **)&(*method)->method_data);
    break;

#ifdef EAP_SIM_ENABLE
  case EAP_TYPE_SIM:
    delete_config_eap_sim((struct config_eap_sim **)&(*method)->method_data);
    break;
  case EAP_TYPE_AKA:
    delete_config_eap_aka((struct config_eap_aka **)&(*method)->method_data);
    break;
#endif

  case EAP_TYPE_TTLS:
    delete_config_eap_ttls((struct config_eap_ttls **)&(*method)->method_data);
    break; 
  case EAP_TYPE_LEAP:
    delete_config_eap_leap((struct config_eap_leap **)&(*method)->method_data);
    break;
  case EAP_TYPE_MSCHAPV2:
    delete_config_eap_mschapv2((struct config_eap_mschapv2 **)&(*method)->method_data);
    break;
    
  default:
    debug_printf(DEBUG_NORMAL, "AAAH! Trying to delete an undefined config"
		 " type.\nNotify developers. Type: 0x%x\n", 
		 (*method)->method_num);
  }
  if ((*method)->next)
    delete_config_eap_method(&(*method)->next);
  
}

void dump_config_eap_method(struct config_eap_method *method, int dumplevel)
{
  if (method == NULL)
    return;
  switch ((method)->method_num) {
  case WPA_PSK:
    dump_config_wpa_psk((struct config_wpa_psk *)((method)->method_data));
    break;
  case EAP_TYPE_TLS:
    dump_config_eap_tls((struct config_eap_tls *)((method)->method_data));
    break;
  case EAP_TYPE_MD5:
    dump_config_eap_md5((struct config_eap_md5 *)(method)->method_data, 
			dumplevel);
    break;
  case EAP_TYPE_PEAP:
    dump_config_eap_peap((struct config_eap_peap *)(method)->method_data);
    break;

#ifdef EAP_SIM_ENABLE
  case EAP_TYPE_SIM:
    dump_config_eap_sim((struct config_eap_sim *)(method)->method_data,
			dumplevel);
    break;
  case EAP_TYPE_AKA:
    dump_config_eap_aka((struct config_eap_aka *)(method)->method_data,
			dumplevel);
    break;
#endif

  case EAP_TYPE_TTLS:
    dump_config_eap_ttls((struct config_eap_ttls *)(method)->method_data);
    break; 
  case EAP_TYPE_LEAP:
    dump_config_eap_leap((struct config_eap_leap *)(method)->method_data);
  case EAP_TYPE_MSCHAPV2:
    dump_config_eap_mschapv2((struct config_eap_mschapv2 *)(method)->method_data,
			     dumplevel);
    break;
    
  default:
    debug_printf(DEBUG_NORMAL, "AAAH! Trying to dump an undefined config"
		 " type\n.Notify developers. Type: 0x%x\n", 
		 (method)->method_num);
  }

  dump_config_eap_method(method->next, dumplevel);
}

int config_eap_method_contains_method(struct config_eap_method *method,
				      int new_num)
{
  struct config_eap_method *tmp;

  if (!method)
    return 0;
  
  tmp = method;
  while (tmp) {
    if (tmp->method_num == new_num)
      return 1;
    tmp = tmp->next;
  }

  return 0;
}

  /*******************/
 /* CONFIG_NETWORK  */
/*******************/
void delete_config_network(struct config_network **tmp_network)
{
  if (*tmp_network == NULL)
    return;

  FREE_STRING((*tmp_network)->name);
  FREE_STRING((*tmp_network)->ssid);
  FREE_STRING((*tmp_network)->identity);

  if ((*tmp_network)->methods)
    delete_config_eap_method(&(*tmp_network)->methods);

  if ((*tmp_network)->next)
    delete_config_network(&(*tmp_network)->next);
      
  free (*tmp_network);
  *tmp_network = NULL;
}

void initialize_config_network(struct config_network **tmp_network)
{
  if (*tmp_network != NULL) {
    delete_config_network(tmp_network);
  }
  *tmp_network = 
    (struct config_network *)malloc(sizeof(struct config_network));  
  if (*tmp_network)
    memset(*tmp_network, 0, sizeof(struct config_network));
}

int config_network_contains_net(struct config_network *net, char *netname)
{
  while (net != NULL) {
    if (strcmp(net->name, netname) == 0)
      return TRUE;
    net = net->next;
  }
  return FALSE;
}

void config_network_add_net(struct config_network **list, 
			    struct config_network *toadd)
{
  struct config_network **current = list;
  while (*current != NULL) {
    if (strcmp((*current)->name, toadd->name) == 0) {
      return;
    }
    current = &(*current)->next;
  }
  (*current) = toadd;
}

void dump_config_network(struct config_network *net)
{
  if (!net)
    return;
  debug_printf(DEBUG_NORMAL, "+-+-+-+-+  Network Name: \"%s\" +-+-+-+-+\n",
	       net->name);
  if (net->type == UNSET)
    debug_printf(DEBUG_NORMAL, "  Type: UNSET\n");
  else if (net->type == WIRED)
    debug_printf(DEBUG_NORMAL, "  Type: WIRED\n");
  else
    debug_printf(DEBUG_NORMAL, "  Type: WIRELESS\n");

  if (net->wireless_ctrl == CTL_UNSET)
    debug_printf(DEBUG_NORMAL, "  Wireless Control: UNSET\n");
  else if (net->wireless_ctrl == CTL_YES)
    debug_printf(DEBUG_NORMAL, "  Wireless Control: YES\n");
  else
    debug_printf(DEBUG_NORMAL, "  Wireless Control: NO\n");

  debug_printf(DEBUG_NORMAL, "  Crypt Method: ");
  switch (net->crypt_type)
    {
    case CRYPT_ANY:
      debug_printf(DEBUG_NORMAL, "ANY\n");
      break;

    case CRYPT_WEP:
      debug_printf(DEBUG_NORMAL, "WEP\n");
      break;

    case CRYPT_WPA_TKIP:
      debug_printf(DEBUG_NORMAL, "WPA-TKIP\n");
      break;
      
    case CRYPT_WPA_CCMP:
      debug_printf(DEBUG_NORMAL, "WPA-CCMP\n");
      break;

    case CRYPT_WPA_ANY:
      debug_printf(DEBUG_NORMAL, "WPA-ANY\n");
      break;
    }

  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_WPA_PSK))
    debug_printf(DEBUG_NORMAL, "  Allow Type: WPA-PSK\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_TLS))
    debug_printf(DEBUG_NORMAL, "  Allow Type: TLS\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_MD5))
    debug_printf(DEBUG_NORMAL, "  Allow Type: MD5\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_TTLS))
    debug_printf(DEBUG_NORMAL, "  Allow Type: TTLS\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_LEAP))
    debug_printf(DEBUG_NORMAL, "  Allow Type: LEAP\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_MSCV2))
    debug_printf(DEBUG_NORMAL, "  Allow Type: MSCHAPv2\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_PEAP))
    debug_printf(DEBUG_NORMAL, "  Allow Type: PEAP\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_SIM))
    debug_printf(DEBUG_NORMAL, "  Allow Type: SIM\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_AKA))
    debug_printf(DEBUG_NORMAL, "  Allow Type: AKA\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_GTC))
    debug_printf(DEBUG_NORMAL, "  Allow Type: GTC\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_ALLOW_OTP))
    debug_printf(DEBUG_NORMAL, "  Allow Type: OTP\n");

  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_WPA_PSK))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: WPA-PSK\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_TLS))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: TLS\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_MD5))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: MD5\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_TTLS))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: TTLS\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_LEAP))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: LEAP\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_MSCV2))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: MSCHAPv2\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_PEAP))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: PEAP\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_SIM))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: SIM\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_AKA))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: AKA\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_GTC))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: GTC\n");
  if (TEST_FLAG(net->flags, CONFIG_NET_PREFER_OTP))
    debug_printf(DEBUG_NORMAL, "  Prefer Type: OTP\n");

  debug_printf(DEBUG_NORMAL, "  SSID: \"%s\"\n", net->ssid);
  debug_printf(DEBUG_NORMAL, "  Identity: \"%s\"\n", net->identity);
  debug_printf(DEBUG_NORMAL, "  Priority: %d\n", net->priority);

  if (TEST_FLAG(net->flags, CONFIG_NET_DEST_MAC))
    debug_printf(DEBUG_NORMAL, "  DEST MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
		 net->dest_mac[0], net->dest_mac[1], net->dest_mac[2],
		 net->dest_mac[3], net->dest_mac[4], net->dest_mac[5]);

  dump_config_eap_method(net->methods, 0);

  if (net->next)
    dump_config_network(net->next);
      
}


  /*****************************/
 /* CONFIG_STRING_LIST       */
/*****************************/
void delete_config_string_list(struct config_string_list **tmp_string_list)
{
  if (*tmp_string_list == NULL)
    return;

  free (*tmp_string_list);
  *tmp_string_list = NULL;
}

void initialize_config_string_list(struct config_string_list **tmp_string_list)
{
  if (*tmp_string_list != NULL) {
    delete_config_string_list(tmp_string_list);
  }
  *tmp_string_list = 
    (struct config_string_list *)malloc(sizeof(struct config_string_list));  
  if (*tmp_string_list)
    memset(*tmp_string_list, 0, sizeof(struct config_string_list));
}

int config_string_list_contains_string(struct config_string_list *net_list,
				     char *netname)
{
  // if there is a list, we need to search for the net
  while (net_list != NULL) {
    if (strcmp(net_list->name, netname) == 0)
      return TRUE;
    net_list = net_list->next;
  }
  return FALSE;
}

void config_string_list_add_string(struct config_string_list **net_list,
				 char *netname)
{
  struct config_string_list **current = net_list;

  while (*current != NULL) {
    if (strcmp((*current)->name, netname) == 0)
      return;
    current = &(*current)->next;
  }
  initialize_config_string_list(current);
  (*current)->name = netname;
}

void dump_config_string_list(struct config_string_list *stringlist, 
			     char *title)
{
  if (!stringlist) {
    return;
  }

  debug_printf(DEBUG_NORMAL, "%s: \"%s\"\n", title,  stringlist->name);
  if (stringlist->next)
    dump_config_string_list(stringlist->next, title);

}

  /*******************/
 /* CONFIG_GLOBALS  */
/*******************/
void delete_config_globals(struct config_globals **tmp_globals)
{
  if (*tmp_globals == NULL)
    return;

  if ((*tmp_globals)->default_net)
    free((*tmp_globals)->default_net);

  if ((*tmp_globals)->allowed_nets)
    delete_config_string_list(&(*tmp_globals)->allowed_nets);

  FREE_STRING((*tmp_globals)->startup_command);
  FREE_STRING((*tmp_globals)->first_auth_command);
  FREE_STRING((*tmp_globals)->reauth_command);
  FREE_STRING((*tmp_globals)->logfile);
  
  if ((*tmp_globals)->allow_interfaces)
    delete_config_string_list(&(*tmp_globals)->allow_interfaces);
  if ((*tmp_globals)->deny_interfaces)
    delete_config_string_list(&(*tmp_globals)->deny_interfaces);
  
  free (*tmp_globals);
  *tmp_globals = NULL;
}

void initialize_config_globals(struct config_globals **tmp_globals)
{
  if (*tmp_globals != NULL) {
    delete_config_globals(tmp_globals);
  }
  *tmp_globals = 
    (struct config_globals *)malloc(sizeof(struct config_globals));  
  if (*tmp_globals)
    memset(*tmp_globals, 0, sizeof(struct config_globals));

  // We want to set allmulti to have a default of 1.
  (*tmp_globals)->allmulti = 1;
}

void dump_config_globals(struct config_globals *globals)
{
  if (!globals) {
    debug_printf(DEBUG_NORMAL, "No Globals\n");
    return;
  }
  debug_printf(DEBUG_NORMAL, "Default Net: \"%s\"\n", globals->default_net);
  if (globals->allowed_nets)
    dump_config_string_list(globals->allowed_nets, "Network Allowed");
  else 
    debug_printf(DEBUG_NORMAL, "Allowed Nets: ALL\n");

  if (globals->startup_command)
    debug_printf(DEBUG_NORMAL,
		 "Startup Command: '%s'\n", globals->startup_command);
  if (globals->first_auth_command)
    debug_printf(DEBUG_NORMAL,
		 "First Auth Command: '%s'\n", globals->first_auth_command);
  if (globals->reauth_command)
    debug_printf(DEBUG_NORMAL,
		 "Re-Auth Command: '%s'\n", globals->reauth_command);
  if (globals->logfile)
    debug_printf(DEBUG_NORMAL,
		 "Logfile: '%s'\n", globals->logfile);

  if (TEST_FLAG(globals->flags, CONFIG_GLOBALS_AUTH_PER))
    debug_printf(DEBUG_NORMAL, "Auth Period: %d\n", globals->auth_period);
  if (TEST_FLAG(globals->flags, CONFIG_GLOBALS_HELD_PER))
    debug_printf(DEBUG_NORMAL, "Held Period: %d\n", globals->held_period);
  if (TEST_FLAG(globals->flags, CONFIG_GLOBALS_MAX_STARTS))
    debug_printf(DEBUG_NORMAL,"Max Starts: %d\n", globals->max_starts);

  if (globals->allmulti == 1)
    debug_printf(DEBUG_NORMAL, "ALLMULTI is enabled!\n");
  else
    debug_printf(DEBUG_NORMAL, "ALLMULTI is disabled!\n");

  if (globals->association == 0)
    debug_printf(DEBUG_NORMAL, "ASSOCIATION is set to AUTO!\n");
  else
    debug_printf(DEBUG_NORMAL, "ASSOCIATION is set to MANUAL!\n");

  if (globals->allow_interfaces)
    dump_config_string_list(globals->allow_interfaces, "Interface Allowed");
  else 
    debug_printf(DEBUG_NORMAL, "Allowed Interfaces: ALL\n");

  if (globals->deny_interfaces)
    dump_config_string_list(globals->deny_interfaces, "Interface Denied");
  else 
    debug_printf(DEBUG_NORMAL, "Denied Interfaces: NONE\n");
}

  /*******************/
 /* CONFIG_DATA     */
/*******************/
void delete_config_data(struct config_data **tmp_data)
{
  if (*tmp_data == NULL)
    return;

  if ((*tmp_data)->config_fname)
    free((*tmp_data)->config_fname);
  if ((*tmp_data)->globals)
    delete_config_globals(&(*tmp_data)->globals);
  if ((*tmp_data)->networks)
    delete_config_network(&(*tmp_data)->networks);
  
  free (*tmp_data);
  *tmp_data = NULL;
}

void initialize_config_data(struct config_data **tmp_data)
{
  if (*tmp_data != NULL) {
    delete_config_data(tmp_data);
  }
  *tmp_data = 
    (struct config_data *)malloc(sizeof(struct config_data));  
  if (*tmp_data)
    memset(*tmp_data, 0, sizeof(struct config_data));
}

void dump_config_data(struct config_data *data)
{
  if (!data)
    return;
  debug_printf(DEBUG_NORMAL, "=-=-=-=-=-=-=-=-=-=-=-=-=\n");
  debug_printf(DEBUG_NORMAL, "Configuration File: %s\n", data->config_fname);
  dump_config_globals(data->globals);
  dump_config_network(data->networks);
  debug_printf(DEBUG_NORMAL, "=-=-=-=-=-=-=-=-=-=-=-=-=\n");
}
