/*************************************************************************

  Copyright (c) 1995, 1996 Novell, Inc.  All Rights Reserved.

  With respect to this file, Novell hereby grants to Developer a 
  royalty-free, non-exclusive license to include this sample code 
  and derivative binaries in its product. Novell grants to Developer 
  worldwide distribution rights to market, distribute or sell this 
  sample code file and derivative binaries as a component of 
  Developer's product(s).  Novell shall have no obligations to 
  Developer or Developer's customers with respect to this code.
  
  DISCLAIMER:
  
  Novell disclaims and excludes any and all express, implied, and 
  statutory warranties, including, without limitation, warranties 
  of good title, warranties against infringement, and the implied 
  warranties of merchantibility and fitness for a particular purpose.  
  Novell does not warrant that the software will satisfy customer's 
  requirements or that the licensed works are without defect or error 
  or that the operation of the software will be uninterrupted.  
  Novell makes no warranties respecting any technical services or 
  support tools provided under the agreement, and disclaims all other 
  warranties, including the implied warranties of merchantability and 
  fitness for a particular purpose. */

/**************************************************************************
   DSINSYNC.C
***************************************************************************

  Reads the NAMESDB.DBT file and syncs the information to a Directory 
  Services tree database by simply overwriting existing attribute
  values. Although it is not necessary to read the current
  NDS attributes and values to accomplish this task, this process
  is shown to demonstrate these steps. 
     
  The tag DS_OVERWRITE_VALUE is used to modify the Directory Services
  Database.  This name may be somewhat misleading; the value is
  overwritten only if the two values are identical.  If the new value
  is unique, it is appended to the list of attribute values already
  in the Directory Services database.

**************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ntypes.h>
#include <nwnet.h>
#include <nwcalls.h>
#include <nwlocale.h>
#include "dsinsync.h"

/* Prototypes */
void SyncFlatDBToDS(void);
void ReadAttributes(pnstr8 objectName);
void GetAttributeValues(pBuf_T buf);

/* Globals */
FILE                *stream;
DSRECORD            namesDS;
NWDSContextHandle   dContext;

char *attrList[] = {"Full Name",
                    "CN",
                    "Surname",
                    "Telephone Number"};

/****************************************************************************
**   Function: main()
**   Description: This function handles the DS intialization.
*/

int main(void)
{
   NWDSCCODE   cCode;
   LCONV       lconvInfo;   
   nstr8       strUserName[NW_MAX_USER_NAME_LEN];
   nstr8       strUserPassword[50];
   nbool8      bbDoLogout = N_FALSE;

   cCode = NWCallsInit(NULL, NULL);
   if(cCode)
   {
      printf("\nNWCallsInit returned %04X", cCode);
      exit(1);
   }

   NWLlocaleconv(&lconvInfo);

   cCode = NWInitUnicodeTables(lconvInfo.country_id, lconvInfo.code_page);
   if(cCode)
   {
      printf("NWInitUnicodeTables() returned: %04X\n", cCode);
      goto _FreeUnicodeTables;
   }

   /* Create a context and authenticate to NDS if necessary */
   cCode = NWDSCreateContextHandle(&dContext);
   if(cCode)
   {
      printf("\nNWDSCreateContextHandle returned: %04lX\n", cCode);
      goto _FreeUnicodeTables;
   }

   /*  Must authenticate if not already authenticated to NDS
       (which will always be the case if this example is 
       compiled and run as an NLM). */

   if(!NWIsDSAuthenticated())
   {
      printf("\nMust authenticate to NDS");
      printf("\nEnter User Name: ");
      gets(strUserName);
      printf("Enter User Password: ");
      gets(strUserPassword);

      cCode = NWDSLogin(dContext, 0, strUserName, strUserPassword, 0);
      if(cCode)
      {
         printf("\nNWDSLogin returned %X", cCode);
         goto _FreeContext;
      }
      else
      {  /* If example logs in, it will also log out */
         bbDoLogout = N_TRUE;
      }
   }

   printf("\nDS synchronizing with flat database. Please Wait...");

   SyncFlatDBToDS();

   if(bbDoLogout == N_TRUE)
   {
      cCode = NWDSLogout(dContext);
      if(cCode)
      {
         printf("\nNWDSLogout returned %X", cCode);
      }
   }

_FreeContext:
   cCode = NWDSFreeContext(dContext);
   if(cCode)
      printf("\nNWDSFreeContext returned %04X", cCode);
   
_FreeUnicodeTables:
   NWFreeUnicodeTables();

   return(0);
}

