Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1044 lines
28 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
purchase.c
Abstract:
Author:
Arthur Hanson (arth) 03-Jan-1995
Revision History:
Jeff Parham (jeffparh) 05-Dec-1995
o Added support for uniting per seat and per server purchase models.
o Added extra parameters and code to support secure certificates and
certificate database.
--*/
#include <stdlib.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <dsgetdc.h>
#include <debug.h>
#include "llsevent.h"
#include "llsapi.h"
#include "llsutil.h"
#include "llssrv.h"
#include "mapping.h"
#include "msvctbl.h"
#include "purchase.h"
#include "svctbl.h"
#include "perseat.h"
#include "registry.h"
#include "llsrpc_s.h"
#include "certdb.h"
#include "server.h"
#include <strsafe.h> //include last
//
// Initialized in ReplicationInit in repl.c.
//
extern PLLS_CONNECT_W pLlsConnectW;
extern PLLS_CLOSE pLlsClose;
extern PLLS_LICENSE_ADD_W pLlsLicenseAddW;
extern HANDLE g_hThrottleConnect;
ULONG LicenseServiceListSize = 0;
PLICENSE_SERVICE_RECORD *LicenseServiceList = NULL;
ULONG PerServerLicenseServiceListSize = 0;
PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList = NULL;
PLICENSE_PURCHASE_RECORD PurchaseList = NULL;
ULONG PurchaseListSize = 0;
RTL_RESOURCE LicenseListLock;
static
NTSTATUS
LicenseAdd_UpdateQuantity(
LPTSTR ServiceName,
LONG Quantity,
BOOL UsePerServerList,
PLICENSE_SERVICE_RECORD * ppService,
BOOL * pChangeLicense,
LONG * pNewLicenses,
PMASTER_SERVICE_RECORD * pmService
);
NTSTATUS
ReplicationInitDelayed();
/////////////////////////////////////////////////////////////////////////
NTSTATUS
LicenseListInit()
/*++
Routine Description:
Arguments:
None.
Return Value:
None.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
try
{
RtlInitializeResource(&LicenseListLock);
} except(EXCEPTION_EXECUTE_HANDLER ) {
status = GetExceptionCode();
}
return status;
} // LicenseListInit
/////////////////////////////////////////////////////////////////////////
int __cdecl LicenseServiceListCompare(const void *arg1, const void *arg2) {
PLICENSE_SERVICE_RECORD Svc1, Svc2;
Svc1 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg1);
Svc2 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg2);
return lstrcmpi( Svc1->ServiceName, Svc2->ServiceName);
} // LicenseServiceListCompare
/////////////////////////////////////////////////////////////////////////
PLICENSE_SERVICE_RECORD
LicenseServiceListFind(
LPTSTR ServiceName,
BOOL UsePerServerList
)
/*++
Routine Description:
Arguments:
ServiceName -
(JeffParh 95-10-31)
UsePerServerList - Determines whether the license record is searched for
in the per seat list (as in 3.51) or in the per server list (new for
SUR). The license purchase models are now unified, so there is now
a purchase history for both per seat and per server licenses.
Return Value:
Pointer to found service table entry or NULL if not found.
--*/
{
LONG begin = 0;
LONG end = (LONG) LicenseServiceListSize - 1;
LONG cur;
int match;
PLICENSE_SERVICE_RECORD Service;
PLICENSE_SERVICE_RECORD * l_pServiceList;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseServiceListFind\n"));
#endif
if (ServiceName == NULL)
return NULL;
if ( UsePerServerList )
{
end = PerServerLicenseServiceListSize - 1;
l_pServiceList = PerServerLicenseServiceList;
}
else
{
end = LicenseServiceListSize - 1;
l_pServiceList = LicenseServiceList;
}
while (end >= begin) {
// go halfway in-between
cur = (begin + end) / 2;
Service = l_pServiceList[cur];
// compare the two result into match
match = lstrcmpi(ServiceName, Service->ServiceName);
if (match < 0)
// move new begin
end = cur - 1;
else
begin = cur + 1;
if (match == 0)
return Service;
}
return NULL;
} // LicenseServiceListFind
/////////////////////////////////////////////////////////////////////////
PLICENSE_SERVICE_RECORD
LicenseServiceListAdd(
LPTSTR ServiceName,
BOOL UsePerServerList
)
/*++
Routine Description:
Arguments:
ServiceName -
(JeffParh 95-10-31)
UsePerServerList - Determines whether the license record is added to
the per seat list (as in 3.51) or the per server list (new for
SUR). The license purchase models are now unified, so there is now
a purchase history for both per seat and per server licenses.
Return Value:
Pointer to added service table entry, or NULL if failed.
--*/
{
PLICENSE_SERVICE_RECORD Service;
LPTSTR NewServiceName;
PLICENSE_SERVICE_RECORD ** pServiceList;
LPDWORD pServiceListSize;
PLICENSE_SERVICE_RECORD * pServiceListTmp;
HRESULT hr;
size_t cch;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseServiceListAdd\n"));
#endif
//
// We do a double check here to see if another thread just got done
// adding the service, between when we checked last and actually got
// the write lock.
//
Service = LicenseServiceListFind(ServiceName, UsePerServerList);
if (Service != NULL) {
return Service;
}
if ( UsePerServerList )
{
pServiceList = &PerServerLicenseServiceList;
pServiceListSize = &PerServerLicenseServiceListSize;
}
else
{
pServiceList = &LicenseServiceList;
pServiceListSize = &LicenseServiceListSize;
}
//
// Allocate space for table (zero init it).
//
if (*pServiceList == NULL)
pServiceListTmp = (PLICENSE_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1));
else
pServiceListTmp = (PLICENSE_SERVICE_RECORD *) LocalReAlloc(*pServiceList, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1), LHND);
//
// Make sure we could allocate service table
//
if (pServiceListTmp == NULL) {
return NULL;
} else {
*pServiceList = pServiceListTmp;
}
Service = (PLICENSE_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_SERVICE_RECORD));
if (Service == NULL) {
ASSERT(FALSE);
return NULL;
}
//
// Create space for saving off the name.
//
cch = lstrlen(ServiceName) + 1;
NewServiceName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewServiceName == NULL) {
ASSERT(FALSE);
LocalFree(Service);
return NULL;
}
// now copy it over...
Service->ServiceName = NewServiceName;
hr = StringCchCopy(NewServiceName, cch, ServiceName);
ASSERT(SUCCEEDED(hr));
(*pServiceList)[*pServiceListSize] = Service;
Service->NumberLicenses = 0;
Service->Index = *pServiceListSize;
(*pServiceListSize)++;
// Have added the entry - now need to sort it in order of the service names
qsort((void *) *pServiceList, (size_t) *pServiceListSize, sizeof(PLICENSE_SERVICE_RECORD), LicenseServiceListCompare);
return Service;
} // LicenseServiceListAdd
/////////////////////////////////////////////////////////////////////////
ULONG
ProductLicensesGet(
LPTSTR ServiceName,
BOOL UsePerServerList
)
/*++
Routine Description:
Arguments:
ServiceName -
(JeffParh 95-10-31)
UsePerServerList - Determines whether the number of licenses is retirved
from the per seat list (as in 3.51) or the per server list (new for
SUR). The license purchase models are now unified, so there is now
a purchase history for both per seat and per server licenses.
Return Value:
--*/
{
PLICENSE_SERVICE_RECORD Service;
ULONG NumLicenses = 0;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: ProductLicenseGet\n"));
#endif
//
// Try to find the service.
//
RtlAcquireResourceShared(&LicenseListLock, TRUE);
Service = LicenseServiceListFind(ServiceName, UsePerServerList);
if (Service != NULL)
NumLicenses = Service->NumberLicenses;
RtlReleaseResource(&LicenseListLock);
return NumLicenses;
} // ProductLicensesGet
/////////////////////////////////////////////////////////////////////////
NTSTATUS
LicenseAdd(
LPTSTR ServiceName,
LPTSTR Vendor,
LONG Quantity,
DWORD MaxQuantity,
LPTSTR Admin,
LPTSTR Comment,
DWORD Date,
DWORD AllowedModes,
DWORD CertificateID,
LPTSTR Source,
DWORD ExpirationDate,
LPDWORD Secrets
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
static DWORD NullSecrets[ LLS_NUM_SECRETS ] = { 0, 0, 0, 0 };
static DWORD ConnectErr = STATUS_SUCCESS;
BOOL ChangeLicense = FALSE;
PLICENSE_SERVICE_RECORD Service = NULL;
PLICENSE_PURCHASE_RECORD PurchaseRecord;
LONG NewLicenses = 0;
NTSTATUS Status;
BOOL PerServerChangeLicense = FALSE;
PLICENSE_SERVICE_RECORD PerServerService = NULL;
LONG PerServerNewLicenses = 0;
LPTSTR NewName,NewComment,NewSource,NewVendor;
PMASTER_SERVICE_RECORD mService = NULL;
LLS_LICENSE_INFO_1 lic;
DWORD dwWait;
HRESULT hr;
size_t cch;
#if DBG
if (TraceFlags & TRACE_FUNCTION_TRACE)
dprintf(TEXT("LLS TRACE: LicenseAdd\n"));
#endif
//init
ZeroMemory(&lic, sizeof(lic));
if ( ( 0 == CertificateID ) && ( ServiceIsSecure( ServiceName ) ) )
{
return STATUS_ACCESS_DENIED;
}
if ( ( 0 != ExpirationDate ) && ( ExpirationDate < DateSystemGet() ) )
{
// certificate has expired
return STATUS_ACCOUNT_EXPIRED;
}
if ( ( NULL == ServiceName )
|| ( NULL == Vendor )
|| ( 0 == Quantity )
|| ( ( 0 != CertificateID ) && ( 0 == MaxQuantity ) )
|| ( NULL == Admin )
|| ( NULL == Comment )
|| ( 0 == ( AllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) ) )
|| ( NULL == Source ) )
{
// invalid parameter
return STATUS_INVALID_PARAMETER;
}
if ( NULL == Secrets )
{
Secrets = NullSecrets;
}
//
// ** NEW - NT 5.0 **
//
// All per seat purchase requests are deferred to the site license master
// server. Per server is still handled individually at each server.
//
if ( AllowedModes & 1 ) {
//
// Update enterprise information, in case the site license master
// has changed.
//
#if DELAY_INITIALIZATION
EnsureInitialized();
#endif
ConfigInfoUpdate(NULL,FALSE);
//
// Connect to the site license master server, if this server is
// not the master.
//
RtlEnterCriticalSection(&ConfigInfoLock);
if ( !ConfigInfo.IsMaster && (ConfigInfo.SiteServer != NULL)) {
// Make sure function pointers are initialized
Status = ReplicationInitDelayed();
if (STATUS_SUCCESS != ConnectErr)
{
dwWait = WaitForSingleObject(g_hThrottleConnect, 0);
if (dwWait == WAIT_TIMEOUT)
{
// We've already tried in the past 15 minutes
Status = ConnectErr;
RtlLeaveCriticalSection(&ConfigInfoLock);
return Status;
}
else
{
ConnectErr = STATUS_SUCCESS;
}
}
if ((NOERROR == Status) && ( pLlsConnectW != NULL )) {
LLS_HANDLE LlsHandle;
Status = (*pLlsConnectW)(ConfigInfo.SiteServer,
&LlsHandle);
if ( Status == STATUS_SUCCESS ) {
LLS_LICENSE_INFO_0 LicenseInfo0;
LicenseInfo0.Product = ServiceName;
LicenseInfo0.Quantity = Quantity;
LicenseInfo0.Date = Date;
LicenseInfo0.Admin = Admin;
LicenseInfo0.Comment = Comment;
Status = (*pLlsLicenseAddW)(LlsHandle,
0,
(LPBYTE)&LicenseInfo0);
(*pLlsClose)(LlsHandle);
}
else {
//
// Connection failed
// Don't allow any more connections for a while
//
ConnectErr = Status;
}
}
else {
if (NOERROR == Status)
{
//
// Not the best error, but we must return something should
// this obscure error condition become true.
//
Status = STATUS_INVALID_PARAMETER;
}
}
RtlLeaveCriticalSection(&ConfigInfoLock);
return Status;
}
RtlLeaveCriticalSection(&ConfigInfoLock);
}
RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
if ( 0 != CertificateID )
{
lic.Product = ServiceName;
lic.Vendor = Vendor;
lic.Quantity = Quantity;
lic.MaxQuantity = MaxQuantity;
lic.Admin = Admin;
lic.Comment = Comment;
lic.Date = Date;
lic.AllowedModes = AllowedModes;
lic.CertificateID = CertificateID;
lic.Source = Source;
lic.ExpirationDate = ExpirationDate;
memcpy( lic.Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
if ( !CertDbClaimApprove( &lic ) )
{
// no way, hoser!
RtlReleaseResource( &LicenseListLock );
return STATUS_OBJECT_NAME_EXISTS;
}
}
// update totals for per seat / per server mode licenses
Status = STATUS_SUCCESS;
if ( AllowedModes & 1 )
{
// per seat allowed; add to per seat license tally
Status = LicenseAdd_UpdateQuantity( ServiceName,
Quantity,
FALSE,
&Service,
&ChangeLicense,
&NewLicenses,
&mService );
}
if ( ( STATUS_SUCCESS == Status ) && ( AllowedModes & 2 ) )
{
// per server allowed; add to per server license tally
Status = LicenseAdd_UpdateQuantity( ServiceName,
Quantity,
TRUE,
&PerServerService,
&PerServerChangeLicense,
&PerServerNewLicenses,
&mService );
}
if ( STATUS_SUCCESS != Status )
{
RtlReleaseResource( &LicenseListLock );
return Status;
}
//
// Service now points to the service table entry - now update purchase
// History.
//
//
// First allocate members, then new struct
//
//
// Create space for saving off the Admin Name
//
cch = lstrlen(Admin) + 1;
NewName = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewName == NULL)
{
ASSERT(FALSE);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
// now copy it over...
hr = StringCchCopy(NewName, cch, Admin);
ASSERT(SUCCEEDED(hr));
//
// Create space for saving off the Comment
//
cch = lstrlen(Comment) + 1;
NewComment = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewComment == NULL)
{
ASSERT(FALSE);
LocalFree(NewName);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
// now copy it over...
hr = StringCchCopy(NewComment, cch, Comment);
ASSERT(SUCCEEDED(hr));
//
// Create space for saving off the Source
//
cch = lstrlen(Source) + 1;
NewSource = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewSource == NULL)
{
ASSERT(FALSE);
LocalFree(NewName);
LocalFree(NewComment);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
// now copy it over...
hr = StringCchCopy(NewSource, cch, Source);
ASSERT(SUCCEEDED(hr));
//
// Create space for saving off the Vendor
//
cch = lstrlen(Vendor) + 1;
NewVendor = (LPTSTR) LocalAlloc(LPTR, cch * sizeof(TCHAR));
if (NewVendor == NULL)
{
ASSERT(FALSE);
LocalFree(NewName);
LocalFree(NewComment);
LocalFree(NewSource);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
// now copy it over...
hr = StringCchCopy(NewVendor, cch, Vendor);
ASSERT(SUCCEEDED(hr));
if (PurchaseList == NULL)
PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1));
else
{
PLICENSE_PURCHASE_RECORD PurchaseListTemp = NULL;
PurchaseListTemp = (PLICENSE_PURCHASE_RECORD) LocalReAlloc(PurchaseList, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1), LHND);
if (PurchaseListTemp != NULL)
{
PurchaseList = PurchaseListTemp;
} else
{
ASSERT(FALSE);
LocalFree(NewName);
LocalFree(NewComment);
LocalFree(NewSource);
LocalFree(NewVendor);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
}
//
// Make sure we could allocate service table
//
if (PurchaseList == NULL)
{
ASSERT(FALSE);
PurchaseListSize = 0;
LocalFree(NewName);
LocalFree(NewComment);
LocalFree(NewSource);
LocalFree(NewVendor);
RtlReleaseResource(&LicenseListLock);
return STATUS_NO_MEMORY;
}
PurchaseRecord = &PurchaseList[PurchaseListSize];
PurchaseRecord->Admin = NewName;
PurchaseRecord->Comment = NewComment;
PurchaseRecord->Source = NewSource;
PurchaseRecord->Vendor = NewVendor;
//
// Update the rest of the stuff.
//
PurchaseRecord->NumberLicenses = Quantity;
PurchaseRecord->MaxQuantity = MaxQuantity;
PurchaseRecord->Service = Service;
PurchaseRecord->PerServerService = PerServerService;
PurchaseRecord->AllowedModes = AllowedModes;
PurchaseRecord->CertificateID = CertificateID;
PurchaseRecord->ExpirationDate = ExpirationDate;
memcpy( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) );
if (Date == 0)
PurchaseRecord->Date = DateSystemGet();
else
PurchaseRecord->Date = Date;
PurchaseListSize++;
RtlReleaseResource(&LicenseListLock);
if ( 0 != CertificateID )
{
// these should still be set from above
ASSERT( lic.Product == ServiceName );
ASSERT( lic.Vendor == Vendor );
ASSERT( lic.Quantity == Quantity );
ASSERT( lic.MaxQuantity == MaxQuantity );
ASSERT( lic.Admin == Admin );
ASSERT( lic.Comment == Comment );
ASSERT( lic.Date == Date );
ASSERT( lic.AllowedModes == AllowedModes );
ASSERT( lic.CertificateID == CertificateID );
ASSERT( lic.Source == Source );
ASSERT( lic.ExpirationDate == ExpirationDate );
ASSERT( 0 == memcmp( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ) );
CertDbClaimEnter( NULL, &lic, FALSE, 0 );
}
//
// Now check if we need to re-scan the user list and update licenses
//
if (ChangeLicense)
{
if ( NewLicenses < 0 )
UserListLicenseDelete( mService, NewLicenses );
if ( NewLicenses > 0 )
FamilyLicenseUpdate ( mService->Family );
}
if ( AllowedModes & 2 )
{
// per server licenses modified
LocalServiceListConcurrentLimitSet();
LocalServerServiceListUpdate();
ServiceListResynch();
}
return STATUS_SUCCESS;
} // LicenseAdd
/////////////////////////////////////////////////////////////////////////
static
NTSTATUS
LicenseAdd_UpdateQuantity(
LPTSTR ServiceName,
LONG Quantity,
BOOL UsePerServerList,
PLICENSE_SERVICE_RECORD * ppService,
BOOL * pChangeLicense,
LONG * pNewLicenses,
PMASTER_SERVICE_RECORD * pmService
)
{
BOOL ChangeLicense = FALSE;
PLICENSE_SERVICE_RECORD Service;
PMASTER_SERVICE_RECORD mService;
LONG NewLicenses = 0;
Service = LicenseServiceListFind( ServiceName, UsePerServerList );
//
// If we didn't find it we will need to add it.
//
if (Service == NULL)
{
if (Quantity < 0)
{
#if DBG
dprintf(TEXT("Releasing Licenses from Non-existant product!\n"));
#endif
// ASSERT(FALSE);
return STATUS_UNSUCCESSFUL;
}
else
{
Service = LicenseServiceListAdd(ServiceName, UsePerServerList);
}
}
//
// Check to make sure we found or added it. The only way we can fail is
// if we couldn't alloc memory for it.
//
if (Service == NULL)
{
ASSERT(FALSE);
return STATUS_NO_MEMORY;
}
if (((LONG) Service->NumberLicenses + Quantity) < 0)
{
return STATUS_UNSUCCESSFUL;
}
//
// Update license count in service record
//
Service->NumberLicenses += Quantity;
//
// Now in master Service Record
//
RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
mService = MasterServiceListFind(ServiceName);
if (mService != NULL)
{
//
// if we were out of license and added more, then we need to update
// the user list.
//
if ( ( Quantity > 0 )
&& ( (mService->LicensesUsed > mService->Licenses) || (mService == BackOfficeRec) ) )
{
ChangeLicense = TRUE;
}
//
// Can only add a number of licenses up to licensed amount
//
if ( ChangeLicense )
{
// Get current unlicensed delta
NewLicenses = mService->LicensesUsed - mService->Licenses;
if ((NewLicenses <= 0) || (NewLicenses > Quantity))
{
NewLicenses = Quantity;
}
}
if ( UsePerServerList )
{
// this will be done by LicenseAdd() in LocalServerServiceListUpdate()
// mService->MaxSessionCount += Quantity;
}
else
{
mService->Licenses += Quantity;
}
//
// if we we subtracted licenses and are out of licenses, then we
// need to scan the user list.
//
if (Quantity < 0)
{
if (mService->LicensesUsed > mService->Licenses)
{
ChangeLicense = TRUE;
//
// Only remove # of licenses past limit
//
NewLicenses = mService->Licenses - mService->LicensesUsed;
if (NewLicenses < Quantity)
{
NewLicenses = Quantity;
}
}
}
}
RtlReleaseResource(&MasterServiceListLock);
ASSERT(NULL != ppService &&
NULL != pChangeLicense &&
NULL != pNewLicenses &&
NULL != pmService);
*ppService = Service;
*pChangeLicense = ChangeLicense;
*pNewLicenses = NewLicenses;
*pmService = mService;
return STATUS_SUCCESS;
}
#if DBG
/////////////////////////////////////////////////////////////////////////
VOID
LicenseListDebugDump( )
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
ULONG i = 0;
ULONG j = 0;
HRESULT hr;
//
// Need to scan list so get read access.
//
RtlAcquireResourceShared(&LicenseListLock, TRUE);
//
// Dump License Service List first
//
dprintf(TEXT("Per Seat License Service Table, # Entries: %lu\n"), LicenseServiceListSize);
if (LicenseServiceList != NULL)
{
for (i = 0; i < LicenseServiceListSize; i++)
dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, LicenseServiceList[i]->NumberLicenses, LicenseServiceList[i]->ServiceName);
}
dprintf(TEXT("\nPer Server License Service Table, # Entries: %lu\n"), PerServerLicenseServiceListSize);
if (PerServerLicenseServiceList != NULL)
{
for (i = 0; i < PerServerLicenseServiceListSize; i++)
dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, PerServerLicenseServiceList[i]->NumberLicenses, PerServerLicenseServiceList[i]->ServiceName);
}
//
// Now do purchase history
//
dprintf(TEXT("\nPurchase History, # Entries: %lu\n"), PurchaseListSize);
if (PurchaseList != NULL)
{
for (i = 0; i < PurchaseListSize; i++)
{
TCHAR szExpirationDate[ 40 ];
hr = StringCbCopy( szExpirationDate, sizeof(szExpirationDate), TimeToString( PurchaseList[i].ExpirationDate ) );
ASSERT(SUCCEEDED(hr));
dprintf( TEXT(" %3lu) Product : %s\n" )
TEXT( " Vendor : %s\n" )
TEXT( " Allowed Modes :%s%s\n" )
TEXT( " Licenses : %d\n" )
TEXT( " Max Licenses : %lu\n" )
TEXT( " Date Entered : %s\n" )
TEXT( " Date Expires : %s\n" )
TEXT( " Certificate ID : %lu\n" )
TEXT( " Secrets :" ),
i,
( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
? PurchaseList[i].Service->ServiceName
: PurchaseList[i].PerServerService->ServiceName,
PurchaseList[i].Vendor,
( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
? TEXT(" PERSEAT")
: TEXT(""),
( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
? TEXT(" PERSERVER")
: TEXT(""),
PurchaseList[i].NumberLicenses,
PurchaseList[i].MaxQuantity,
TimeToString( PurchaseList[i].Date ),
szExpirationDate,
PurchaseList[i].CertificateID
);
for ( j=0; j < LLS_NUM_SECRETS; j++ )
{
dprintf( TEXT( " %08X" ), PurchaseList[i].Secrets[j] );
}
dprintf( TEXT( "\n" )
TEXT( " Source : %s\n" )
TEXT( " Admin : %s\n" )
TEXT( " Comment : %s\n\n" ),
PurchaseList[i].Source,
PurchaseList[i].Admin,
PurchaseList[i].Comment
);
}
}
RtlReleaseResource(&LicenseListLock);
return;
} // LicenseListDebugDump
#endif //DBG