#include "NWCOMPAT.hxx"
#pragma hdrstop

//----------------------------------------------------------------------------
//
//  Function: NWApiGetProperty
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetProperty(
    BSTR bstrObjectName,
    LPSTR lpszPropertyName,
    NWOBJ_TYPE dwOT_ID,
    NWCONN_HANDLE hConn,
    LPP_RPLY_SGMT_LST lppReplySegment,
    LPDWORD pdwNumSegment
    )
{
    CHAR             szObjectName[(OBJ_NAME_SIZE + 1)*2];
    NWFLAGS          pucMoreFlag = 0;
    NWFLAGS          pucPropFlag = 0;
    unsigned char    ucSegment = 1;
    LP_RPLY_SGMT_LST lpHeadReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTempReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTemp = NULL;
    HRESULT          hr = S_OK;
    NWCCODE          usRet = 0;

    //
    // lppReplySegment is assumed to be NULL.
    //

    ADsAssert((*lppReplySegment) == NULL);

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    
    if (wcslen(bstrObjectName) > OBJ_NAME_SIZE) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }

    UnicodeToAnsiString(
        bstrObjectName,
        szObjectName,
        0
        );

    //
    // Initialize first node of the list and set up temp traversal pointer.
    //

    INIT_RPLY_SGMT(lpTempReplySegment);
    lpHeadReplySegment = lpTempReplySegment;

    //
    // Read & dump property values into linked-list.
    //

    do {

       usRet = NWCReadPropertyValue(
                   hConn,
                   szObjectName,
                   dwOT_ID,
                   lpszPropertyName,
                   ucSegment,
                   lpTempReplySegment->Segment,
                   &pucMoreFlag,
                   &pucPropFlag
                   );
       hr = HRESULT_FROM_NWCCODE(usRet);

       BAIL_ON_FAILURE(hr);

       if (pucMoreFlag) {

           INIT_RPLY_SGMT(lpTempReplySegment->lpNext);
           lpTempReplySegment = lpTempReplySegment->lpNext;

          ucSegment++;
       }

    } while(pucMoreFlag);

    //
    // Return the resulting linked-list.
    //

    *lppReplySegment = lpHeadReplySegment;
    *pdwNumSegment = ucSegment;