/****************************************************************************
** Function: void SyncFlatDBToDS(void)
**
** This function prepares the input buffer to modify an existing object's
** attributes - and then modifies the attributes. 
** In order to derive this information, each record is read from the flat
** database file and copied to a DBRECORD and compared against the current
** DS attribute values. A DSRECORD structure is populated with this
** information by calling the ReadAttributes() and GetAttributeValue()
** functions. 
*/

void SyncFlatDBToDS(void)
{
   NWDSCCODE   cCode;
   nuint32     flags;
   nuint32     syntaxID;
   pBuf_T      inpBuf;
   DBRECORD    namesDB;
   int         recordCount = 0, iterCount = 0;
   int         skipChanges;
   int         j;

   if ((stream = fopen("NAMESDB.DBT", "rt")) == NULL)
   {
      fprintf(stderr, "Cannot open NAMESDB.DBT file\n");
      exit(1);
   }

   while(!feof(stream))
   {
      fread(&namesDB, sizeof(DBRECORD), 1, stream);
      recordCount += 1;
   }

   --recordCount;

   fseek(stream, 0L, SEEK_SET);

   cCode = NWDSSetContext(
            /* Context Handle */ dContext,
            /* Key            */ DCK_NAME_CONTEXT,
            /* Cntxt to set   */ DS_ROOT_NAME);
   if(cCode)
   {
      printf("\nNWDSSetContext returned %04X", cCode);
      goto _FreeContext;
   }

   /*  Get current directory dContext flags so we can modify them. */

   cCode = NWDSGetContext(
            /* Contxt Handle    */ dContext,
            /* Key              */ DCK_FLAGS,
            /* Context Flags    */ &flags);
   if(cCode)
   {
      printf("NWDSGetContext returned: %04X\n", cCode);
      goto _FreeContext;
   }

   /* Turn typeless naming on and canonicalize names off.  This will 
      return full names. */

   flags |= DCV_TYPELESS_NAMES;
   flags &= ~DCV_CANONICALIZE_NAMES;

   /* Set the directory dContext flags so they take effect */

   cCode = NWDSSetContext(
            /* Context Handle */ dContext,
            /* Key            */ DCK_FLAGS,
            /* Set Flag Value */ &flags);
   if(cCode)
   {
      printf("NWDSSetContext returned: %04X\n", cCode);
      goto _FreeContext;
   }
   
   /* Allocate and initialize the input buffer. */

   cCode = NWDSAllocBuf(
            /* Size    */ DEFAULT_MESSAGE_LEN,
            /*  buffer */ &inpBuf);
   if(cCode)
   {
      printf("NWDSAllocBuf returned: %04X\n", cCode);
      goto _FreeContext;
   }

   cCode = NWDSInitBuf(
            /* Context Handle */ dContext,
            /* DS Operation   */ DSV_MODIFY_ENTRY,
            /* Buffer         */ inpBuf);
   if(cCode)
   {
      printf("NWDSInitBuf returned: %04X\n", cCode);
      goto _FreeBuf;
   }

   for (j=0; j < recordCount; ++j)
   {

      skipChanges = 0;
      fread(&namesDB, sizeof(DBRECORD), 1, stream);
      ReadAttributes(namesDB.fdn);
      
      /* If records are the same, get next record */
      
      if (!memcmp(&namesDB, &namesDS, sizeof(DBRECORD)))
         skipChanges = 1;

      /* Overwrite value of Full Name */
                                                         
      if (strcmp(namesDS.FullName, namesDB.FullName) != 0)
      {
         cCode = NWDSGetSyntaxID(
                   /* Context Handle */ dContext,
                   /* attribute name */ "Full Name",
                   /* Syntax ID      */ &syntaxID);
         if(cCode)
         {
            printf("NWDSGetSyntax returned: %04X\n", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutChange(
                   /* Contxt Handle */ dContext,
                   /* Buffer        */ inpBuf,
                   /* DS Operation  */ DS_OVERWRITE_VALUE,
                   /* Attribute val */ "Full Name");
         if(cCode)
         {
            printf("\nNWDSPutChange returned: %04X", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutAttrVal(
                    /* Contxt Handle */ dContext,
                    /* Buffer        */ inpBuf,
                    /* Syntax        */ syntaxID, 
                    /* Attribute val */ namesDB.FullName);                  
         if(cCode)
         {
            printf("\nNWDSPutAttrVal returned: %04X", cCode);
            goto _FreeBuf;
         }
      }

      /* Overwrite Value of CN (Common Name) */

      if (strcmp(namesDS.CName, namesDB.CName) != 0)
      {
         cCode = NWDSGetSyntaxID(
                   /* Context Handle */   dContext,
                   /* attribute name */   "CN",
                   /* Syntax ID      */   &syntaxID);
         if(cCode)
         {
            printf("NWDSGetSyntax returned: %04X\n", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutChange(
                   /* Contxt Handle */ dContext,
                   /* Buffer        */ inpBuf,
                   /* DS Operation  */ DS_OVERWRITE_VALUE,
                   /* Attribute val */ "CN");   
       if(cCode)
         {
            printf("\nNWDSPutChange returned: %04X", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutAttrVal(
                   /* Contxt Handle */ dContext,
                   /* Buffer        */ inpBuf,
                   /* Syntax        */ syntaxID,
                   /* Attribute val */ namesDB.CName);      
         if(cCode)
         {
            printf("\nNWDSPutAttrVal returned: %04X", cCode);
            goto _FreeBuf;
         }
     }

      /* Overwrite Value of Surname */

      if (strcmp(namesDS.SurName, namesDB.SurName) != 0)
      {
         cCode = NWDSGetSyntaxID(
                    /* Context Handle */   dContext,
                    /* attribute name */   "Surname",
                    /* Syntax ID      */   &syntaxID);
         if(cCode)
         {
            printf("NWDSGetSyntax returned: %04X\n", cCode);
            goto _FreeBuf;
         }
         cCode = NWDSPutChange(
                    /* Contxt Handle */ dContext,
                    /* Buffer        */ inpBuf,
                    /* DS Operation  */ DS_OVERWRITE_VALUE,
                    /* Attribute val */ "Surname");   
         if(cCode)
         {
            printf("\nNWDSPutChange returned: %04X", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutAttrVal(
                    /* Contxt Handle */ dContext,
                    /* Buffer        */ inpBuf,
                    /* Syntax        */ syntaxID,
                    /* Attribute val */ namesDB.SurName);      
         if(cCode)
         {
            printf("\nNWDSPutAttrVal returned: %04X", cCode);
            goto _FreeBuf;
         }
      }

      /* Overwrite Value of Telephone */

      if (strcmp(namesDS.Telephone, namesDB.Telephone) != 0)
      {
         cCode = NWDSGetSyntaxID(
                     /* Context Handle */ dContext,
                     /* attribute name */ "Telephone Number",
                     /* Syntax ID      */ &syntaxID);
         if(cCode)
         {
            printf("NWDSGetSyntax returned: %04X\n", cCode);
            goto _FreeBuf;
         }
  
         cCode = NWDSPutChange(
                    /* Contxt Handle */ dContext,
                    /* Buffer        */ inpBuf,
                    /* DS Operation  */ DS_OVERWRITE_VALUE,
                    /* Attribute val */ "Telephone Number");
         if(cCode)
         {
            printf("\nNWDSPutChange returned: %04X", cCode);
            goto _FreeBuf;
         }

         cCode = NWDSPutAttrVal(
                     /* Contxt Handle */ dContext,
                     /* Buffer        */ inpBuf,
                     /* Syntax        */ syntaxID,
                     /* Attribute val */ namesDB.Telephone);
         if(cCode)
         {
            printf("\nNWDSPutAttrVal returned: %04X", cCode);
            goto _FreeBuf;
         }
      }

      if (!skipChanges)
      {
         /* All of the changes are in the buffer, modify object */
         cCode = NWDSModifyObject(
                   /* Context  Handle*/ dContext,
                   /* Object    Name */ namesDB.fdn,
                   /* Iteration hand */ NULL,
                   /* Partial message*/ 0,
                   /* Change buffer  */ inpBuf);
         if ( cCode < 0 )
         {
            printf("\nNWDSModifyObject returned: %04X", cCode);
            goto _FreeBuf;
         }
         ++iterCount;
      }
   }

   printf("\n%d objects modified.", iterCount);

_FreeBuf:
   NWDSFreeBuf(inpBuf);

_FreeContext:
   fclose(stream);
}

/**************************************************************************
** Func: void ReadAttributes(pnstr8 objectName)
**
** This function reads the current DS attributes and then calls the
** GetAttributeValues() function to get the values for each attribute.
*/

void ReadAttributes(pnstr8 objectName)
{
   NWDSCCODE   cCode;
   nint32      iterHandle = -1L;
   pBuf_T      outBuf;
   pBuf_T      inBuf;   
   int         i;

   memset(&namesDS, 0, sizeof(namesDS));
   memcpy(&namesDS.fdn, objectName, strlen(objectName));

   cCode = NWDSAllocBuf(
              /* Buffer Size */ DEFAULT_MESSAGE_LEN,
              /* Buff. Point.*/ &inBuf);
   if(cCode)
   {
      printf("NWDSAllocBuf returned: %04X\n", cCode);
      exit(1);
   }

   cCode = NWDSInitBuf(
              /* Context  */ dContext,
              /* Operation*/ DSV_READ,
              /* buffer   */ inBuf);
   if(cCode)
   {
      printf("NWDSInitBuf returned: %04X\n", cCode);
      NWDSFreeBuf(inBuf);
      exit(1);
   }

   cCode = NWDSAllocBuf(
             /* Buffer Size */ DEFAULT_MESSAGE_LEN,
             /* Buff. Point.*/ &outBuf);
   if(cCode)
   {
      printf("NWDSAllocBuf returned: %04X\n", cCode);
      NWDSFreeBuf(inBuf);
      exit(1);
   }

   for(i = 0; i < 4; i++)
   {
      cCode = NWDSPutAttrName(
                 /* context   */ dContext,
                 /* in buffer */ inBuf,
                 /* attr. name*/ (pnstr8)attrList[i]);
      if(cCode)
      {
         printf("NWDSPutAttrName returned: %04X\n", cCode);
         NWDSFreeBuf(inBuf);
         NWDSFreeBuf(outBuf);
         exit(1);
      }
   }

   cCode = NWDSRead(
             /* Context     */ dContext,
             /* Object name */ objectName,
             /* Info. Type  */ DS_ATTRIBUTE_VALUES,
             /* All Attrib ?*/ FALSE,
             /* Attri. names*/ inBuf,
             /* Iter. Handle*/ &iterHandle,
             /* Object info */ outBuf);
   if(cCode)
   {
      printf("\nNWDSRead returned: %04X\n", cCode);
      NWDSFreeBuf(inBuf);
      NWDSFreeBuf(outBuf);
      exit(1);
   }

   GetAttributeValues(outBuf);

   NWDSFreeBuf(inBuf);
   NWDSFreeBuf(outBuf);
}

/****************************************************************************
** Function: void GetAttributeValues(pBuf_T   buf)
**
** This function retrieves the values for a given attribute and stores the
** value in the DSRECORD structure. This record is compared against the
** DBRECORD structure in SyncFlatDBToDS(void) to determine what actions
** should be taken on the DS attribute value.
*/

void GetAttributeValues(pBuf_T buf)
{
   NWDSCCODE   cCode;
   nuint32     syntax;
   nuint32     attrCount;
   nuint32     valCount;
   nstr8       attrName[MAX_DN_CHARS + 1];
   nstr8       attrVal[MAX_DN_CHARS + 1];
   nuint32     i, j;
   int         k, cnt;

   cCode = NWDSGetAttrCount(
             /* Context    */ dContext,
             /* attr. buff */ buf,
             /* num of attr*/ &attrCount);
   if(cCode)
   {
      printf("\nNWDSGetAttrCount returned: %04X\n", cCode);
      exit(1);
   }
   
   for (i = 0; i < attrCount; i++)
   {
      cCode = NWDSGetAttrName(
                /* Context        */ dContext,
                /* attrib. buf    */ buf,
                /* attrib name    */ attrName,
                /* attr. val. cnt */ &valCount,
                /* Syntax ID      */ &syntax);
      if(cCode)
      {
         printf("\nNWDSGetAttrName returned: %04X\n", cCode);
         exit(1);
      }

      /* Copy the attribute value to the DSRECORD structure  */

      for (j = 0; j < valCount; j++)
      {
         cCode = NWDSGetAttrVal(
                   /* Context  */ dContext,
                   /* read buf */ buf,
                   /* syntax id*/ syntax,
                   /* attr. val*/ attrVal);
         if(cCode)
         {
            printf("\nNWDSGetAttrVal returned: %04X\n", cCode);
            exit(1);
         }

         if (j == 0)
         {
            for (k=0; k < 4; k++)
            {
               if (!strncmp(attrName, attrList[k], 2))
                  cnt = k;
            }

            switch(cnt)
            {
               case 0:
                  memcpy(namesDS.FullName, attrVal, strlen(attrVal));
                  break;

               case 1:
                  memcpy(namesDS.CName, attrVal, strlen(attrVal));
                  break;

               case 2:
                  memcpy(namesDS.SurName, attrVal, strlen(attrVal));
                  break;

               case 3:
                  memcpy(namesDS.Telephone, attrVal, strlen(attrVal));
                  break;

            }
         }
      }
   }
} 
