text: /* Editor Settings: expandtabs and use 4 spaces for indentation
   * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
   * -*- mode: c, c-basic-offset: 4 -*- */
  
  /*
   * Copyright © BeyondTrust Software 2004 - 2019
   * All rights reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *        http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * BEYONDTRUST MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING TERMS AS
   * WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT WITH
   * BEYONDTRUST, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE TERMS OF THAT
   * SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE APACHE LICENSE,
   * NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU HAVE QUESTIONS, OR WISH TO REQUEST
   * A COPY OF THE ALTERNATE LICENSING TERMS OFFERED BY BEYONDTRUST, PLEASE CONTACT
   * BEYONDTRUST AT beyondtrust.com/contact
   */
  
  #include "../includes.h"
  
  
  DWORD
  LWSetCapacity(
      DynamicArray *array,
      size_t itemSize,
      size_t capacity
      )
  {
      DWORD dwError = 0;
  
      /* Resize the array */
      dwError = LwReallocMemory(array->data, &array->data, capacity * itemSize);
      BAIL_ON_MAC_ERROR(dwError);
      
      array->capacity = capacity;
      if (array->size > capacity)
      {
          array->size = capacity;
      }
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWArrayInsert(
      DynamicArray *array,
      int insertPos,
      int itemSize,
      const void *data,
      size_t dataLen
      )
  {
      DWORD dwError = 0;
  
      if (array->size + dataLen > array->capacity)
      {
          /* Resize the array */
          dwError = LWSetCapacity(array, itemSize, array->capacity + dataLen + array->capacity);
          BAIL_ON_MAC_ERROR(dwError);
      }
      
      /* Make room for the new value */
      memmove((char *)array->data + (insertPos + dataLen)*itemSize,
              (char *)array->data + insertPos*itemSize,
              (array->size - insertPos)*itemSize);
      
      memcpy((char *)array->data + insertPos*itemSize, data, dataLen*itemSize);
      
      array->size += dataLen;
      
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWArrayAppend(
      DynamicArray* array,
      int           itemSize,
      const void *  data,
      size_t        dataLen
      )
  {
      return LWArrayInsert(
                  array,
                  array->size,
                  itemSize,
                  data,
                  dataLen);
  }
  
  static
  void
  LWFreeNVPair(
      PNVPAIR pNVPair
      )
  {
      LW_SAFE_FREE_STRING(pNVPair->pszName);
      LW_SAFE_FREE_STRING(pNVPair->pszValue);
  
      LwFreeMemory(pNVPair);
  }
  
  static
  void
  LWFreeNVPairList(
      PNVPAIR pNVPairList
      )
  {
      PNVPAIR pNVPair = NULL;
  
      while (pNVPairList) {
          pNVPair = pNVPairList;
          pNVPairList = pNVPairList->pNext;
          LWFreeNVPair(pNVPair);
      }
  }
  
  static
  void
  LWFreeSection(
      PCFGSECTION pSection
      )
  {
      LW_SAFE_FREE_STRING(pSection->pszName);
  
      if (pSection->pNVPairList)
          LWFreeNVPairList(pSection->pNVPairList);
      LwFreeMemory(pSection);
  }
  
  void
  LWFreeConfigSectionList(
      PCFGSECTION pSectionList
      )
  {
      PCFGSECTION pSection = NULL;
      while (pSectionList) {
          pSection = pSectionList;
          pSectionList = pSectionList->pNext;
          LWFreeSection(pSection);
      }
  }
  
  static
  VOID
  ArrayFree(
      DynamicArray* array
      )
  {
      if (array->data)
      {
          LwFreeMemory(array->data);
      }
      
      array->size = 0;
      array->capacity = 0;
  }
  
  static
  DWORD
  ReadNextLine(
      FILE* fp,
      PSTR *output,
      PBOOLEAN pbEndOfFile
      )
  {
      DWORD dwError = 0;
      DynamicArray buffer;
      const char nullTerm = '\0';
  
      *pbEndOfFile = 0;
      *output = NULL;
      memset(&buffer, 0, sizeof(buffer));
      dwError = LWSetCapacity(&buffer, 1, 100);
      BAIL_ON_MAC_ERROR(dwError);
  
      while(1)
      {
          if(fgets((char*)buffer.data + buffer.size,
                  buffer.capacity - buffer.size, fp) ==  NULL)
          {
              if (feof(fp)) {
                  *pbEndOfFile = 1;
              } else {
                  dwError = errno;
                  BAIL_ON_MAC_ERROR(dwError);
              }
          }
          buffer.size += strlen((char*)buffer.data + buffer.size);
  
          if(*pbEndOfFile)
              break;
          if(buffer.size == buffer.capacity - 1 &&
                  ((char *)buffer.data)[buffer.size - 1] != '\n')
          {
              dwError = LWSetCapacity(&buffer, 1, buffer.capacity * 2);
              BAIL_ON_MAC_ERROR(dwError);
          }
          else
              break;
      }
  
      dwError = LWArrayAppend(&buffer, 1, &nullTerm, 1);
      BAIL_ON_MAC_ERROR(dwError);
      
      *output = (PSTR)buffer.data;
      buffer.data = NULL;
  
  cleanup:
  
      ArrayFree(&buffer);
      
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  static
  PNVPAIR
  ReverseNVPairList(
      PNVPAIR pNVPairList
      )
  {
      PNVPAIR pP = NULL;
      PNVPAIR pQ = pNVPairList;
      PNVPAIR pR = NULL;
  
      while (pQ) {
          pR = pQ->pNext;
          pQ->pNext = pP;
          pP = pQ;
          pQ = pR;
      }
  
      return pP;
  }
  
  static
  PCFGSECTION
  ReverseSectionsAndNVPairs(
      PCFGSECTION pSectionList
      )
  {
      PCFGSECTION pP = NULL;
      PCFGSECTION pQ = pSectionList;
      PCFGSECTION pR = NULL;
  
      while (pQ) {
  
          if (pQ->pNVPairList) {
              pQ->pNVPairList = ReverseNVPairList(pQ->pNVPairList);
          }
  
          pR = pQ->pNext;
          pQ->pNext = pP;
          pP = pQ;
          pQ = pR;
      }
  
      return pP;
  }
  
  static
  DWORD
  ParseHeader(
      FILE* fp,
      PDWORD pdwSignature)
  {
      DWORD dwError = 0;
      uint16_t wSignature = 0;
  
      if (1 != fread(&wSignature, sizeof(wSignature), 1, fp)) {
          dwError = errno;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      *pdwSignature = CONVERT_ENDIAN_WORD(wSignature);
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  
  static
  DWORD
  ReadNextDoubleByteLine(
      FILE* fp,
      PSTR pszBuf,
      DWORD dwMaxLen,
      PBOOLEAN pbEndOfFile
      )
  {
      DWORD dwError = 0;
      DBLBYTE dbBuf;
      PSTR pszTmp = pszBuf;
      DWORD iBuf = 0;
  
      *pbEndOfFile = 0;
  
      while ((iBuf < dwMaxLen)) {
  
          if (1 != fread(&dbBuf, sizeof(dbBuf), 1, fp)) {
  
              if (feof(fp)) {
  
                  *pbEndOfFile = 1;
                  break;
  
              } else {
  
                  dwError = errno;
                  BAIL_ON_MAC_ERROR(dwError);
  
              }
  
          }
  
          if ((*pszTmp++ = dbBuf.b1) == '\n')
              break;
  
          iBuf++;
      }
  
      if (iBuf == dwMaxLen) {
          dwError = MAC_AD_ERROR_INSUFFICIENT_BUFFER;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      *pszTmp = '\0';
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWParseConfigFile(
      PCSTR pszFilePath,
      PCFGSECTION* ppCfgSectionList,
      BOOLEAN bWindowsDoubleByteFormat
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSectionList = NULL;
      PCFGSECTION pSection = NULL;
      PNVPAIR pNVPair = NULL;
      FILE* fp = NULL;
      CHAR staticBuffer[1024+1];
      PSTR szBuf = NULL;
      DWORD dwLen = 0;
      PSTR pszTmp = NULL;
      PSTR pszName = NULL;
      PSTR pszValue = NULL;
      DWORD dwSignature = 0;
      /*DWORD nRead = 0;*/
      BOOLEAN bEOF = FALSE;
  
      if ((fp = fopen(pszFilePath, "r")) == NULL) {
          dwError = errno;
          goto error;
      }
  
      if (fcntl(fileno(fp), F_SETFD, FD_CLOEXEC) < 0) {
          dwError = errno;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      if (bWindowsDoubleByteFormat) {
          dwError = ParseHeader(fp, &dwSignature);
          BAIL_ON_MAC_ERROR(dwError);
  
          if (dwSignature != 0xFEFF) {
              dwError = MAC_AD_ERROR_INVALID_TAG;
              BAIL_ON_MAC_ERROR(dwError);
          }
      }
  
      while (!bEOF) {
          LW_SAFE_FREE_STRING(szBuf);
  
          if (bWindowsDoubleByteFormat) {
  
              staticBuffer[0] = '\0';
              dwError = ReadNextDoubleByteLine(fp, staticBuffer, 1024, &bEOF);
              BAIL_ON_MAC_ERROR(dwError);
              dwError = LwAllocateString(staticBuffer, &szBuf);
              BAIL_ON_MAC_ERROR(dwError);
  
          } else {
  
              dwError = ReadNextLine(fp, &szBuf, &bEOF);
              BAIL_ON_MAC_ERROR(dwError);
  
          }
  
          LwStripWhitespace(szBuf, TRUE, TRUE);
  
          if (!(dwLen=strlen(szBuf)))
              continue;
  
          /* Skip comments for now */
          if (szBuf[0] == '#' ||
              szBuf[0] == ';')
              continue;
  
          if (szBuf[0]       == '[' &&
              szBuf[dwLen-1] == ']') {
  
              if (pSection) {
                  pSection->pNext = pSectionList;
                  pSectionList = pSection;
                  pSection = NULL;
              }
  
              dwError = LwAllocateMemory(sizeof(CFGSECTION), (PVOID*)&pSection);
              BAIL_ON_MAC_ERROR(dwError);
  
              szBuf[dwLen-1] = '\0';
  
              dwError = LwAllocateString(szBuf+1, &pSection->pszName);
              BAIL_ON_MAC_ERROR(dwError);
  
              LwStripWhitespace(pSection->pszName, TRUE, TRUE);
  
          } else {
  
              if (!pSection) {
                  dwError = MAC_AD_ERROR_NO_SUCH_ATTRIBUTE;
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
              if ((pszTmp = strchr(szBuf, '=')) == NULL) {
                  continue;
              }
  
              if (pszTmp == szBuf) {
                  dwError = MAC_AD_ERROR_INVALID_TAG;
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
              dwError = LwAllocateMemory(pszTmp-szBuf+1, (PVOID*)&pszName);
              BAIL_ON_MAC_ERROR(dwError);
  
              strncpy(pszName, szBuf, pszTmp-szBuf);
  
              pszTmp++;
              while (*pszTmp != '\0' && isspace((int)*pszTmp))
                  pszTmp++;
  
              if (*pszTmp != '\0') {
                  dwError = LwAllocateString(pszTmp, &pszValue);
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
              dwError = LwAllocateMemory(sizeof(NVPAIR), (PVOID*)&pNVPair);
              BAIL_ON_MAC_ERROR(dwError);
  
              LwStripWhitespace(pszName, TRUE, TRUE);
              LwStripWhitespace(pszValue, TRUE, TRUE);
  
              pNVPair->pszName = pszName; pszName = NULL;
              pNVPair->pszValue = pszValue; pszValue = NULL;
  
              pNVPair->pNext = pSection->pNVPairList;
              pSection->pNVPairList = pNVPair;
              pNVPair = NULL;
  
          }
      }
  
      if (pSection) {
          pSection->pNext = pSectionList;
          pSectionList = pSection;
          pSection = NULL;
      }
  
      pSectionList = ReverseSectionsAndNVPairs(pSectionList);
  
      *ppCfgSectionList = pSectionList;
  
      fclose(fp); fp = NULL;
  
  cleanup:
  
      LW_SAFE_FREE_STRING(szBuf);
  
      if (fp) {
          fclose(fp);
      }
  
      LW_SAFE_FREE_STRING(pszName);
      LW_SAFE_FREE_STRING(pszValue);
  
      return dwError;
      
  error:
  
      *ppCfgSectionList = NULL;
  
      if (pSectionList)
      {
         LWFreeConfigSectionList(pSectionList);
      }
  
      if (pSection)
      {
          LWFreeSection(pSection);
      }
  
      if (pNVPair)
      {
          LWFreeNVPair(pNVPair);
      }
  
      goto cleanup;
  }
  
  DWORD
  LWSaveConfigSectionListToFile(
      FILE* fp,
      PCFGSECTION pSectionList
      )
  {
      DWORD dwError = 0;
      PNVPAIR pNVPair = NULL;
  
      while (pSectionList) {
  
          if (IsNullOrEmptyString(pSectionList->pszName)) {
              dwError = MAC_AD_ERROR_INVALID_RECORD_TYPE;
              goto error;
          }
  
          if (fprintf(fp, "[%s]\r\n", pSectionList->pszName) < 0) {
              dwError = errno;
              BAIL_ON_MAC_ERROR(dwError);
          }
  
          pNVPair = pSectionList->pNVPairList;
          while (pNVPair) {
  
              if (IsNullOrEmptyString(pNVPair->pszName)) {
                  dwError = MAC_AD_ERROR_INVALID_ATTRIBUTE_TYPE;
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
              if (fprintf(fp, "    %s = %s\r\n",
                          pNVPair->pszName,
                          (IsNullOrEmptyString(pNVPair->pszValue) ? "" : pNVPair->pszValue)) < 0) {
                  dwError = errno;
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
              pNVPair = pNVPair->pNext;
          }
  
          pSectionList = pSectionList->pNext;
  
      }
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWSaveConfigSectionList(
      PCSTR pszConfigFilePath,
      PCFGSECTION pSectionList
      )
  {
      DWORD dwError = 0;
      /*PNVPAIR pNVPair = NULL;*/
      FILE* fp = NULL;
      PSTR pszTmpPath = NULL;
      BOOLEAN bRemoveFile = FALSE;
  
      dwError = LwAllocateMemory(strlen(pszConfigFilePath)+strlen(".macadutil")+1,
                                 (PVOID*)&pszTmpPath);
      BAIL_ON_MAC_ERROR(dwError);
  
      sprintf(pszTmpPath, "%s.macadutil", pszConfigFilePath);
  
      if ((fp = fopen(pszTmpPath, "w")) == NULL) {
          dwError = errno;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      if (fcntl(fileno(fp), F_SETFD, FD_CLOEXEC) < 0) {
          dwError = errno;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      bRemoveFile = TRUE;
  
      dwError = LWSaveConfigSectionListToFile(fp, pSectionList);
      BAIL_ON_MAC_ERROR(dwError);
  
      fclose(fp); fp = NULL;
  
      dwError = LwMoveFile(pszTmpPath, pszConfigFilePath);
      BAIL_ON_MAC_ERROR(dwError);
      
      bRemoveFile = FALSE;
  
  cleanup:
  
      if (bRemoveFile)
      {
          LwRemoveFile(pszTmpPath);
      }
  
      if (fp)
      {
          fclose(fp);
      }
  
      LW_SAFE_FREE_STRING(pszTmpPath);
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWCreateConfigSection(
      PCFGSECTION* ppSectionList,
      PCFGSECTION* pCreatedSection,
      PCSTR pszSectionName
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSectionList = NULL;
      PCFGSECTION pSection = NULL;
      PCFGSECTION pTmpSection = NULL;
  
      if (!ppSectionList || IsNullOrEmptyString(pszSectionName)) {
          dwError = MAC_AD_ERROR_INVALID_PARAMETER;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      pSectionList = *ppSectionList;
  
      pSection = pSectionList;
      while (pSection)
      {
          if (!strcmp(pSection->pszName, pszSectionName))
          {
              break;
          }
      }
  
      if (!pSection) {
          dwError = LwAllocateMemory(sizeof(CFGSECTION), (PVOID*)&pSection);
          BAIL_ON_MAC_ERROR(dwError);
  
          dwError = LwAllocateString(pszSectionName, &pSection->pszName);
          BAIL_ON_MAC_ERROR(dwError);
  
          if (!pSectionList) {
  
              pSectionList = pSection;
  
          } else {
  
              pTmpSection = pSectionList;
              while (pTmpSection->pNext)
                  pTmpSection = pTmpSection->pNext;
  
              pTmpSection->pNext = pSection;
  
          }
      }
  
      *pCreatedSection = pSection;
      *ppSectionList = pSectionList;
  
  cleanup:
  
      return dwError;
      
  error:
  
      *pCreatedSection = NULL;
  
      if (pSection)
      {
          LWFreeSection(pSection);
      }
      
      goto cleanup;
  }
  
  DWORD
  LWGetConfigValueBySection(
      PCFGSECTION pSection,
      PCSTR pszName,
      PSTR* ppszValue
      )
  {
      DWORD dwError = 0;
      PNVPAIR pNVPair = NULL;
      
      *ppszValue = NULL;
  
      pNVPair = pSection->pNVPairList;
      while (pNVPair) {
          if (!strcmp(pNVPair->pszName, pszName))
              break;
  
          pNVPair = pNVPair->pNext;
      }
  
      if (!pNVPair) {
          dwError = MAC_AD_ERROR_INVALID_ATTRIBUTE_TYPE;
          goto cleanup; /* Not uncommon, no need to log error. */
      }
  
      if (pNVPair->pszValue) {
  
          dwError = LwAllocateString(pNVPair->pszValue, ppszValue);
          BAIL_ON_MAC_ERROR(dwError);
  
      }
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWGetConfigValueBySectionName(
      PCFGSECTION pSectionList,
      PCSTR pszSectionName,
      PCSTR pszName,
      PSTR* ppszValue
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSection = NULL;
  
      while (pSectionList) {
          if (!strcmp(pSectionList->pszName, pszSectionName)) {
              pSection = pSectionList;
              break;
          }
          pSectionList = pSectionList->pNext;
      }
  
      if (!pSection) {
          dwError = MAC_AD_ERROR_INVALID_RECORD_TYPE;
          goto error;
      }
  
      dwError = LWGetConfigValueBySection(pSection,
                                          pszName,
                                          ppszValue);
      goto cleanup; /* Not uncommon, no need to log error. */
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWSetConfigValueBySection(
      PCFGSECTION pSection,
      PCSTR pszName,
      PCSTR pszValue
      )
  {
      DWORD dwError = 0;
      PNVPAIR pNVPair = NULL;
      PNVPAIR pTmpNVPair = NULL;
  
      pTmpNVPair = pSection->pNVPairList;
      while (pTmpNVPair) {
          if (!strcmp(pTmpNVPair->pszName, pszName)) {
              pNVPair = pTmpNVPair;
              break;
          }
          pTmpNVPair = pTmpNVPair->pNext;
      }
  
      if (pNVPair == NULL) {
  
          dwError = LwAllocateMemory(sizeof(NVPAIR), (PVOID*)&pNVPair);
          BAIL_ON_MAC_ERROR(dwError);
  
          dwError = LwAllocateString(pszName, &pNVPair->pszName);
          BAIL_ON_MAC_ERROR(dwError);
  
          LwStripWhitespace(pNVPair->pszName, TRUE, TRUE);
  
          if (!IsNullOrEmptyString(pszValue)) {
              dwError = LwAllocateString(pszValue, &pNVPair->pszValue);
              BAIL_ON_MAC_ERROR(dwError);
          }
  
          if (!pSection->pNVPairList) {
              pSection->pNVPairList = pNVPair;
              pNVPair = NULL;
  
          } else {
  
              pTmpNVPair = pSection->pNVPairList;
              while (pTmpNVPair->pNext != NULL)
                  pTmpNVPair = pTmpNVPair->pNext;
  
              pTmpNVPair->pNext = pNVPair;
              pNVPair = NULL;
  
          }
  
      } else {
  
          if (pNVPair->pszValue) {
  
              LW_SAFE_FREE_STRING(pNVPair->pszValue);
              pNVPair->pszValue = NULL;
  
              if (!IsNullOrEmptyString(pszValue)) {
                  dwError = LwAllocateString(pszValue, &pNVPair->pszValue);
                  BAIL_ON_MAC_ERROR(dwError);
              }
  
          }
  
      }
  
  cleanup:
  
      return dwError;
      
  error:
  
      if (pNVPair) {
          LWFreeNVPair(pNVPair);
      }
  
      goto cleanup;
  }
  
  DWORD
  LWSetConfigValueBySectionName(
      PCFGSECTION pSectionList,
      PCSTR pszSectionName,
      PCSTR pszName,
      PCSTR pszValue
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSection = NULL;
  
      if (IsNullOrEmptyString(pszSectionName) ||
          IsNullOrEmptyString(pszName)) {
          dwError = MAC_AD_ERROR_INVALID_PARAMETER;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      while (pSectionList) {
          if (!strcmp(pSectionList->pszName, pszSectionName)) {
              pSection = pSectionList;
              break;
          }
          pSectionList = pSectionList->pNext;
      }
  
      if (!pSection) {
          dwError = MAC_AD_ERROR_INVALID_RECORD_TYPE;
          goto error;
      }
  
      dwError = LWSetConfigValueBySection(pSection,
                                          pszName,
                                          pszValue);
      BAIL_ON_MAC_ERROR(dwError);
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWDeleteConfigSection(
      PCFGSECTION* ppSectionList,
      PCSTR pszSectionName
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSection = NULL;
      PCFGSECTION pIterSection = *ppSectionList;
      PCFGSECTION pPrevSection = NULL;
  
      if (IsNullOrEmptyString(pszSectionName)) {
          dwError = MAC_AD_ERROR_INVALID_PARAMETER;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      while (pIterSection) {
          if (!strcmp(pszSectionName, pIterSection->pszName)) {
              pSection = pIterSection;
              break;
          }
          pPrevSection = pIterSection;
          pIterSection = pIterSection->pNext;
      }
  
      if (pSection) {
  
          if (!pPrevSection) {
  
              *ppSectionList = pSection->pNext;
  
          } else {
  
              pPrevSection->pNext = pSection->pNext;
  
          }
  
          LWFreeSection(pSection);
      }
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  DWORD
  LWDeleteNameValuePairBySection(
      PCFGSECTION pSection,
      PCSTR pszName
      )
  {
      DWORD dwError = 0;
      PNVPAIR pNVPair = NULL;
      PNVPAIR pPrevNVPair = NULL;
  
      pNVPair = pSection->pNVPairList;
      while (pNVPair) {
          if (!strcmp(pNVPair->pszName, pszName)) {
              break;
          }
          pPrevNVPair = pNVPair;
          pNVPair = pNVPair->pNext;
      }
  
      if (pNVPair != NULL) {
  
          if (pPrevNVPair) {
              pPrevNVPair->pNext = pNVPair->pNext;
          } else {
              pSection->pNVPairList = pNVPair->pNext;
          }
  
          LWFreeNVPair(pNVPair);
      }
  
      return dwError;
  }
  
  DWORD
  LWDeleteNameValuePairBySectionName(
      PCFGSECTION pSectionList,
      PCSTR pszSectionName,
      PCSTR pszName
      )
  {
      DWORD dwError = 0;
      PCFGSECTION pSection = NULL;
  
      if (IsNullOrEmptyString(pszSectionName) ||
          IsNullOrEmptyString(pszName)) {
          dwError = MAC_AD_ERROR_INVALID_PARAMETER;
          BAIL_ON_MAC_ERROR(dwError);
      }
  
      while (pSectionList) {
          if (!strcmp(pszSectionName, pszName)) {
              pSection = pSectionList;
              break;
          }
          pSectionList = pSectionList->pNext;
      }
  
      if (!pSection) {
          dwError = MAC_AD_ERROR_INVALID_RECORD_TYPE;
          goto error;
      }
  
      dwError = LWDeleteNameValuePairBySection(pSection, pszName);
      BAIL_ON_MAC_ERROR(dwError);
  
  cleanup:
  
      return dwError;
      
  error:
  
      goto cleanup;
  }
  
  