#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <readpassphrase.h>
#include <DirectoryService/DirectoryService.h>


#ifndef bool
typedef enum    { false = 0, true = 1 } bool;
#endif  /* bool */


// 32k buffer
#define BUFFERSIZE  32 * 1024  



tDirReference DoTinyAuth2(
    tDirReference       dirRef,
    tDirNodeReference  nodeRef,
    const char           *user, 
    const char           *pass, 
    bool           *authResult,
          char     **resultMsg  )
{
    long               dirStatus = eDSNoErr ;
    tDataBufferPtr    authBuffer ;
    tDataBufferPtr    respBuffer ;
    tContextData     authContext ;
    tDataNodePtr        authType ;

    long                 userLen = strlen( user );
    long                 passLen = strlen( pass );
    long          authBufferLen  ;

    authBufferLen = sizeof(long) + userLen
                  + sizeof(long) + passLen ;

    authBuffer    = dsDataBufferAllocate( dirRef, authBufferLen );
    respBuffer    = dsDataBufferAllocate( dirRef, BUFFERSIZE    ); //dummy

#if ( MAC_OS_X_VERSION_MAX_ALLOWED  >= MAC_OS_X_VERSION_10_5 )

    dsFillAuthBuffer( authBuffer, 2,
                      userLen, user, 
                      passLen, pass  );

#else // Tiger or eariler
    { 
        long             pos         = 0;

        pos = 0;
        memcpy( &(authBuffer->fBufferData[pos]), &userLen, sizeof(long) );
        pos += sizeof(long);
        memcpy( &(authBuffer->fBufferData[pos]),     user,      userLen );
        pos += userLen ;
        memcpy( &(authBuffer->fBufferData[pos]), &passLen, sizeof(long) );
        pos += sizeof(long);
        memcpy( &(authBuffer->fBufferData[pos]),     pass,      passLen );
        pos += passLen ;
        authBuffer->fBufferLength = pos ;
    }
#endif

    authType  = dsDataNodeAllocateString( dirRef, kDSStdAuthNodeNativeNoClearText );
    dirStatus =  dsDoDirNodeAuth( nodeRef, authType, true, authBuffer, respBuffer, &authContext );
    switch( dirStatus )
    {
    case eDSNoErr: // Auth Success
       *authResult = true ;
        if( resultMsg ) *resultMsg = "Auth Success." ;
        break;
    case eDSAuthFailed : // Auth Fail 
       *authResult = false ;
        if( resultMsg ) *resultMsg = "Auth Failure." ;
        break;
    case eDSAuthMethodNotSupported : // Auth not supported
       *authResult = false ;
        if( resultMsg ) *resultMsg = "This Auth Type not supported." ;
        break;
    default: 
       *authResult = false ;
        if( resultMsg ) *resultMsg = "unknown error." ;
        break;
    }
    dsDataBufferDeAllocate ( dirRef, authBuffer );
    dsDataBufferDeAllocate ( dirRef, respBuffer );

    return dirStatus;
}

int main( int argc, char *argv[] )
{
    int                         ret = 0 ;
    long                  dirStatus = eDSNoErr ;

    tDirReference            dirRef = 0 ;
    tDirNodeReference       nodeRef = 0 ;

    const char            *nodepath = (argc > 1 ? argv[1] : "/Local/Default" );
    tDataListPtr           nodeName = NULL ;
    const char                *user = (argc > 2 ? argv[2] : "me" );
    char           pass[BUFFERSIZE] ;  // tenuki
    char                 *resultMsg = NULL ;
    bool                   isAuthOK = false;


  // open OD
    dirStatus = dsOpenDirService( &dirRef );
    if( dirStatus != eDSNoErr ) goto errexit ;

  // open node
    nodeName = dsBuildFromPath( dirRef, nodepath, "/" );
    if( nodeName == NULL )
    {
        fprintf( stderr, "name buffer allocate error\n") ;
        ret = 1; 
        goto toexit;
    }
    dirStatus = dsOpenDirNode( dirRef, nodeName, &nodeRef );
    if( dirStatus != eDSNoErr ) goto errexit ;

    if( NULL == readpassphrase("Passwd: ", pass, sizeof( pass ), 0 ) )
        goto errexit ;


    dirStatus = DoTinyAuth2( dirRef, nodeRef, user, pass, &isAuthOK, &resultMsg );

    printf( "user %s, auth %s.\n", user, ( isAuthOK ? "OK" : "NG" ) );
    if( resultMsg != NULL )
         printf( "msg = %s (%d)\n", resultMsg, dirStatus );

    goto toexit;
errexit:
    ret = 1;
    fprintf( stderr, "errocode = %d\n", dirStatus );
toexit:
    if( nodeRef != 0 )
        dsCloseDirNode( nodeRef );
    if( dirRef != 0 )
        dsCloseDirService( dirRef );
    exit(ret);
}