/***********************************************************************/
/*  MACUTIL: Author Olivier Bertrand --  2008-2012                     */
/*  From the article and sample code by Khalid Shaikh.                 */
/***********************************************************************/
#if defined(_WIN32)
#include "my_global.h"
#else   // !_WIN32
#error This is WINDOWS only DLL
#endif  // !_WIN32
#include "global.h"
#include "plgdbsem.h"
#include "macutil.h"

#if 0   // This is placed here just to know what are the actual values
#define MAX_ADAPTER_DESCRIPTION_LENGTH  128
#define MAX_ADAPTER_NAME_LENGTH         256
#define MAX_ADAPTER_ADDRESS_LENGTH      8
#define DEFAULT_MINIMUM_ENTITIES        32
#define MAX_HOSTNAME_LEN                128
#define MAX_DOMAIN_NAME_LEN             128
#define MAX_SCOPE_ID_LEN                256

#define BROADCAST_NODETYPE              1
#define PEER_TO_PEER_NODETYPE           2
#define MIXED_NODETYPE                  4
#define HYBRID_NODETYPE                 8

#define IP_ADAPTER_DDNS_ENABLED               0x01
#define IP_ADAPTER_REGISTER_ADAPTER_SUFFIX    0x02
#define IP_ADAPTER_DHCP_ENABLED               0x04
#define IP_ADAPTER_RECEIVE_ONLY               0x08
#define IP_ADAPTER_NO_MULTICAST               0x10
#define IP_ADAPTER_IPV6_OTHER_STATEFUL_CONFIG 0x20
#endif // 0

/***********************************************************************/
/*  Implementation of the MACINFO class.                               */
/***********************************************************************/
MACINFO::MACINFO(bool adap, bool fix)
  {
  Fip = NULL;
  Piaf = NULL;
  Curp = NULL;
  Buflen = 0;
  Fix = fix;
  Adap = adap;
  N = -1;
  } // end of MACINFO constructor

/***********************************************************************/
/*  MACINFO: Return an error message.                                  */
/***********************************************************************/
void MACINFO::MakeErrorMsg(PGLOBAL g, DWORD drc)
  {
  if (drc == ERROR_BUFFER_OVERFLOW)
    snprintf(g->Message, sizeof(g->Message),
      "GetAdaptersInfo: Buffer Overflow buflen=%d nbofadap=%d",
      Buflen, N);
  else if (drc == ERROR_INVALID_PARAMETER)
    snprintf(g->Message, sizeof(g->Message), "GetAdaptersInfo: Invalid parameters");
  else if (drc == ERROR_NO_DATA)
    snprintf(g->Message, sizeof(g->Message), "No adapter information exists for the local computer");
  else if (drc == ERROR_NOT_SUPPORTED)
    snprintf(g->Message, sizeof(g->Message), "GetAdaptersInfo is not supported");
  else
    FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                  FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
                  0, g->Message, sizeof(g->Message), NULL);

  } // end of MakeErrorMsg

/***********************************************************************/
/*  MAC: Get the number of found adapters.                             */
/***********************************************************************/
int MACINFO::GetNadap(PGLOBAL g)
  {
  if (N < 0) {
    // Best method
    if (Adap) {
      DWORD drc = GetAdaptersInfo(NULL, &Buflen);

      if (drc == ERROR_SUCCESS)
        N = (Fix) ? 1 : 0;
      else if (drc == ERROR_BUFFER_OVERFLOW)
        N = Buflen / sizeof(IP_ADAPTER_INFO);
      else
        MakeErrorMsg(g, drc);

    } else
      N = (Fix) ? 1 : 0;

#if 0
    // This method returns too many adapters
    DWORD dw, drc = GetNumberOfInterfaces((PDWORD)&dw);

    if (drc == NO_ERROR) {
      N = (int)dw;
      Buflen = N * sizeof(IP_ADAPTER_INFO);
    } else
      MakeErrorMsg(g, 0);
#endif
    } // endif MaxSize

  return N;
  } // end of GetNadap

/***********************************************************************/
/*  GetMacInfo: Get info for all found adapters.                       */
/***********************************************************************/
bool MACINFO::GetMacInfo(PGLOBAL g)
  {
  DWORD drc;

  if (GetNadap(g) < 0)
    return true;
  else if (N == 0)
    return false;

  Piaf = (PIP_ADAPTER_INFO)PlugSubAlloc(g, NULL, Buflen);
  drc = GetAdaptersInfo(Piaf, &Buflen);

  if (drc == ERROR_SUCCESS) {
    Curp = Piaf;               // Curp is the first one
    return false;              // Success
    } // endif drc

  MakeErrorMsg(g, drc);
  return true;
  } // end of GetMacInfo

/***********************************************************************/
/*  GetMacInfo: Get info for all found adapters.                       */
/***********************************************************************/
bool MACINFO::GetFixedInfo(PGLOBAL g)
  {
  ULONG len = (uint)sizeof(FIXED_INFO);
  DWORD drc;

  Fip = (FIXED_INFO*)PlugSubAlloc(g, NULL, len);
  drc = GetNetworkParams(Fip, &len);

  if (drc == ERROR_BUFFER_OVERFLOW) {
    Fip = (FIXED_INFO*)PlugSubAlloc(g, NULL, len);
    drc = GetNetworkParams(Fip, &len);
    } // endif drc

  if (drc != ERROR_SUCCESS) {
    snprintf(g->Message, sizeof(g->Message), "GetNetworkParams failed. Rc=%08x\n", drc);
    return true;
    } // endif drc

  return false;
  } // end of GetFip