error:
    if (FAILED(hr) && lpHeadReplySegment) {

        DELETE_LIST(lpHeadReplySegment);
    }

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: NWApiGetFileServerVersionInfo
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetFileServerVersionInfo(
    NWCONN_HANDLE hConn,
    VERSION_INFO  *pVersionInfo
    )
{
    NWCCODE usRet = SUCCESSFUL;
    HRESULT hr = S_OK;

    usRet = NWCGetFileServerVersionInfo(
                hConn,
                pVersionInfo
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: NWApiIsObjectInSet
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiIsObjectInSet(
    NWCONN_HANDLE hConn,
    LPWSTR lpszObjectName,
    NWOBJ_TYPE wObjType,
    LPSTR lpszPropertyName,
    LPSTR lpszMemberName,
    NWOBJ_TYPE wMemberType
    )
{
    CHAR szAnsiObjectName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //

    if (wcslen(lpszObjectName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }

    UnicodeToAnsiString(
        lpszObjectName,
        szAnsiObjectName,
        0
        );

    //
    // Call NWCIsObjectInSet.
    //

    usRet = NWCIsObjectInSet(
                hConn,
                szAnsiObjectName,
                wObjType,
                lpszPropertyName,
                lpszMemberName,
                wMemberType
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: NWApiGetObjectID
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetObjectID(
    NWCONN_HANDLE hConn,
    LPWSTR lpszObjectName,
    NWOBJ_TYPE wObjType,
    NWOBJ_ID *pObjectID
    )
{
    CHAR szAnsiObjectName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszObjectName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }

    UnicodeToAnsiString(
        lpszObjectName,
        szAnsiObjectName,
        0
        );

    //
    // Get Object's ID.
    //

    usRet = NWCGetObjectID(
                hConn,
                szAnsiObjectName,
                wObjType,
                pObjectID
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: NWApiGroupGetMembers
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGroupGetMembers(
    NWCONN_HANDLE hConn,
    LPWSTR szGroupName,
    LPBYTE *lppBuffer
    )
{
    DWORD   dwNumSegment = 0;
    HRESULT hr = S_OK;
    DWORD   i;
    LP_RPLY_SGMT_LST lpTemp = NULL;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;

    //
    // Assert
    //

    ADsAssert(*lppBuffer == NULL);

    //
    // Get GROUP_MEMBERS.
    //

    hr = NWApiGetProperty(
             szGroupName,
             NW_PROP_GROUP_MEMBERS,
             OT_USER_GROUP,
             hConn,
             &lpReplySegment,
             &dwNumSegment
             );
    BAIL_ON_FAILURE(hr);

    //
    // Pack returned linked list into buffer.
    //

    *lppBuffer = (LPBYTE) AllocADsMem(
                           dwNumSegment * REPLY_VALUE_SIZE
                           );
    if (!(*lppBuffer)) {
        RRETURN(E_OUTOFMEMORY);
    }

    lpTemp = lpReplySegment;

    for (i = 0; i < dwNumSegment; i++) {
        memcpy(
            *lppBuffer + i * REPLY_VALUE_SIZE,
            lpTemp->Segment,
            REPLY_VALUE_SIZE
            );
        lpTemp = lpTemp->lpNext;
    }

error:

    //
    // Clean up.
    //

    lpTemp = NULL;

    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }

    RRETURN(hr);
}
//----------------------------------------------------------------------------
//
//  Function: NWApiAddGroupMember
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiAddGroupMember(
    NWCONN_HANDLE hConn,
    LPWSTR pszGroupName,
    LPWSTR pszMemberName
    )
{
    CHAR    szGroupName[(OBJ_NAME_SIZE + 1)*2];
    CHAR    szMemberName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(pszGroupName) > OBJ_NAME_SIZE) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }
    
    if (wcslen(pszMemberName) > OBJ_NAME_SIZE) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }

    UnicodeToAnsiString(
        pszGroupName,
        szGroupName,
        0
        );

    UnicodeToAnsiString(
        pszMemberName,
        szMemberName,
        0
        );

    //
    // Modify GROUP_MEMBERS property of the group to include the new member.
    //

    usRet = NWCAddObjectToSet(
                hConn,
                szGroupName,
                OT_USER_GROUP,
                "GROUP_MEMBERS",
                szMemberName,
                OT_USER
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Modify GROUPS_I'M_IN property of the new member to reflect its included
    // in the new group.
    //

    usRet = NWCAddObjectToSet(
                hConn,
                szMemberName,
                OT_USER,
                "GROUPS_I'M_IN",
                szGroupName,
                OT_USER_GROUP
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Modify SECURITY_EQUALS property of the new member to equate its security
    // to that of the new group it just joined.
    //

    usRet = NWCAddObjectToSet(
                hConn,
                szMemberName,
                OT_USER,
                "SECURITY_EQUALS",
                szGroupName,
                OT_USER_GROUP
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiRemoveGroupMember
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiRemoveGroupMember(
    NWCONN_HANDLE hConn,
    LPWSTR pszGroupName,
    LPWSTR pszMemberName
    )
{
    CHAR    szGroupName[(OBJ_NAME_SIZE + 1)*2];
    CHAR    szMemberName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(pszGroupName) > OBJ_NAME_SIZE) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }
    
    if (wcslen(pszMemberName) > OBJ_NAME_SIZE) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }

    UnicodeToAnsiString(
        pszGroupName,
        szGroupName,
        0
        );

    UnicodeToAnsiString(
        pszMemberName,
        szMemberName,
        0
        );

    //
    // Modify SECURITY_EQUALS property of the removed member to break its
    // security tie with the group it joined.
    //

    usRet = NWCDeleteObjectFromSet(
                hConn,
                szMemberName,
                OT_USER,
                "SECURITY_EQUALS",
                szGroupName,
                OT_USER_GROUP
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Modify GROUPS_I'M_IN property of the new member to reflect it is not
    // included in the group anymore.
    //

    usRet = NWCDeleteObjectFromSet(
                hConn,
                szMemberName,
                OT_USER,
                "GROUPS_I'M_IN",
                szGroupName,
                OT_USER_GROUP
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Modify GROUP_MEMBERS property of the group to remove the member.
    //

    usRet = NWCDeleteObjectFromSet(
                hConn,
                szGroupName,
                OT_USER_GROUP,
                "GROUP_MEMBERS",
                szMemberName,
                OT_USER
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

error:
    RRETURN(hr);

}
//----------------------------------------------------------------------------
//
//  Function: NWApiGetLOGIN_CONTROL
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetLOGIN_CONTROL(
    NWCONN_HANDLE hConn,
    LPWSTR lpszUserName,
    LPLC_STRUCTURE lpLoginCtrlStruct
    )
{
    DWORD            dwNumSegment = 0;
    HRESULT          hr = S_OK;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTemp = NULL;

    hr = NWApiGetProperty(
             lpszUserName,
             NW_PROP_LOGIN_CONTROL,
             OT_USER,
             hConn,
             &lpReplySegment,
             &dwNumSegment
             );
    BAIL_ON_FAILURE(hr);

    *lpLoginCtrlStruct = *((LPLC_STRUCTURE) lpReplySegment->Segment);

error:

    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiSetDefaultAcctExpDate
//
//  Synopsis: This function looks at the local time and returns a default value
//            for an account expiration date in a variant date.
//
//----------------------------------------------------------------------------
HRESULT
NWApiSetDefaultAcctExpDate(
    DOUBLE * pdTime,
    SYSTEMTIME SysTime
    )
{
    DOUBLE  vTime;
    HRESULT hr = S_OK;

    //
    // According to SysCon, the default account expiration date is the first day
    // of the following month.
    //

    if (SysTime.wMonth == 12) {
        SysTime.wMonth = 1;
    }
    else {
        SysTime.wMonth++;
    }

    SysTime.wDay = 1;

    //
    // Subtract 1980 from wYear for NWApiMakeVariantTime.
    //

    SysTime.wYear -= 1980;

    hr = NWApiMakeVariantTime(
             &vTime,
             SysTime.wDay,
             SysTime.wMonth,
             SysTime.wYear,
             0,0,0
             );
    BAIL_ON_FAILURE(hr);

    *pdTime = vTime;

error:

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiUserAsSupervisor
//
//  Synopsis: This functions turns the user into one of the supervisors if TRUE
//            is passed.  User's supervisor privilege is removed otherwise.
//
//----------------------------------------------------------------------------
HRESULT
NWApiUserAsSupervisor(
    NWCONN_HANDLE hConn,
    LPWSTR lpszUserName,
    BOOL fSupervisor
    )
{
    CHAR    szUserName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszUserName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        lpszUserName,
        szUserName,
        0
        );

    //
    // Make it a supervisor.
    //

    if (fSupervisor == TRUE) {
        usRet = NWCAddObjectToSet(
                    hConn,
                    szUserName,
                    OT_USER,
                    "SECURITY_EQUALS",
                    "SUPERVISOR",
                    OT_USER
                    );
    }

    //
    // Remove supervisor privilege.
    //

    else {
        usRet = NWCDeleteObjectFromSet(
                    hConn,
                    szUserName,
                    OT_USER,
                    "SECURITY_EQUALS",
                    "SUPERVISOR",
                    OT_USER
                    );
    }

    hr = HRESULT_FROM_NWCCODE(usRet);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiGetVolumeNumber
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetVolumeNumber(
    NWCONN_HANDLE hConn,
    LPWSTR lpszVolumeName,
    NWVOL_NUM *pVolumeNumber
    )
{
    CHAR    szVolumeName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszVolumeName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        lpszVolumeName,
        szVolumeName,
        0
        );

    //
    // Get Volume's number.
    //

    usRet = NWCGetVolumeNumber(
                hConn,
                szVolumeName,
                pVolumeNumber
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    //
    // Return.
    //

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiGetVolumeName
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetVolumeName(
    NWCONN_HANDLE hConn,
    NWVOL_NUM bVolNum,
    LPWSTR *lppszVolName
    )
{
    CHAR    szVolumeName[OBJ_NAME_SIZE + 1];
    HRESULT hr = S_OK;
    LPWSTR  lpszTemp = NULL;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Get Volume's name.
    //

    usRet = NWCGetVolumeName(
                hConn,
                bVolNum,
                szVolumeName
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Convert result into Unicode.
    //

    lpszTemp = AllocateUnicodeString(szVolumeName);
    if (!lpszTemp) {
       RRETURN(E_OUTOFMEMORY);
    }

    *lppszVolName = AllocADsStr(lpszTemp);
    if (!(*lppszVolName)) {
        RRETURN(E_OUTOFMEMORY);
    }

    FreeUnicodeString(lpszTemp);

    //
    // Return.
    //

    RRETURN(hr);

error:

    *lppszVolName = NULL;

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiEnumJobs
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiEnumJobs(
    HANDLE hPrinter,
    DWORD dwFirstJob,
    DWORD dwNoJobs,
    DWORD dwLevel,
    LPBYTE *lplpbJobs,
    DWORD *pcbBuf,
    LPDWORD lpdwReturned
    )
{
    BOOL    fStatus = TRUE;
    HRESULT hr = S_OK;

    fStatus = WinNTEnumJobs(
                  hPrinter,
                  dwFirstJob,
                  dwNoJobs,
                  dwLevel,
                  lplpbJobs,
                  pcbBuf,
                  lpdwReturned
                  );

    if (fStatus == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiGetPrinter
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiGetPrinter(
    HANDLE hPrinter,
    DWORD  dwLevel,
    LPBYTE *lplpbPrinters
    )
{
    BOOL    fStatus = TRUE;
    HRESULT hr = S_OK;

    fStatus = WinNTGetPrinter(
                  hPrinter,
                  dwLevel,
                  lplpbPrinters
                  );

    if (fStatus == FALSE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiUncFromADsPath
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiUncFromADsPath(
    LPWSTR lpszADsPath,
    LPWSTR lpszUncName
    )
{
    HRESULT hr;
    POBJECTINFO pObjectInfo = NULL;

    hr = BuildObjectInfo(lpszADsPath,
                         &pObjectInfo );

    BAIL_ON_FAILURE(hr);

   wsprintf(
        lpszUncName,
        L"\\\\%s\\%s",
        pObjectInfo->ComponentArray[0],
        pObjectInfo->ComponentArray[1]
        );

error:
    if(pObjectInfo){
        FreeObjectInfo(pObjectInfo);
    }
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiMakeUserInfo
//
//  Synopsis: This function is very provider specific.
//
//----------------------------------------------------------------------------
HRESULT
NWApiMakeUserInfo(
    LPWSTR lpszBinderyName,
    LPWSTR lpszUserName,
    LPWSTR lpszPassword,
    PNW_USER_INFO pNwUserInfo
    )
{
    HRESULT hr = S_OK;
    NW_USER_INFO NwUserInfo = {NULL, NULL, NULL, NULL};

    hr = NWApiGetBinderyHandle(
             &NwUserInfo.hConn,
             lpszBinderyName
             );
    BAIL_ON_FAILURE(hr);

    hr = ADsAllocString(lpszBinderyName, &NwUserInfo.lpszBinderyName);
    BAIL_ON_FAILURE(hr);

    hr = ADsAllocString(lpszUserName, &NwUserInfo.lpszUserName);
    BAIL_ON_FAILURE(hr);

    if (lpszPassword) {

        hr = ADsAllocString(lpszPassword, &NwUserInfo.lpszPassword);
        BAIL_ON_FAILURE(hr);
    }

    //
    // Return.
    //

    *pNwUserInfo = NwUserInfo;
    RRETURN(hr);

error:
    if (NwUserInfo.lpszBinderyName)
        ADsFreeString(NwUserInfo.lpszBinderyName);

    if (NwUserInfo.lpszUserName)
        ADsFreeString(NwUserInfo.lpszUserName);

    if (NwUserInfo.lpszPassword)
        ADsFreeString(NwUserInfo.lpszPassword);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiFreeUserInfo
//
//  Synopsis: This function is very provider specific.
//
//----------------------------------------------------------------------------
HRESULT
NWApiFreeUserInfo(
    PNW_USER_INFO pNwUserInfo
    )
{
    HRESULT hr = S_OK;

    if (pNwUserInfo->lpszBinderyName) {
        ADsFreeString(pNwUserInfo->lpszBinderyName);
        pNwUserInfo->lpszBinderyName = NULL ;
    }

    if (pNwUserInfo->lpszUserName) {
        ADsFreeString(pNwUserInfo->lpszUserName);
        pNwUserInfo->lpszUserName = NULL;
    }

    if (pNwUserInfo->lpszPassword) {
        ADsFreeString(pNwUserInfo->lpszPassword);
        pNwUserInfo->lpszPassword = NULL;
    }

    if (pNwUserInfo->hConn) {
        hr = NWApiReleaseBinderyHandle(
                 pNwUserInfo->hConn
                 );
        BAIL_ON_FAILURE(hr);
    }

error:
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreateUser
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreateUser(
    PNW_USER_INFO pNwUserInfo
    )
{
    HRESULT       hr = S_OK;
    HRESULT       hrTemp = S_OK;
    NTSTATUS      Status = STATUS_SUCCESS;
    NWCCODE       usRet = SUCCESSFUL;
    NWCONN_HANDLE hConn = NULL;
    NWOBJ_ID      UserObjectID;
    UCHAR         ChallengeKey[8];
    UCHAR         NewKeyedPassword[17];
    UCHAR         ValidationKey[8];
    WCHAR         szTemp[MAX_PATH];

    //
    // "Create Bindery Object" - user object.  This user object is going to be
    // static, with access equals to logged read, supervisor write.
    //

    hr = NWApiCreateBinderyObject(
             pNwUserInfo->hConn,
             pNwUserInfo->lpszUserName,
             OT_USER,
             BF_STATIC,
             BS_LOGGED_READ | BS_SUPER_WRITE
             );
    BAIL_ON_FAILURE(hr);

    //
    // Add user password.
    //

    hr = NWApiSetUserPassword(
             pNwUserInfo,
             &UserObjectID,
             NULL                 // no old passwd - this is a SET
             );
    BAIL_ON_FAILURE(hr);

    //
    // Create necessary bindery property to facilitate the addition of this user
    // to the group EVERYONE.
    //

    hr = NWApiCreateProperty(
             pNwUserInfo->hConn,
             pNwUserInfo->lpszUserName,
             OT_USER,
             "GROUPS_I'M_IN",
             BF_SET
             );
    BAIL_ON_FAILURE(hr);

    hr = NWApiCreateProperty(
             pNwUserInfo->hConn,
             pNwUserInfo->lpszUserName,
             OT_USER,
             "SECURITY_EQUALS",
             BF_SET
             );
    BAIL_ON_FAILURE(hr);

    //
    // Add this user to the group EVERYONE.
    // (okay if this fails, EVERYONE might not exist)
    //

    wcscpy(szTemp, L"EVERYONE");

    hrTemp = NWApiAddGroupMember(
                pNwUserInfo->hConn,
                szTemp,
                pNwUserInfo->lpszUserName
                );

    //
    // Create mail directory and login files.
    // (okay if this fails)
    //

    hrTemp = NWApiCreateMailDirectory(
                pNwUserInfo,
                UserObjectID
                );

    //
    // Create LOGIN_CONTROL & ACCOUNT_BALANCE property for the user.  Values
    // from USER_DEFAULTS are used as default.
    //

    hr = NWApiSetLoginCtrlAndAcctBalance(
             pNwUserInfo
             );
    BAIL_ON_FAILURE(hr);

error:

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiDeleteUser
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiDeleteUser(
    POBJECTINFO pObjectInfo
    )
{
    BOOL          err = TRUE;
    DWORD         dwErr = 0;
    HRESULT       hr = S_OK;
    NWCONN_HANDLE hConn = NULL;
    NWOBJ_ID      ObjectID;
    WCHAR         szPath[MAX_PATH];

    //
    // Open a handle to the bindery.
    //

    hr = NWApiGetBinderyHandle(
             &hConn,
             pObjectInfo->ComponentArray[0]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Get the user's ObjectID which is needed to compose the path name of LOGIN
    // and LOGIN.OS2.
    //

    hr = NWApiGetObjectID(
             hConn,
             pObjectInfo->ComponentArray[1],
             OT_USER,
             &ObjectID
             );
    BAIL_ON_FAILURE(hr);

    //
    // Delete SYS:MAIL\<JOBID>\LOGIN.  If the file is not found, that's OK, as
    // long as it is not there.
    //

    wsprintf(
        szPath,
        L"\\\\%s\\SYS\\MAIL\\%X\\LOGIN",
        pObjectInfo->ComponentArray[0],
        dwSWAP(ObjectID)
        );

    err = DeleteFile(szPath);

    //
    // Remove any error checking for the cleanup of
    // files. If they do exist, and we do clean them up
    // great. But otherwise Win95 chokes on us.
    //

    //
    // Delete SYS:MAIL\<JOBID>\LOGIN.OS2.  If the file is not found, that's OK,
    // as long as it is not there.
    //

    wsprintf(
        szPath,
        L"\\\\%s\\SYS\\MAIL\\%X\\LOGIN.OS2",
        pObjectInfo->ComponentArray[0],
        dwSWAP(ObjectID)
        );

    err = DeleteFile(szPath);

    //
    // Remove any error checking for the cleanup of
    // files. If they do exist, and we do clean them up
    // great. But otherwise Win95 chokes on us.
    //

    //
    // Delete SYS:MAIL\<JOBID>.
    //

    wsprintf(
        szPath,
        L"\\\\%s\\SYS\\MAIL\\%X",
        pObjectInfo->ComponentArray[0],
        dwSWAP(ObjectID)
        );

    err = RemoveDirectory(szPath);

    //
    // Remove any error checking for the cleanup of
    // files. If they do exist, and we do clean them up
    // great. But otherwise Win95 chokes on us.
    //


    //
    // Delete the user object.
    //

    hr = NWApiDeleteBinderyObject(
             hConn,
             pObjectInfo->ComponentArray[1],
             OT_USER
             );
    BAIL_ON_FAILURE(hr);

error:

    NWApiReleaseBinderyHandle(hConn);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreateBinderyObject
//
//  Synopsis: This function create the specified object in the specified NetWare
//            bindery.  It returns S_OK if the object alread exist.
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreateBinderyObject(
    NWCONN_HANDLE hConn,
    LPWSTR lpszObjectName,
    NWOBJ_TYPE wObjType,
    NWFLAGS ucObjectFlags,
    NWFLAGS usObjSecurity
    )
{
    CHAR szAnsiObjectName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszObjectName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        lpszObjectName,
        szAnsiObjectName,
        0
        );

    //
    // Create a Static object with LOGGED_READ & SUPERVISOR_WRITE.
    //

    usRet = NWCCreateObject(
                hConn,
                szAnsiObjectName,
                wObjType,
                BF_STATIC,
                BS_LOGGED_READ | BS_SUPER_WRITE
                );
    //
    // If an error occured, check if it is OBJECT_ALREADY_EXISTS.  If it is,
    // treat it as no error.
    //

    if (usRet) {
        if (usRet == OBJECT_ALREADY_EXISTS) {
            usRet = SUCCESSFUL;
        }
    }

    //
    // Return.
    //

    hr = HRESULT_FROM_NWCCODE(usRet);
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiDeleteBinderyObject
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiDeleteBinderyObject(
    NWCONN_HANDLE hConn,
    LPWSTR lpszObjectName,
    NWOBJ_TYPE wObjType
    )
{
    CHAR szAnsiObjectName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszObjectName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        lpszObjectName,
        szAnsiObjectName,
        0
        );

    //
    // Delete the object from the bindery.
    //

    usRet = NWCDeleteObject(
                hConn,
                szAnsiObjectName,
                wObjType
                );
    //
    // Return.
    //

    hr = HRESULT_FROM_NWCCODE(usRet);
    RRETURN(hr);
}

#define            NW_MAX_PASSWORD_LEN    256

//----------------------------------------------------------------------------
//
//  Function: NWApiSetUserPassword
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiSetUserPassword(
    PNW_USER_INFO pNwUserInfo,
    DWORD *pdwUserObjID,
    LPWSTR pszOldPassword
    )
{
    CHAR           szAnsiUserName[(OBJ_NAME_SIZE + 1)*2];
    CHAR           szAnsiPassword[(NW_MAX_PASSWORD_LEN + 1)*2];
    CHAR           szAnsiOldPassword[(NW_MAX_PASSWORD_LEN + 1)*2];
    CHAR           Buffer[128];
    DWORD          err = 0;
    HRESULT        hr = S_OK;
    LC_STRUCTURE   LoginCtrl;
    NTSTATUS       NtStatus;
    UCHAR          ChallengeKey[8];
    UCHAR          ucMoreFlag;
    UCHAR          ucPropFlag;
    WCHAR          szOldPasswordCopy[NW_MAX_PASSWORD_LEN + 1];

    if ( !pNwUserInfo ||
         !(pNwUserInfo->lpszUserName) ||
         !(pNwUserInfo->lpszPassword) ) {

        hr = E_INVALIDARG ;
        BAIL_ON_FAILURE(hr);
    }

    if ( (wcslen(pNwUserInfo->lpszUserName) > OBJ_NAME_SIZE) ||
         (wcslen(pNwUserInfo->lpszPassword) > NW_MAX_PASSWORD_LEN) ||
         ( pszOldPassword && (wcslen(pszOldPassword) > NW_MAX_PASSWORD_LEN)) ) {
        hr = E_INVALIDARG;
        BAIL_ON_FAILURE(hr);
    }

    //
    // Convert UNICODE into ANSI representation required by NW APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //

    err = UnicodeToAnsiString(
        pNwUserInfo->lpszUserName,
        szAnsiUserName,
        0
        );
    if (!err) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    _wcsupr(pNwUserInfo->lpszPassword) ;
    err = UnicodeToAnsiString(
        pNwUserInfo->lpszPassword,
        szAnsiPassword,
        0
        );
    if (!err) {
        hr = E_FAIL;
        BAIL_ON_FAILURE(hr);
    }

    if (pszOldPassword) {
        wcscpy(szOldPasswordCopy, pszOldPassword);
        _wcsupr(szOldPasswordCopy) ;
        err = UnicodeToAnsiString(
            szOldPasswordCopy,
            szAnsiOldPassword,
            0
            );
        if (!err) {
            hr = E_FAIL;
            BAIL_ON_FAILURE(hr);
        }

    }
    else {

        szAnsiOldPassword[0] = 0 ;
    }

    //
    // Get challenge key.
    //

    err = NWApiMapNtStatusToDosError(
              NWPGetChallengeKey(
                  pNwUserInfo->hConn,
                  ChallengeKey
                  ));

    if (!err) {

        //
        // For NetWare 4.x servers, this has to be done after the
        // NWPGetChallengeKey so that the object id returned can be used to
        // encrypt the password. 4.x bindery emulation might return different
        // object ids for some users depending on whether the NWPGetChallengeKey
        // is called beforehand.
        //

        err = NWApiMapNtStatusToDosError(
                  NWPGetObjectID(
                      pNwUserInfo->hConn,
                      szAnsiUserName,
                      OT_USER,
                      pdwUserObjID
                      ));
    }


    if (!err) {

        //
        // The old password and object ID make up the 17-byte Vold. This is used
        // later to form the 17-byte Vc for changing password on the server.
        //

        UCHAR ValidationKey[8];
        UCHAR NewKeyedPassword[17];

        EncryptChangePassword(
            (PUCHAR) szAnsiOldPassword,
            (PUCHAR) szAnsiPassword,
            *pdwUserObjID,
            ChallengeKey,
            ValidationKey,
            NewKeyedPassword
            );

        err = NWApiMapNtStatusToDosError(
                  NWPChangeObjectPasswordEncrypted(
                      pNwUserInfo->hConn,
                      szAnsiUserName,
                      OT_USER,
                      ValidationKey,
                      NewKeyedPassword
                      ));
    }

    //
    // Return.
    //

    hr = HRESULT_FROM_WIN32(err);


error:

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreateMailDirectory
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreateMailDirectory(
    PNW_USER_INFO pNwUserInfo,
    NWOBJ_ID UserObjID
    )
{
    BYTE    szPath[(MAX_PATH + 1) * sizeof(WCHAR)];
    CHAR    szUserObjID[255];
    DWORD   err = 0;
    HRESULT hr = S_OK;

    //
    // Make path.
    //

    _ltoa(
        dwSWAP(UserObjID),
        szUserObjID,
        16
        );

    strcpy((char *) szPath, "SYS:\\MAIL\\");
    strcat((char *) szPath, szUserObjID);

    //
    // Create a directory with Maximum rights mask.
    //

    err = NWApiMapNtStatusToDosError(
              NWPCreateDirectory(
                  pNwUserInfo->hConn,
                  0,
                  (char *) szPath,
                  0xFF // From SysCon --- Max. access rights for directory
                  ));  //   = Full Access
                  
    if ( !err ) {
        //
        // Add a trustee with all rights except PARENTAL right.
        //

        err = NWApiMapNtStatusToDosError(
                  NWPAddTrustee(
                      pNwUserInfo->hConn,
                      0,
                      (char *) szPath,
                      UserObjID,
                      0xDF  // From SysCon --- Trustee has all rights
                      ));   //   EXCEPT parental rights (right to create/
                            //   delete subdirs, make others trustees)
        //
        // Create a Login file.
        //

        if ( !err ) {
            HANDLE hFile;

            wsprintfW(
                (LPWSTR) szPath,
                L"\\\\%ws\\SYS\\MAIL\\%X\\LOGIN",
                pNwUserInfo->lpszBinderyName,
                dwSWAP(UserObjID)
                );

            hFile = CreateFile(
                        (LPWSTR) szPath,
                        GENERIC_WRITE,
                        0,
                        NULL,
                        OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        0
                        );

            if ( hFile == INVALID_HANDLE_VALUE ) {
                err = GetLastError();
            }

            if ( !err )
                CloseHandle( hFile );

            //
            // Create a Login.os2 file.
            //

            wsprintfW(
                (LPWSTR) szPath,
                L"\\\\%ws\\SYS\\MAIL\\%X\\LOGIN.OS2",
                pNwUserInfo->lpszBinderyName,
                dwSWAP(UserObjID)
                );

            hFile = CreateFile(
                        (LPWSTR) szPath,
                        GENERIC_WRITE,
                        0,
                        NULL,
                        OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        0
                        );

            if ( hFile == INVALID_HANDLE_VALUE ) {
                err = GetLastError();
            }

            if ( !err )
                CloseHandle( hFile );
        }
    }

    // err == 255 == "FAILURE"
    // might be used to indicate directory already exists,
    // but also used to signal generic failure

    hr = HRESULT_FROM_WIN32(err);
    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiSetLoginCtrlAndAcctBalance
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiSetLoginCtrlAndAcctBalance(
    PNW_USER_INFO pNwUserInfo
    )
{
    ACCT_BALANCE     AccountBalance;
    DWORD            dwNumSegment;
    HRESULT          hr = S_OK;
    int              i = 0;
    LC_STRUCTURE     LoginCtrl;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;
    LP_RPLY_SGMT_LST lpTemp = NULL;
    USER_DEFAULT     UserDefault;
    WCHAR            szTemp[MAX_PATH];

    //
    // Get Supervisor's USER_DEFAULTS.
    //

    wcscpy(szTemp, NW_PROP_SUPERVISORW);

    hr = NWApiGetProperty(
             szTemp,
             NW_PROP_USER_DEFAULTS,
             OT_USER,
             pNwUserInfo->hConn,
             &lpReplySegment,
             &dwNumSegment
             );

    if (SUCCEEDED(hr)) {
    
        UserDefault = *((LPUSER_DEFAULT) lpReplySegment->Segment);

        //
        // Put default values into LoginCtrl.
        //

        LoginCtrl.byAccountExpires[0] = UserDefault.byAccountExpiresYear;
        LoginCtrl.byAccountExpires[1] = UserDefault.byAccountExpiresMonth;
        LoginCtrl.byAccountExpires[2] = UserDefault.byAccountExpiresDay;
        LoginCtrl.byAccountDisabled = 0;
        LoginCtrl.byPasswordExpires[0] = 85;
        LoginCtrl.byPasswordExpires[1] = 01;
        LoginCtrl.byPasswordExpires[2] = 01;
        LoginCtrl.byGraceLogins = UserDefault.byGraceLoginReset;
        LoginCtrl.wPasswordInterval = UserDefault.wPasswordInterval;
        LoginCtrl.byGraceLoginReset = UserDefault.byGraceLoginReset;
        LoginCtrl.byMinPasswordLength = UserDefault.byMinPasswordLength;
        LoginCtrl.wMaxConnections = UserDefault.wMaxConnections;
        LoginCtrl.byRestrictions = UserDefault.byRestrictions;
        LoginCtrl.byUnused = 0;
        LoginCtrl.lMaxDiskBlocks = UserDefault.lMaxDiskBlocks;
        LoginCtrl.wBadLogins = 0;
        LoginCtrl.lNextResetTime = 0;

        for (i = 0; i < 42; i++) {
            LoginCtrl.byLoginTimes[i] = UserDefault.byLoginTimes[i];
        }

        for (i = 0; i < 6; i++) {
            LoginCtrl.byLastLogin[i] = 0;
        }

        for (i = 0; i < 12; i++) {
            LoginCtrl.byBadLoginAddr[i] = 0;
        }


        LoginCtrl.byGraceLogins = LoginCtrl.byGraceLoginReset;

        //
        // Put default values into AccountBalance.
        //

        AccountBalance.lBalance = UserDefault.lBalance;
        AccountBalance.lCreditLimit = UserDefault.lCreditLimit;

        //
        // Write LOGIN_CONTROL property.
        //

        hr = NWApiWriteProperty(
                 pNwUserInfo->hConn,
                 pNwUserInfo->lpszUserName,
                 OT_USER,
                 NW_PROP_LOGIN_CONTROL,
                 (LPBYTE) &LoginCtrl
                 );
        BAIL_ON_FAILURE(hr);

        //
        // Write ACCOUNT_BALANCE property.
        //

        hr = NWApiWriteProperty(
                 pNwUserInfo->hConn,
                 pNwUserInfo->lpszUserName,
                 OT_USER,
                 NW_PROP_ACCOUNT_BALANCE,
                 (LPBYTE) &AccountBalance
                 );
        BAIL_ON_FAILURE(hr);
    }

    //
    // SUPERVISOR may not exist, or may not have USER_DEFAULTS.
    // This is okay.
    //
    hr = S_OK;

error:

    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreateGroup
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreateGroup(
    POBJECTINFO pObjectInfo
    )
{
    HRESULT       hr = S_OK;
    NWCONN_HANDLE hConn = NULL;

    //
    // Open a handle to the bindery.
    //

    hr = NWApiGetBinderyHandle(
             &hConn,
             pObjectInfo->ComponentArray[0]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Create a group bindery object.
    //

    hr = NWApiCreateBinderyObject(
             hConn,
             pObjectInfo->ComponentArray[1],
             OT_USER_GROUP,
             BF_STATIC,
             BS_LOGGED_READ | BS_SUPER_WRITE
             );
    BAIL_ON_FAILURE(hr);

    //
    // Create GROUP_MEMBERS property.
    //

    hr = NWApiCreateProperty(
             hConn,
             pObjectInfo->ComponentArray[1],
             OT_USER_GROUP,
             NW_PROP_GROUP_MEMBERS,
             BF_SET
             );
    BAIL_ON_FAILURE(hr);

error:

    NWApiReleaseBinderyHandle(hConn);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiDeleteGroup
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiDeleteGroup(
    POBJECTINFO pObjectInfo
    )
{
    HRESULT       hr = S_OK;
    NWCONN_HANDLE hConn = NULL;

    //
    // Open a handle to the bindery.
    //

    hr = NWApiGetBinderyHandle(
             &hConn,
             pObjectInfo->ComponentArray[0]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Delete the group object.
    //

    hr = NWApiDeleteBinderyObject(
             hConn,
             pObjectInfo->ComponentArray[1],
             OT_USER_GROUP
             );
    BAIL_ON_FAILURE(hr);

error:

    NWApiReleaseBinderyHandle(hConn);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreatePrinter
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreatePrinter(
    POBJECTINFO pObjectInfo
    )
{
    CHAR          szQueueName[(OBJ_NAME_SIZE + 1)*2];
    HRESULT       hr = S_OK;
    NWCCODE       usRet = SUCCESSFUL;
    NWCONN_HANDLE hConn = NULL;
    WCHAR         szTemp[MAX_PATH];

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(pObjectInfo->ComponentArray[1]) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        pObjectInfo->ComponentArray[1],
        szQueueName,
        0
        );

    //
    // Open a handle to the bindery.
    //

    hr = NWApiGetBinderyHandle(
             &hConn,
             pObjectInfo->ComponentArray[0]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Create a print queue object.
    //

    hr = NWApiCreatePrintQueue(
             hConn,
             pObjectInfo->ComponentArray[1]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Change property security.
    //

    usRet = NWCChangePropertySecurity(
                hConn,
                szQueueName,
                OT_PRINT_QUEUE,
                NW_PROP_Q_OPERATORS,
                BS_LOGGED_READ | BS_SUPER_WRITE
                );
    hr = HRESULT_FROM_NWCCODE(usRet);
    BAIL_ON_FAILURE(hr);

    //
    // Add SUPERVISOR to Q_OPERATORS.
    // (okay if this fails, maybe SUPERVISOR doesn't exist)
    //

    usRet = NWCAddObjectToSet(
                hConn,
                szQueueName,
                OT_PRINT_QUEUE,
                NW_PROP_Q_OPERATORS,
                NW_PROP_SUPERVISOR,
                OT_USER
                );

    //
    // Add EVERYONE to Q_USERS.
    // (okay if this fails, maybe EVERYONE doesn't exist)    
    //

    usRet = NWCAddObjectToSet(
                hConn,
                szQueueName,
                OT_PRINT_QUEUE,
                NW_PROP_Q_USERS,
                NW_PROP_EVERYONE,
                OT_USER_GROUP
                );

    //
    // Return.
    //

error:

    NWApiReleaseBinderyHandle(hConn);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiDeleteGroup
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiDeletePrinter(
    POBJECTINFO pObjectInfo
    )
{
    HRESULT       hr = S_OK;
    NWCONN_HANDLE hConn = NULL;
    NWOBJ_ID      dwQueueID = 0;

    //
    // Open a handle to the bindery.
    //

    hr = NWApiGetBinderyHandle(
             &hConn,
             pObjectInfo->ComponentArray[0]
             );
    BAIL_ON_FAILURE(hr);

    //
    // Get Queue ID.
    //

    hr = NWApiDestroyPrintQueue(
             hConn,
             pObjectInfo->ComponentArray[1]
             );
    BAIL_ON_FAILURE(hr);

error:

    NWApiReleaseBinderyHandle(hConn);

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiCreatePrintQueue
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiCreatePrintQueue(
    NWCONN_HANDLE hConn,
    LPWSTR lpszQueueName
    )
{
    CHAR    szQueueName[(OBJ_NAME_SIZE + 1)*2];
    DWORD   dwQueueID = 0;
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Convert BSTR into an ANSI representation required by NWC APIs.  "0" is
    // passed to UnicodeToAnsiString when the length of the string is unknown.
    //
    if (wcslen(lpszQueueName) > OBJ_NAME_SIZE) {
        RRETURN(E_INVALIDARG);
    }    

    UnicodeToAnsiString(
        lpszQueueName,
        szQueueName,
        0
        );

    //
    // Create a print queue object.
    //

    usRet = NWCCreateQueue(
                hConn,
                NULL,
                szQueueName,
                OT_PRINT_QUEUE,
                NW_PRINTER_PATH,
                &dwQueueID
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    //
    // Return.
    //

    RRETURN(hr);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiDestroyPrintQueue
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiDestroyPrintQueue(
    NWCONN_HANDLE hConn,
    LPWSTR lpszQueueName
    )
{
    DWORD   dwQueueID = 0;
    HRESULT hr = S_OK;
    NWCCODE usRet = SUCCESSFUL;

    //
    // Get Queue ID.
    //

    hr = NWApiGetObjectID(
             hConn,
             lpszQueueName,
             OT_PRINT_QUEUE,
             &dwQueueID
             );
    BAIL_ON_FAILURE(hr);

    //
    // Destroy print queue.
    //

    usRet = NWCDestroyQueue(
                hConn,
                dwSWAP(dwQueueID)
                );
    hr = HRESULT_FROM_NWCCODE(usRet);

    //
    // Return.
    //

error:

    RRETURN(hr);
}


//----------------------------------------------------------------------------
//
//  Function: NWApiMapNtStatusToDosError
//
//  Synopsis: This function maps the ntstatus that was returned from the NetWare
//            redirector to window errors. Similar to RtlNtStatusToDosError
//            except that the special handling is done to ntstatus with netware
//            facility codes.
//
//  Argument: NtStatus - The ntstatus returned from NetWare rdr
//
//  Return Value: WinError
//
//----------------------------------------------------------------------------
DWORD
NWApiMapNtStatusToDosError(
    IN NTSTATUS NtStatus
    )
{
    if ( (HIWORD( NtStatus) & FACILITY_NWRDR ) == FACILITY_NWRDR )
    {
        if ( NtStatus == NWRDR_PASSWORD_HAS_EXPIRED )
            return ERROR_PASSWORD_EXPIRED;
        else
            return NETWARE_GENERAL_ERROR;
    }
    else if ( HIWORD( NtStatus) == 0xC001 )
    {
        return LOWORD( NtStatus ) + NETWARE_ERROR_BASE;
    }

    return RtlNtStatusToDosError( NtStatus );
}

//----------------------------------------------------------------------------
//
//  Function: NWApiConvertToAddressFormat
//
//  Synopsis: Convert an IPX address obtain from NWApiGetProperty into the
//            format specified in spec.
//
//----------------------------------------------------------------------------
HRESULT
NWApiConvertToAddressFormat(
    LP_RPLY_SGMT_LST lpReplySegment,
    LPWSTR *lppszAddresses
    )
{
    int    i = 0;
    LPBYTE lpBuffer = NULL;
    LPWSTR lpszTemp = NULL;
    WORD   wSegment[NET_ADDRESS_WORD_SIZE];

    //
    // Put values from szReply into the wSegment array
    //

    lpBuffer = (LPBYTE) lpReplySegment->Segment;

    for (i = 0; i < NET_ADDRESS_WORD_SIZE; i++) {

        wSegment[i] = NWApiReverseWORD(*((LPWORD)lpBuffer + i));
    }

    //
    // Put address together in the format described in spec.
    //

    lpszTemp = (LPWSTR) AllocADsMem((NET_ADDRESS_NUM_CHAR+1)*sizeof(WCHAR));
    if (!lpszTemp) {
        RRETURN(E_OUTOFMEMORY);
    }

    wsprintf(
        lpszTemp,
        L"%s:%04X%04X.%04X%04X%04X.%04X",
        bstrAddressTypeString,
        wSegment[0],
        wSegment[1],
        wSegment[2],
        wSegment[3],
        wSegment[4],
        wSegment[5]
        );

    //
    // Return.
    //
    *lppszAddresses = lpszTemp;

    RRETURN(S_OK);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiMakeVariantTime
//
//  Synopsis: This function creates a double precision variant time.
//
//----------------------------------------------------------------------------
HRESULT
NWApiMakeVariantTime(
    DOUBLE * pdTime,
    WORD wDay,         // Day = 1..31
    WORD wMonth,       // Month = 1..12
    WORD wYear,        // Year = (19XX or 20XX) - 1980, ie. 2019 -> 39
    WORD wSecond,      // Second = 0..30, Second divided by 2
    WORD wMinute,      // Minute = 0..59
    WORD wHour         // Hour = 0..23
    )
{
    BOOL   fBool = TRUE;
    DOUBLE vTime = 0;
    WORD   wDOSDate = 0;
    WORD   wDOSTime = 0;

    //
    // Fix up parameters.
    // If wDay and wMonth are 0, turn them into one.
    //

    if (wDay == 0) {
        wDay++;
    }

    if (wMonth == 0) {
        wMonth++;
    }

    //
    // Shift data to correct bit as required by the DOS date & time format.
    //

    wMonth = wMonth << 5;
    wYear =  wYear << 9;
    wMinute = wMinute << 5;
    wHour = wHour << 11;

    //
    // Put them in DOS format.
    //

    wDOSDate = wYear | wMonth | wDay;
    wDOSTime = wHour | wMinute | wSecond;

    //
    // Convert into VariantTime.
    //

    fBool = DosDateTimeToVariantTime(
                wDOSDate,
                wDOSTime,
                &vTime
                );

    //
    // Return.
    //

    if (fBool == TRUE) {

        *pdTime = vTime;

        RRETURN(S_OK);
    }
    else {
        RRETURN(E_FAIL);
    }
}

//----------------------------------------------------------------------------
//
//  Function: NWApiBreakVariantTime
//
//  Synopsis: This function interprets a double precision variant time and
//            returns the day, month and year individually.
//
//----------------------------------------------------------------------------
HRESULT
NWApiBreakVariantTime(
    DOUBLE daDate,
    PWORD pwDay,
    PWORD pwMonth,
    PWORD pwYear
    )
{
    BOOL   fBool;
    DOUBLE vTime;
    WORD   wDOSDate = 0;
    WORD   wDOSTime = 0;
    WORD   wDay = 0;
    WORD   wMonth = 0;
    WORD   wYear = 0;

    //
    // Convert variant time into DOS format.
    //

    fBool = VariantTimeToDosDateTime(
                daDate,
                &wDOSDate,
                &wDOSTime
                );
    if (fBool == FALSE) {
        goto error;
    }

    //
    // Year: bits 9-15, add 80 to wYear because 80 was subtracted from it to
    // call VariantTimeToDosDateTime.
    //

    wYear = wDOSDate >> 9;
    wYear += 80;

    //
    // Month: bits 5-8.
    //

    wMonth = (wDOSDate >> 5) - (wYear << 4);

    //
    // Day: bits 0-4.
    //

    wDay = wDOSDate - (wMonth << 5) - (wYear << 9);

    //
    // Return.
    //

    *pwDay = wDay;
    *pwMonth = wMonth;
    *pwYear = wYear;

    RRETURN(S_OK);

error:

    RRETURN(E_FAIL);
}

//----------------------------------------------------------------------------
//
//  Function: NWApiReverseWORD
//
//  Synopsis: This function reverse a WORD.
//
//----------------------------------------------------------------------------
WORD
NWApiReverseWORD(
    WORD wWORD
    )
{

    LPBYTE lpbTemp = (LPBYTE) &wWORD;
    BYTE bTemp;

    bTemp = *lpbTemp;
    *lpbTemp = *(lpbTemp + 1);
    *(lpbTemp + 1) = bTemp;

    return(*((LPWORD) lpbTemp));
}


//----------------------------------------------------------------------------
//
//  Function: NWApiUserGetGroups
//
//  Synopsis:
//
//----------------------------------------------------------------------------
HRESULT
NWApiUserGetGroups(
    NWCONN_HANDLE hConn,
    LPWSTR szUserName,
    LPBYTE *lppBuffer
    )
{
    DWORD   dwNumSegment = 0;
    HRESULT hr = S_OK;
    DWORD   i;
    LP_RPLY_SGMT_LST lpTemp = NULL;
    LP_RPLY_SGMT_LST lpReplySegment = NULL;

    //
    // Assert
    //

    ADsAssert(*lppBuffer == NULL);

    //
    // Get GROUP_MEMBERS.
    //

    hr = NWApiGetProperty(
             szUserName,
             NW_PROP_USER_GROUPS,
             OT_USER,
             hConn,
             &lpReplySegment,
             &dwNumSegment
             );
    BAIL_ON_FAILURE(hr);

    //
    // Pack returned linked list into buffer.
    //

    *lppBuffer = (LPBYTE) AllocADsMem(
                           dwNumSegment * REPLY_VALUE_SIZE
                           );
    if (!(*lppBuffer)) {
        RRETURN(E_OUTOFMEMORY);
    }

    lpTemp = lpReplySegment;

    for (i = 0; i < dwNumSegment; i++) {
        memcpy(
            *lppBuffer + i * REPLY_VALUE_SIZE,
            lpTemp->Segment,
            REPLY_VALUE_SIZE
            );
        lpTemp = lpTemp->lpNext;
    }

error:

    //
    // Clean up.
    //

    lpTemp = NULL;

    if (lpReplySegment) {
        DELETE_LIST(lpReplySegment);
    }

    RRETURN(hr);
}