#if 0
#define IF_OTHER_ADAPTERTYPE 0
#define IF_ETHERNET_ADAPTERTYPE 1
#define IF_TOKEN_RING_ADAPTERTYPE 2
#define IF_FDDI_ADAPTERTYPE 3
#define IF_PPP_ADAPTERTYPE 4
#define IF_LOOPBACK_ADAPTERTYPE 5
#endif // 0

/***********************************************************************/
/*  Get next MAC info.                                                 */
/***********************************************************************/
bool MACINFO::NextMac(void)
  {
  if (Curp)
    Curp = Curp->Next;

  return Curp != NULL;
  } // end of NextMac

/***********************************************************************/
/*  Get the next MAC address elements.                                 */
/***********************************************************************/
bool MACINFO::GetOneInfo(PGLOBAL g, int flag, void *v, int lv)
  {
  char        *p = NULL, buf[260] = "";
  unsigned int i;
  int         n = 0;

  if (!Curp && flag >= 10) {
    // Fix info row, no adapter info available
    switch (flag) {
      case 13:
      case 14:
      case 19:
      case 22:
      case 23:
        break;
      default:
        p = PlugDup(g, "");
      } // endswitch flag

  } else switch (flag) {
    // FIXED INFO
    case 1:                     // Host Name
      p = Fip->HostName;
      break;
    case 2:                     // Domain Name
      p = Fip->DomainName;
      break;
    case 3:                     // DNS IPaddress
      p = (Fip->CurrentDnsServer)
        ? (char*)&Fip->CurrentDnsServer->IpAddress
        : (char*)&Fip->DnsServerList.IpAddress;
      break;
    case 4:                     // Node Type
      n = (int)Fip->NodeType;
      break;
    case 5:                     // Scope ID ???
      p = Fip->ScopeId;
      break;
    case 6:                     // Routing enabled
      n = (int)Fip->EnableRouting;
      break;
    case 7:                     // Proxy enabled
      n = (int)Fip->EnableProxy;
      break;
    case 8:                     // DNS enabled
      n = (int)Fip->EnableDns;
      break;
    // ADAPTERS INFO
    case 10:                    // Name
      p = Curp->AdapterName;
      break;
    case 11:                    // Description
      if ((p = strstr(Curp->Description, " - Packet Scheduler Miniport"))) {
        strncpy(buf, Curp->Description, p - Curp->Description);
        i = (int)(p - Curp->Description);
        strncpy(buf, Curp->Description, i);
        buf[i] = 0;
        p = buf;
      } else if ((p = strstr(Curp->Description,
                  " - Miniport d'ordonnancement de paquets"))) {
        i = (int)(p - Curp->Description);
        strncpy(buf, Curp->Description, i);
        buf[i] = 0;
        p = buf;
      } else
        p = Curp->Description;

      break;
    case 12:                    // MAC Address
      for (p = buf, i = 0; i < Curp->AddressLength; i++) {
        if (i)
          strcat(p++, "-");

        p += sprintf(p, "%.2X", Curp->Address[i]);
        } // endfor i

      p = buf;
      break;
    case 13:                    // Type
#if 0                       // This is not found in the SDK
      switch (Curp->Type) {
        case IF_ETHERNET_ADAPTERTYPE:   p = "Ethernet Adapter";     break;
        case IF_TOKEN_RING_ADAPTERTYPE: p = "Token Ring Adapter";   break;
        case IF_FDDI_ADAPTERTYPE:       p = "FDDI Adapter";         break;
        case IF_PPP_ADAPTERTYPE:        p = "PPP Adapter";          break;
        case IF_LOOPBACK_ADAPTERTYPE:   p = "Loop Back Adapter";    break;
//      case IF_SLIP_ADAPTERTYPE:       p = "Generic Slip Adapter"; break;
        default:
          sprintf(buf, "Other Adapter, type=%d", Curp->Type);
          p = buf;
        } // endswitch Type
#endif // 0
      n = (int)Curp->Type;
      break;
    case 14:                    // DHCP enabled
      n = (int)Curp->DhcpEnabled;
      break;
    case 15:                    // IP Address
      p = (Curp->CurrentIpAddress)
        ? (char*)&Curp->CurrentIpAddress->IpAddress
        : (char*)&Curp->IpAddressList.IpAddress;
      break;
    case 16:                    // Subnet Mask
      p = (Curp->CurrentIpAddress)
        ? (char*)&Curp->CurrentIpAddress->IpMask
        : (char*)&Curp->IpAddressList.IpMask;
      break;
    case 17:                    // Gateway
      p = (char*)&Curp->GatewayList.IpAddress;
      break;
    case 18:                    // DHCP Server
      p = (char*)&Curp->DhcpServer.IpAddress;
      break;
    case 19:                    // Have WINS
      n = (Curp->HaveWins) ? 1 : 0;
      break;
    case 20:                    // Primary WINS
      p = (char*)&Curp->PrimaryWinsServer.IpAddress;
      break;
    case 21:                    // Secondary WINS
      p = (char*)&Curp->SecondaryWinsServer.IpAddress;
      break;
    case 22:                    // Lease obtained
      n = (int)Curp->LeaseObtained;
      break;
    case 23:                    // Lease expires
      n = (int)Curp->LeaseExpires;
      break;
    default:
      snprintf(g->Message, sizeof(g->Message), "Invalid flag value %d", flag);
      return true;
    } // endswitch flag

  if (p)
    strncpy((char*)v, p, lv);
  else
    *((int*)v) = n;

  return false;
  } // end of GetOneInfo