summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/lls
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/net/svcdlls/lls
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/net/svcdlls/lls')
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi.cpp354
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi.h110
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi32.def13
-rw-r--r--private/net/svcdlls/lls/ccfapi32/ccfapi32.rc346
-rw-r--r--private/net/svcdlls/lls/ccfapi32/exports.cpp382
-rw-r--r--private/net/svcdlls/lls/ccfapi32/imagelst.h38
-rw-r--r--private/net/svcdlls/lls/ccfapi32/licobj.cpp308
-rw-r--r--private/net/svcdlls/lls/ccfapi32/licobj.h76
-rw-r--r--private/net/svcdlls/lls/ccfapi32/makefile6
-rw-r--r--private/net/svcdlls/lls/ccfapi32/md4.h120
-rw-r--r--private/net/svcdlls/lls/ccfapi32/md4c.cpp291
-rw-r--r--private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp1386
-rw-r--r--private/net/svcdlls/lls/ccfapi32/nlicdlg.h102
-rw-r--r--private/net/svcdlls/lls/ccfapi32/paper.cpp1389
-rw-r--r--private/net/svcdlls/lls/ccfapi32/paper.h145
-rw-r--r--private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp154
-rw-r--r--private/net/svcdlls/lls/ccfapi32/pseatdlg.h62
-rw-r--r--private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp152
-rw-r--r--private/net/svcdlls/lls/ccfapi32/psrvdlg.h63
-rw-r--r--private/net/svcdlls/lls/ccfapi32/remdlg.cpp1364
-rw-r--r--private/net/svcdlls/lls/ccfapi32/remdlg.h105
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc224
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/license.icobin0 -> 1086 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/smicons.bmpbin0 -> 334 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/res/warning.icobin0 -> 766 bytes
-rw-r--r--private/net/svcdlls/lls/ccfapi32/resource.h105
-rw-r--r--private/net/svcdlls/lls/ccfapi32/source.cpp706
-rw-r--r--private/net/svcdlls/lls/ccfapi32/source.h83
-rw-r--r--private/net/svcdlls/lls/ccfapi32/sources51
-rw-r--r--private/net/svcdlls/lls/ccfapi32/srclist.cpp393
-rw-r--r--private/net/svcdlls/lls/ccfapi32/srclist.h47
-rw-r--r--private/net/svcdlls/lls/ccfapi32/stdafx.h46
-rw-r--r--private/net/svcdlls/lls/ccfapi32/utils.cpp713
-rw-r--r--private/net/svcdlls/lls/ccfapi32/utils.h93
-rw-r--r--private/net/svcdlls/lls/client/llsevent.mc176
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.c6600
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.def106
-rw-r--r--private/net/svcdlls/lls/client/llsrpc.rc30
-rw-r--r--private/net/svcdlls/lls/client/makefile6
-rw-r--r--private/net/svcdlls/lls/client/makefile.inc8
-rw-r--r--private/net/svcdlls/lls/client/ntver.rc53
-rw-r--r--private/net/svcdlls/lls/client/sources68
-rw-r--r--private/net/svcdlls/lls/common/debug.c19
-rw-r--r--private/net/svcdlls/lls/common/makefile6
-rw-r--r--private/net/svcdlls/lls/common/sources44
-rw-r--r--private/net/svcdlls/lls/dirs1
-rw-r--r--private/net/svcdlls/lls/inc/debug.h40
-rw-r--r--private/net/svcdlls/lls/inc/llsapi.h1441
-rw-r--r--private/net/svcdlls/lls/inc/llsconst.h14
-rw-r--r--private/net/svcdlls/lls/inc/llsimp.h24
-rw-r--r--private/net/svcdlls/lls/inc/lpcstub.h38
-rw-r--r--private/net/svcdlls/lls/inc/rpcutil.h76
-rw-r--r--private/net/svcdlls/lls/llscli.acf46
-rw-r--r--private/net/svcdlls/lls/llsdbg.idl113
-rw-r--r--private/net/svcdlls/lls/llsimp.idl47
-rw-r--r--private/net/svcdlls/lls/llsrpc.idl2041
-rw-r--r--private/net/svcdlls/lls/llssrv.acf57
-rw-r--r--private/net/svcdlls/lls/lsapi.idl88
-rw-r--r--private/net/svcdlls/lls/lsapicli.acf46
-rw-r--r--private/net/svcdlls/lls/lsapisrv.acf49
-rw-r--r--private/net/svcdlls/lls/lsdbgcli.acf46
-rw-r--r--private/net/svcdlls/lls/lsdbgsrv.acf49
-rw-r--r--private/net/svcdlls/lls/makefil0151
-rw-r--r--private/net/svcdlls/lls/ntlsapi/main.c57
-rw-r--r--private/net/svcdlls/lls/ntlsapi/makefile6
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.c1150
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.def19
-rw-r--r--private/net/svcdlls/lls/ntlsapi/ntlsapi.rc12
-rw-r--r--private/net/svcdlls/lls/ntlsapi/rpcstub.c399
-rw-r--r--private/net/svcdlls/lls/ntlsapi/sources66
-rw-r--r--private/net/svcdlls/lls/server/certdb.c1511
-rw-r--r--private/net/svcdlls/lls/server/certdb.h129
-rw-r--r--private/net/svcdlls/lls/server/llssrv.c1087
-rw-r--r--private/net/svcdlls/lls/server/llssrv.h109
-rw-r--r--private/net/svcdlls/lls/server/llssrv.rc12
-rw-r--r--private/net/svcdlls/lls/server/llsutil.c982
-rw-r--r--private/net/svcdlls/lls/server/llsutil.h63
-rw-r--r--private/net/svcdlls/lls/server/makefile6
-rw-r--r--private/net/svcdlls/lls/server/makefile.inc1
-rw-r--r--private/net/svcdlls/lls/server/mapping.c756
-rw-r--r--private/net/svcdlls/lls/server/mapping.h70
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.c712
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.h151
-rw-r--r--private/net/svcdlls/lls/server/pack.c4306
-rw-r--r--private/net/svcdlls/lls/server/pack.h195
-rw-r--r--private/net/svcdlls/lls/server/perseat.c3351
-rw-r--r--private/net/svcdlls/lls/server/perseat.h235
-rw-r--r--private/net/svcdlls/lls/server/purchase.c865
-rw-r--r--private/net/svcdlls/lls/server/purchase.h114
-rw-r--r--private/net/svcdlls/lls/server/registry.c1707
-rw-r--r--private/net/svcdlls/lls/server/registry.h83
-rw-r--r--private/net/svcdlls/lls/server/repl.c641
-rw-r--r--private/net/svcdlls/lls/server/repl.h47
-rw-r--r--private/net/svcdlls/lls/server/rpc.c6121
-rw-r--r--private/net/svcdlls/lls/server/scaven.c198
-rw-r--r--private/net/svcdlls/lls/server/scaven.h38
-rw-r--r--private/net/svcdlls/lls/server/server.c754
-rw-r--r--private/net/svcdlls/lls/server/server.h84
-rw-r--r--private/net/svcdlls/lls/server/service.c625
-rw-r--r--private/net/svcdlls/lls/server/service.h59
-rw-r--r--private/net/svcdlls/lls/server/sources84
-rw-r--r--private/net/svcdlls/lls/server/svctbl.c902
-rw-r--r--private/net/svcdlls/lls/server/svctbl.h74
-rw-r--r--private/net/svcdlls/lls/test/common/llsdbg.c441
-rw-r--r--private/net/svcdlls/lls/test/common/llsdbg.h47
-rw-r--r--private/net/svcdlls/lls/test/common/makefile6
-rw-r--r--private/net/svcdlls/lls/test/common/sources45
-rw-r--r--private/net/svcdlls/lls/test/ct/initdata.bat70
-rw-r--r--private/net/svcdlls/lls/test/ct/lic.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/lic1.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/lic2.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/license.txt248
-rw-r--r--private/net/svcdlls/lls/test/ct/lls.docbin0 -> 195072 bytes
-rw-r--r--private/net/svcdlls/lls/test/ct/manyp.bat76
-rw-r--r--private/net/svcdlls/lls/test/ct/map1.bat95
-rw-r--r--private/net/svcdlls/lls/test/ct/mp.bat14
-rw-r--r--private/net/svcdlls/lls/test/ct/n.dat1689
-rw-r--r--private/net/svcdlls/lls/test/ct/n1.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n10.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n2.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n3.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n4.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n5.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n6.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n7.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n8.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/n9.dat1000
-rw-r--r--private/net/svcdlls/lls/test/ct/p1.bat198
-rw-r--r--private/net/svcdlls/lls/test/ct/p2.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p3.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p4.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/p5.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/prod.bat186
-rw-r--r--private/net/svcdlls/lls/test/ct/prod.dat186
-rw-r--r--private/net/svcdlls/lls/test/ct/rpc.bat139
-rw-r--r--private/net/svcdlls/lls/test/dirs1
-rw-r--r--private/net/svcdlls/lls/test/llscmd/llscmd.c1616
-rw-r--r--private/net/svcdlls/lls/test/llscmd/llscmd.rc12
-rw-r--r--private/net/svcdlls/lls/test/llscmd/makefile6
-rw-r--r--private/net/svcdlls/lls/test/llscmd/sources72
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.c448
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.icobin0 -> 1078 bytes
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/llsdbg.rc89
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/makefile6
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/resource.h55
-rw-r--r--private/net/svcdlls/lls/test/llsdbg/sources23
146 files changed, 65561 insertions, 0 deletions
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp b/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp
new file mode 100644
index 000000000..c0104f534
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi.cpp
@@ -0,0 +1,354 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ccfapi.cpp
+
+Abstract:
+
+ Implementation of CCcfApiApp, the MFC application object for CCFAPI32.DLL.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#include "stdafx.h"
+#include <lmerr.h>
+
+#include "ccfapi.h"
+#include "source.h"
+#include "imagelst.h"
+#include "remdlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+CCcfApiApp theApp; // The one and only CCcfApiApp object
+
+
+BEGIN_MESSAGE_MAP(CCcfApiApp, CWinApp)
+ //{{AFX_MSG_MAP(CCcfApiApp)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CCcfApiApp::CCcfApiApp()
+
+/*++
+
+Routine Description:
+
+ Constructor for CCF API application.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ m_LastError = 0;
+ m_LastLlsError = 0;
+
+ LPTSTR pszHelpFileName = m_strHelpFileName.GetBuffer( MAX_PATH );
+
+ if ( NULL != pszHelpFileName )
+ {
+ BOOL ok = GetSystemDirectory( pszHelpFileName, MAX_PATH );
+ m_strHelpFileName.ReleaseBuffer();
+
+ if ( ok )
+ {
+ m_strHelpFileName += TEXT( "\\" );
+ }
+
+ m_strHelpFileName += TEXT( "ccfapi.hlp" );
+ }
+}
+
+
+void CCcfApiApp::DisplayLastError()
+
+/*++
+
+Routine Description:
+
+ Displays a message corresponding to the last error encountered.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CString strLastError;
+ CString strErrorCaption;
+
+ strLastError = GetLastErrorString();
+
+ AfxMessageBox( strLastError, MB_ICONSTOP | MB_OK );
+}
+
+
+CString CCcfApiApp::GetLastErrorString()
+
+/*++
+
+Routine Description:
+
+ Retrieves string for last error.
+
+ (Routine stolen from winsadmn...).
+
+ (And that routine stolen from LlsMgr...).
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ CString strLastError;
+ DWORD nId = m_LastError;
+ const int cchLastErrorSize = 512;
+ LPTSTR pszLastError;
+ DWORD cchLastError;
+
+ if (((long)nId == RPC_S_CALL_FAILED) ||
+ ((long)nId == RPC_NT_SS_CONTEXT_MISMATCH))
+ {
+ strLastError.LoadString(IDS_ERROR_DROPPED_LINK);
+ }
+ else if (((long)nId == RPC_S_SERVER_UNAVAILABLE) ||
+ ((long)nId == RPC_NT_SERVER_UNAVAILABLE))
+ {
+ strLastError.LoadString(IDS_ERROR_NO_RPC_SERVER);
+ }
+ else if ((long)nId == STATUS_INVALID_LEVEL)
+ {
+ strLastError.LoadString(IDS_ERROR_DOWNLEVEL_SERVER);
+ }
+ else if (((long)nId == ERROR_ACCESS_DENIED) ||
+ ((long)nId == STATUS_ACCESS_DENIED))
+ {
+ strLastError.LoadString(IDS_ERROR_ACCESS_DENIED);
+ }
+ else if ((long)nId == STATUS_ACCOUNT_EXPIRED)
+ {
+ strLastError.LoadString(IDS_ERROR_CERTIFICATE_EXPIRED);
+ }
+ else
+ {
+ HINSTANCE hinstDll = NULL;
+
+ if ((nId >= NERR_BASE) && (nId <= MAX_NERR))
+ {
+ hinstDll = ::LoadLibrary( _T( "netmsg.dll" ) );
+ }
+ else if (nId >= 0x4000000)
+ {
+ hinstDll = ::LoadLibrary( _T( "ntdll.dll" ) );
+ }
+
+ cchLastError = 0;
+ pszLastError = strLastError.GetBuffer( cchLastErrorSize );
+
+ if ( NULL != pszLastError )
+ {
+ DWORD dwFlags = FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_MAX_WIDTH_MASK
+ | ( hinstDll ? FORMAT_MESSAGE_FROM_HMODULE
+ : FORMAT_MESSAGE_FROM_SYSTEM );
+
+ cchLastError = ::FormatMessage( dwFlags,
+ hinstDll,
+ nId,
+ 0,
+ pszLastError,
+ cchLastErrorSize,
+ NULL );
+
+ strLastError.ReleaseBuffer();
+ }
+
+ if ( hinstDll )
+ {
+ ::FreeLibrary( hinstDll );
+ }
+
+ if ( 0 == cchLastError )
+ {
+ strLastError.LoadString( IDS_ERROR_UNSUCCESSFUL );
+ }
+ }
+
+ return strLastError;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+DWORD CCcfApiApp::CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ CCertSourceSelectDlg srcDlg( CWnd::FromHandle( hWndParent ) );
+ LPCSTR pszNetServerName = NULL;
+ CHAR szNetServerName[ 3 + MAX_COMPUTERNAME_LENGTH ] = "\\\\";
+
+ // make sure server name, if specified, is in the form \\server
+ if ( NULL != pszServerName )
+ {
+ if ( ( pszServerName[0] != '\\' ) || ( pszServerName[1] != '\\' ) )
+ {
+ // is not prefixed with backslashes
+ lstrcpynA( szNetServerName + 2, pszServerName, sizeof( szNetServerName ) - 3 );
+ pszNetServerName = szNetServerName;
+ }
+ else
+ {
+ // is prefixed with backslashes
+ pszNetServerName = pszServerName;
+ }
+ }
+
+ return srcDlg.CertificateEnter( hWndParent, pszNetServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+}
+
+
+DWORD CCcfApiApp::CertificateRemove( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ CCertRemoveSelectDlg remDlg( CWnd::FromHandle( hWndParent ) );
+ LPCSTR pszNetServerName = NULL;
+ CHAR szNetServerName[ 3 + MAX_COMPUTERNAME_LENGTH ] = "\\\\";
+
+ // make sure server name, if specified, is in the form \\server
+ if ( NULL != pszServerName )
+ {
+ if ( ( pszServerName[0] != '\\' ) || ( pszServerName[1] != '\\' ) )
+ {
+ // is not prefixed with backslashes
+ lstrcpynA( szNetServerName + 2, pszServerName, sizeof( szNetServerName ) - 3 );
+ pszNetServerName = szNetServerName;
+ }
+ else
+ {
+ // is prefixed with backslashes
+ pszNetServerName = pszServerName;
+ }
+ }
+
+ return remDlg.CertificateRemove( pszNetServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+}
+
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi.h b/private/net/svcdlls/lls/ccfapi32/ccfapi.h
new file mode 100644
index 000000000..2bc28aa9f
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi.h
@@ -0,0 +1,110 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ccfapi.cpp
+
+Abstract:
+
+ Prototype of CCcfApiApp, the MFC application object for CCFAPI32.DLL.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+class CCcfApiApp : public CWinApp
+{
+public:
+ // constructor
+ CCcfApiApp();
+
+ // error API
+ void SetLastError( DWORD dwLastError );
+ DWORD GetLastError();
+
+ void SetLastLlsError( NTSTATUS nt );
+ DWORD GetLastLlsError();
+ BOOL IsConnectionDropped();
+
+ CString GetLastErrorString();
+ void DisplayLastError();
+
+ // help API
+ LPCTSTR GetHelpFileName();
+
+ // CCF API
+ DWORD CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+ DWORD CertificateRemove( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCcfApiApp)
+ //}}AFX_VIRTUAL
+
+ //{{AFX_MSG(CCcfApiApp)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+
+private:
+ DWORD m_LastError;
+ NTSTATUS m_LastLlsError;
+ CString m_strHelpFileName;
+};
+
+// return the name of the CCF UI help file
+inline LPCTSTR CCcfApiApp::GetHelpFileName()
+ { return m_strHelpFileName; }
+
+// set last general error
+inline void CCcfApiApp::SetLastError( DWORD dwLastError )
+ { m_LastError = dwLastError; }
+
+// get last general error
+inline DWORD CCcfApiApp::GetLastError()
+ { return m_LastError; }
+
+// set last license server API error
+inline void CCcfApiApp::SetLastLlsError( NTSTATUS nt )
+ { m_LastLlsError = nt; m_LastError = (DWORD) nt; }
+
+// get last license server API error
+inline DWORD CCcfApiApp::GetLastLlsError()
+ { return m_LastLlsError; }
+
+// did the last license server call fail because of a lack of connectivity?
+inline BOOL CCcfApiApp::IsConnectionDropped()
+ { return ( (m_LastLlsError == STATUS_INVALID_HANDLE) ||
+ (m_LastLlsError == RPC_NT_SERVER_UNAVAILABLE) ||
+ (m_LastLlsError == RPC_NT_SS_CONTEXT_MISMATCH) ||
+ (m_LastLlsError == RPC_S_SERVER_UNAVAILABLE) ||
+ (m_LastLlsError == RPC_S_CALL_FAILED) ); }
+
+/////////////////////////////////////////////////////////////////////////////
+
+// maximum amount of data to request at a time from license server
+#define LLS_PREFERRED_LENGTH ((DWORD)-1L)
+
+extern CCcfApiApp theApp;
+
+extern "C"
+{
+ DWORD APIENTRY NoCertificateEnter( HWND hWnd, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD APIENTRY PaperCertificateEnter( HWND hWnd, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+
+ DWORD APIENTRY NoCertificateRemove( HWND hWnd, LPCSTR pszServerName, DWORD dwFlags, DWORD dwLicenseLevel, LPVOID pvLicenseInfo );
+ DWORD APIENTRY PaperCertificateRemove( HWND hWnd, LPCSTR pszServerName, DWORD dwFlags, DWORD dwLicenseLevel, LPVOID pvLicenseInfo );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi32.def b/private/net/svcdlls/lls/ccfapi32/ccfapi32.def
new file mode 100644
index 000000000..5396cf438
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi32.def
@@ -0,0 +1,13 @@
+; ccfapi32.def : Declares the module parameters for the DLL.
+
+LIBRARY "CCFAPI32"
+DESCRIPTION 'CCFAPI32 License Certificate API'
+
+EXPORTS
+ ; Explicit exports can go here
+ CCFCertificateEnterUI
+ CCFCertificateRemoveUI
+ PaperCertificateEnter
+ PaperCertificateRemove
+ NoCertificateEnter
+ NoCertificateRemove
diff --git a/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc b/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc
new file mode 100644
index 000000000..c9cdd44d7
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/ccfapi32.rc
@@ -0,0 +1,346 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif\r\n"
+ "#include ""res\\ccfapi32.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CERT_SOURCE_PAPER DIALOG DISCARDABLE 0, 0, 275, 289
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "License Certificate Entry"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ COMBOBOX IDC_PRODUCT_NAME,90,40,113,65,CBS_DROPDOWN | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ EDITTEXT IDC_VENDOR,90,59,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_SERIAL_NUMBER,90,76,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_KEY_CODE,90,93,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_ACTIVATION_CODE,90,111,113,12,ES_AUTOHSCROLL
+ EDITTEXT IDC_COMMENT,90,128,113,12,ES_AUTOHSCROLL
+ CONTROL "All licenses",IDC_ALL_LICENSES,"Button",
+ BS_AUTORADIOBUTTON | WS_GROUP,47,186,51,10
+ CONTROL "Only",IDC_SOME_LICENSES,"Button",BS_AUTORADIOBUTTON,47,
+ 199,30,10
+ EDITTEXT IDC_NUM_LICENSES,78,197,40,14,ES_AUTOHSCROLL | WS_GROUP
+ CONTROL "Spin1",IDC_SPIN_LICENSES,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS |
+ UDS_NOTHOUSANDS,118,197,10,14
+ CONTROL "Per Seat",IDC_PER_SEAT,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,47,252,43,10
+ CONTROL "Per Server",IDC_PER_SERVER,"Button",BS_AUTORADIOBUTTON,
+ 47,264,49,10
+ DEFPUSHBUTTON "OK",IDOK,217,10,50,14,WS_GROUP
+ PUSHBUTTON "Cancel",IDCANCEL,217,28,50,14
+ PUSHBUTTON "Help",IDC_MY_HELP,217,46,50,14
+ GROUPBOX "Enter the following information",IDC_STATIC,8,5,201,144
+ LTEXT "Product Name:",IDC_STATIC,16,44,52,8
+ LTEXT "Vendor:",IDC_STATIC,16,61,52,8
+ LTEXT "Serial #:",IDC_STATIC,16,78,52,8
+ LTEXT "Key Code:",IDC_STATIC,16,95,52,8
+ ICON IDI_LICENSE,IDC_STATIC,16,19,21,20
+ LTEXT "Please enter the following information exactly as it appears on your certificate:",
+ IDC_STATIC,42,17,160,18
+ LTEXT "Activation Code:",IDC_STATIC,15,113,58,8
+ LTEXT "Comment:",IDC_STATIC,15,130,58,8
+ GROUPBOX "License quantity",IDC_STATIC,8,152,201,66
+ LTEXT "license(s)",IDC_STATIC,133,200,30,8
+ ICON IDI_LICENSE,IDC_STATIC,16,167,21,20
+ LTEXT "How many licenses from this certificate would you like to install on this machine?",
+ IDC_STATIC,42,165,160,18
+ GROUPBOX "License mode",IDC_STATIC,8,221,201,60
+ ICON IDI_LICENSE,IDC_STATIC,15,234,21,20
+ LTEXT "For which license mode would you like to install these licenses?",
+ IDC_STATIC,41,232,160,18
+END
+
+IDD_CERT_SOURCE_SELECT DIALOG DISCARDABLE 0, 0, 242, 79
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Certificate Source"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,188,10,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,188,28,50,14
+ GROUPBOX "Enter the following information",IDC_STATIC,6,6,176,60
+ ICON IDI_LICENSE,IDC_STATIC,14,20,18,20
+ LTEXT "Please select a method for entering your License Certificate.",
+ IDC_STATIC,41,19,137,19
+ COMBOBOX IDC_CERT_SOURCE,41,43,109,32,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "Help",IDC_MY_HELP,188,46,50,14
+END
+
+IDD_NEW_LICENSE DIALOG DISCARDABLE 0, 0, 259, 111
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "New Client Access License"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Choose the server product, quantity, and license mode for this license.",
+ IDC_STATIC,10,10,155,20
+ LTEXT "&Product:",IDC_STATIC,10,35,30,10
+ COMBOBOX IDC_NEW_LICENSE_PRODUCT,69,33,115,60,CBS_DROPDOWNLIST |
+ CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Quantity:",IDC_STATIC,10,56,30,10
+ EDITTEXT IDC_NEW_LICENSE_QUANTITY,69,54,40,12,ES_AUTOHSCROLL
+ CONTROL "Generic1",IDC_NEW_LICENSE_SPIN,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,109,55,7,
+ 11
+ LTEXT "For License &Mode:",IDC_STATIC,10,70,50,16
+ CONTROL "Per Seat",IDC_PER_SEAT,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP,69,73,43,10
+ CONTROL "Per Server",IDC_PER_SERVER,"Button",BS_AUTORADIOBUTTON,
+ 121,73,49,10
+ LTEXT "&Comment:",IDC_STATIC,10,92,35,10
+ EDITTEXT IDC_NEW_LICENSE_COMMENT,69,90,115,12,ES_AUTOHSCROLL |
+ WS_GROUP
+ DEFPUSHBUTTON "OK",IDOK,201,10,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,201,30,50,14
+ PUSHBUTTON "&Help",IDC_MY_HELP,201,50,50,14
+END
+
+IDD_PER_SEAT_LICENSING DIALOG DISCARDABLE 0, 0, 261, 153
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Per Seat Licensing"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "&I agree that:",IDC_PER_SEAT_AGREE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,45,100,140,10
+ ICON IDI_MY_WARNING,IDC_STATIC,10,10,18,20
+ GROUPBOX "Licensing",IDC_STATIC,35,10,165,130
+ LTEXT "If you have not purchased a Client Access License for every computer that will access %1, you must purchase them prior to using %1.",
+ IDC_PER_SEAT_STATIC_CLIENTS,45,25,145,40
+ LTEXT "For complete terms and conditions governing the use of this product, see the license agreements, which can be found under Help.",
+ IDC_STATIC,45,70,145,25
+ LTEXT "I have read and agree to be bound by the license agreements for this product.",
+ IDC_STATIC,45,115,145,20
+ DEFPUSHBUTTON "OK",IDOK,205,15,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,205,35,50,14
+ PUSHBUTTON "&Help",ID_HELP,205,55,50,14
+END
+
+IDD_PER_SERVER_LICENSING DIALOG DISCARDABLE 0, 0, 266, 153
+STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU
+CAPTION "Per Server Licensing"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "&I agree that:",IDC_PER_SERVER_AGREE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,45,105,55,10
+ LTEXT "I have read and agree to be bound by the license agreements for this product.",
+ IDC_STATIC,45,120,145,20
+ ICON IDI_MY_WARNING,IDC_STATIC,10,10,18,20
+ GROUPBOX "Licensing",IDC_STATIC,35,10,165,135
+ LTEXT "If you have not purchased %1 Client Access Licenses, you must purchase the required licenses prior to using %2 on this computer.",
+ IDC_PER_SERVER_STATIC_CLIENTS,45,25,145,35
+ LTEXT "For complete terms and conditions governing the use of this product, see the license agreements, which can be found under Help.",
+ IDC_STATIC,45,65,145,35
+ DEFPUSHBUTTON "OK",IDOK,205,15,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,205,35,50,14
+ PUSHBUTTON "&Help",ID_HELP,205,55,50,14
+END
+
+IDD_CERT_REMOVE_SELECT DIALOG DISCARDABLE 0, 0, 376, 215
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Certificate to Remove Licenses"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ CONTROL "List1",IDC_CERTIFICATE_LIST,"SysListView32",LVS_REPORT |
+ LVS_SINGLESEL | LVS_SHAREIMAGELISTS | WS_BORDER |
+ WS_TABSTOP,7,16,362,100
+ EDITTEXT IDC_NUM_LICENSES,110,124,40,14,ES_AUTOHSCROLL
+ CONTROL "Spin1",IDC_SPIN_LICENSES,"msctls_updown32",
+ UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,150,124,
+ 10,14
+ DEFPUSHBUTTON "Remove",IDOK,145,194,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,203,194,50,14
+ PUSHBUTTON "Help",IDC_MY_HELP,319,194,50,14
+ LTEXT "Number of Licenses to remove:",IDC_STATIC,7,126,98,8
+ LTEXT "Removing licenses allows them to be legally reinstalled on another machine.",
+ IDC_STATIC,7,148,362,19
+ LTEXT "Installed Certificates:",IDC_STATIC,7,7,66,8
+ PUSHBUTTON "Refresh List",IDC_REFRESH,261,194,50,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_LICENSE ICON PRELOAD DISCARDABLE "res\\license.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_SMALL_ICONS BITMAP DISCARDABLE "res\\smicons.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_ERROR_DROPPED_LINK "Communication with the license server has been interrupted. License certificate entry cannot be performed at this time."
+ IDS_ERROR_NO_RPC_SERVER "The License Logging Service is not running on the target machine, or the target machine is not accessible."
+ IDS_ERROR_UNSUCCESSFUL "The operation failed to complete successfully. "
+ IDS_ERROR_DOWNLEVEL_SERVER
+ "The License Logging Server on the target machine does not support license certificates. You must use the Windows NT 3.51-compatible license source to enter licenses for this server."
+ IDS_BAD_ACTIVATION_CODE "The certificate data or activation code is incorrect. Please ensure that the certificate data entered is identical to that on your certificate."
+ IDS_NO_PRODUCT_SEND_TO_ENTERPRISE
+ "This product is not currently registered on the target server. Please install the product before entering this license certificate."
+ IDS_PER_SEAT_SEND_TO_ENTERPRISE
+ "This certificate will be used in the Per Seat licensing mode. Therefore, it will instead be installed on the enterprise server.\n\nIf you wish to abort installation of this certificate, click Cancel."
+ IDS_PER_SEAT_LICENSING_1
+ "If you have not purchased a Client Access License for every computer that will access %1, you must purchase them prior to using %1."
+ IDS_NO_CERTIFICATE_SOURCE_NAME "No Certificate"
+ IDS_ERROR_ACCESS_DENIED "You do not have the appropriate access privileges to complete this operation."
+ IDS_PER_SERVER_LICENSING_1
+ "If you have not purchased %1 Client Access Licenses, you must purchase the required licenses prior to using %2 on this computer."
+ IDS_PER_SERVER_APP_NOT_INSTALLED
+ "The application is not installed on the target machine.\n\nPer server licenses may be installed only on machines on which the application which uses those licenses has already been installed."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE
+ "You have chosen to install licenses in the Per Seat licensing mode. Therefore, they will instead be installed on the enterprise server.\n\nIf you wish to abort installation of these licenses, click Cancel."
+ IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE
+ "There are not enough licenses on this certificate to install the number you requested."
+ IDS_INVALID_NUM_LICENSES
+ "The number of licenses you have selected to install from this certificate is invalid. Please enter a number between 1 and 4095."
+ IDS_ERROR_CERTIFICATE_EXPIRED
+ "This certificate has expired. The licenses it contains are no longer valid."
+ IDS_SERIAL_NUMBER "Serial Number"
+ IDS_PRODUCT_NAME "Product"
+ IDS_QUANTITY "Quantity"
+ IDS_SOURCE "Source"
+ IDS_SOURCE_NONE "None"
+ IDS_REMOVE_CERTIFICATE_CONFIRM
+ "Are you sure you wish to remove %1 licenses from %2?"
+ IDS_CERT_SOURCE_NOT_AVAILABLE
+ "The source with which this certificate was installed is not available on the local machine.\n\nUse License Manager on the target server to remove this certificate."
+ IDS_PAPER_REMOVE_COMMENT "Licenses removed by administrator."
+ IDS_NO_REMOVE_COMMENT "Licenses removed by administrator."
+ IDS_LICENSE_MODE "License Mode"
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_LICENSE_MODE_EITHER "Either"
+ IDS_LICENSE_MODE_PER_SEAT "Per Seat"
+ IDS_LICENSE_MODE_PER_SERVER "Per Server"
+ IDS_LICENSE_MODE_UNKNOWN "Unknown"
+ IDS_LOCAL_LICENSES_ALREADY_INSTALLED
+ "Licenses from this certificate have already been added to the target machine. Adding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_NET_CERTIFICATE_TARGET_ENTRY "%1\t(%2 licenses)"
+ IDS_NET_LICENSES_ALREADY_INSTALLED_ON
+ "Licenses from this certificate have already been installed on the following machines:\n\n%1\n\nAdding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_NET_LICENSES_ALREADY_INSTALLED
+ "Licenses from this certificate have already been installed on your network. Adding the requested number of licenses would exceed the installable license capacity of the certificate."
+ IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT
+ "The enterprise server does not support this license certificate format.\nYou must upgrade the Windows NT installation on the enterprise server or use this product in the Per Server licensing mode."
+ IDS_REMOVE_INVALID_NUM_LICENSES
+ "The number of licenses to remove must be between 1 and the number of licenses currently installed."
+ IDS_LICENSE_MODE_NOT_ALLOWED
+ "This certificate does not support the selected license mode."
+ IDS_NO_PRODUCT_CERTIFICATE_SOURCES
+ "No certificate source is installed with which licenses for this product could be added."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ AFX_IDS_APP_TITLE "License System"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif
+#include "res\ccfapi32.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/private/net/svcdlls/lls/ccfapi32/exports.cpp b/private/net/svcdlls/lls/ccfapi32/exports.cpp
new file mode 100644
index 000000000..58caff51b
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/exports.cpp
@@ -0,0 +1,382 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ exports.cpp
+
+Abstract:
+
+ Provides APIs to enter and remove license certificates from the system.
+ The clientele consists of LICCPA.CPL (the licensing control panel applet)
+ and LLSMGR.EXE (License Manager), and it may also be used directly by setup
+ programs.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "paper.h"
+#include "nlicdlg.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+DWORD APIENTRY CCFCertificateEnterUI( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ dwError = theApp.CertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+
+ return dwError;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY CCFCertificateRemoveUI( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ dwError = theApp.CertificateRemove( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags, pszSourceToUse );
+
+ return dwError;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Source -- No Certificate //
+////////////////////////////////////////////
+
+DWORD APIENTRY NoCertificateEnter( HWND hWnd,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with no certificate (3.51-style).
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ CNewLicenseDialog dlg( CWnd::FromHandle( hWnd ) );
+ return dlg.CertificateEnter( pszServerName, pszProductName, pszVendor, dwFlags );
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY NoCertificateRemove( HWND hWnd,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID pvLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via 3.51 or NoCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( 1 != dwLicenseLevel )
+ {
+ dwError = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ CNewLicenseDialog dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateRemove( pszServerName, dwFlags, (PLLS_LICENSE_INFO_1) pvLicenseInfo );
+ }
+
+ return dwError;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Source -- Paper Certificate //
+///////////////////////////////////////////////
+
+DWORD APIENTRY PaperCertificateEnter( HWND hWnd,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with a paper certificate.
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( !!pszProductName != !!pszVendor )
+ {
+ // they must both be NULL or both have values
+ dwError = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ CPaperSourceDlg dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateEnter( pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+
+ return dwError;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+DWORD APIENTRY PaperCertificateRemove( HWND hWnd,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID pvLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via PaperCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ AFX_MANAGE_STATE( AfxGetStaticModuleState() );
+
+ DWORD dwError;
+
+ if ( 1 != dwLicenseLevel )
+ {
+ dwError = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ CPaperSourceDlg dlg( CWnd::FromHandle( hWnd ) );
+ dwError = dlg.CertificateRemove( pszServerName, dwFlags, (PLLS_LICENSE_INFO_1) pvLicenseInfo );
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/imagelst.h b/private/net/svcdlls/lls/ccfapi32/imagelst.h
new file mode 100644
index 000000000..65313e4e2
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/imagelst.h
@@ -0,0 +1,38 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ imagelst.h
+
+Abstract:
+
+ Constants for shared image list.
+
+Author:
+
+ Don Ryan (donryan) 12-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR and modified for CCFApi bitmaps.
+
+--*/
+
+#ifndef _IMAGELST_H_
+#define _IMAGELST_H_
+
+#define BMPI_RGB_BKGND (RGB(0,255,0)) // green mask...
+
+#define BMPI_CERTIFICATE 0
+
+#define BMPI_SMALL_SIZE 18 // one bit border...
+#define BMPI_LARGE_SIZE 34 // one bit border...
+
+#endif // _IMAGELST_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/licobj.cpp b/private/net/svcdlls/lls/ccfapi32/licobj.cpp
new file mode 100644
index 000000000..120f6f73a
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/licobj.cpp
@@ -0,0 +1,308 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ licobj.cpp
+
+Abstract:
+
+ License object implementation.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, converted to handle level 1 licenses,
+ removed OLE support.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "licobj.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNCREATE(CLicense, CObject)
+
+CLicense::CLicense( LPCTSTR pProduct /* = NULL */,
+ LPCTSTR pVendor /* = NULL */,
+ LPCTSTR pAdmin /* = NULL */,
+ DWORD dwPurchaseDate /* = 0 */,
+ long lQuantity /* = 0 */,
+ LPCTSTR pDescription /* = NULL */,
+ DWORD dwAllowedModes /* = LLS_LICENSE_MODE_ALLOW_PER_SEAT */,
+ DWORD dwCertificateID /* = 0 */,
+ LPCTSTR pSource /* = TEXT("None") */,
+ DWORD dwExpirationDate /* = 0 */,
+ DWORD dwMaxQuantity /* = 0 */,
+ LPDWORD pdwSecrets /* = NULL */ )
+
+/*++
+
+Routine Description:
+
+ Constructor for CLicense object.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(pProduct && *pProduct);
+
+ m_strAdmin = pAdmin;
+ m_strVendor = pVendor;
+ m_strProduct = pProduct;
+ m_strDescription = pDescription;
+ m_strSource = pSource;
+ m_lQuantity = lQuantity;
+ m_dwAllowedModes = dwAllowedModes;
+ m_dwCertificateID = dwCertificateID;
+ m_dwPurchaseDate = dwPurchaseDate;
+ m_dwExpirationDate = dwExpirationDate;
+ m_dwMaxQuantity = dwMaxQuantity;
+
+ if ( NULL == pdwSecrets )
+ {
+ ZeroMemory( m_adwSecrets, sizeof( m_adwSecrets ) );
+ }
+ else
+ {
+ memcpy( m_adwSecrets, pdwSecrets, sizeof( m_adwSecrets ) );
+ }
+
+ m_strSourceDisplayName = TEXT("");
+ m_strAllowedModes = TEXT("");
+}
+
+
+CString CLicense::GetSourceDisplayName()
+
+/*++
+
+Routine Description:
+
+ Retrieve the display name for the certificate source that was used to
+ install these licenses. Note that if the source that was used is not
+ installed locally, the display name is not retrievable, and the source
+ name will be returned instead.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ if ( m_strSourceDisplayName.IsEmpty() )
+ {
+ if ( !m_strSource.CompareNoCase( TEXT( "None" ) ) )
+ {
+ m_strSourceDisplayName.LoadString( IDS_SOURCE_NONE );
+ }
+ else
+ {
+ LONG lError;
+ CString strKeyName = TEXT( "Software\\LSAPI\\Microsoft\\CertificateSources\\" )
+ + m_strSource;
+ HKEY hKeySource;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, strKeyName, 0, KEY_READ, &hKeySource );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ const DWORD cchSourceDisplayName = 80;
+ DWORD cbSourceDisplayName = sizeof( TCHAR ) * cchSourceDisplayName;
+ LPTSTR pszSourceDisplayName;
+ DWORD dwType;
+
+ pszSourceDisplayName = m_strSourceDisplayName.GetBuffer( cchSourceDisplayName );
+
+ if ( NULL != pszSourceDisplayName )
+ {
+ lError = RegQueryValueEx( hKeySource, REG_VALUE_NAME, NULL, &dwType, (LPBYTE) pszSourceDisplayName, &cbSourceDisplayName );
+
+ m_strSourceDisplayName.ReleaseBuffer();
+ }
+
+ RegCloseKey( hKeySource );
+ }
+
+ if ( ( ERROR_SUCCESS != lError ) || m_strSourceDisplayName.IsEmpty() )
+ {
+ m_strSourceDisplayName = m_strSource;
+ }
+ }
+ }
+
+ return m_strSourceDisplayName;
+}
+
+
+DWORD CLicense::CreateLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo1 )
+
+/*++
+
+Routine Description:
+
+ Create a LLS_LICENSE_INFO_1 structure corresponding to this object.
+
+Arguments:
+
+ pLicInfo1 (PLLS_LICENSE_INFO_1)
+ On return, holds the created structure.
+
+Return Values:
+
+ ERROR_SUCCESS or ERROR_NOT_ENOUGH_MEMORY.
+
+--*/
+
+{
+ DWORD dwError;
+
+ pLicInfo1->Product = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strProduct.GetLength() ) );
+ pLicInfo1->Vendor = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strVendor.GetLength() ) );
+ pLicInfo1->Admin = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strAdmin.GetLength() ) );
+ pLicInfo1->Comment = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strDescription.GetLength() ) );
+ pLicInfo1->Source = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + m_strSource.GetLength() ) );
+
+ if ( ( NULL == pLicInfo1->Product )
+ || ( NULL == pLicInfo1->Vendor )
+ || ( NULL == pLicInfo1->Admin )
+ || ( NULL == pLicInfo1->Comment )
+ || ( NULL == pLicInfo1->Source ) )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ lstrcpy( pLicInfo1->Product, m_strProduct );
+ lstrcpy( pLicInfo1->Vendor, m_strVendor );
+ lstrcpy( pLicInfo1->Admin, m_strAdmin );
+ lstrcpy( pLicInfo1->Comment, m_strDescription );
+ lstrcpy( pLicInfo1->Source, m_strSource );
+
+ pLicInfo1->Quantity = m_lQuantity;
+ pLicInfo1->MaxQuantity = m_dwMaxQuantity;
+ pLicInfo1->Date = m_dwPurchaseDate;
+ pLicInfo1->AllowedModes = m_dwAllowedModes;
+ pLicInfo1->CertificateID = m_dwCertificateID;
+ pLicInfo1->ExpirationDate = m_dwExpirationDate;
+ memcpy( pLicInfo1->Secrets, m_adwSecrets, sizeof( m_adwSecrets ) );
+
+ dwError = ERROR_SUCCESS;
+ }
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ if ( NULL != pLicInfo1->Product ) LocalFree( pLicInfo1->Product );
+ if ( NULL != pLicInfo1->Vendor ) LocalFree( pLicInfo1->Vendor );
+ if ( NULL != pLicInfo1->Admin ) LocalFree( pLicInfo1->Admin );
+ if ( NULL != pLicInfo1->Comment ) LocalFree( pLicInfo1->Comment );
+ if ( NULL != pLicInfo1->Source ) LocalFree( pLicInfo1->Source );
+
+ ZeroMemory( pLicInfo1, sizeof( *pLicInfo1 ) );
+ }
+
+ return dwError;
+}
+
+
+void CLicense::DestroyLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo1 )
+
+/*++
+
+Routine Description:
+
+ Frees a license structure previously created by CreateLicenseInfo().
+
+Arguments:
+
+ pLicInfo1 (PLLS_LICENSE_INFO_1)
+ The structure previously created by CreateLicenseInfo().
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != pLicInfo1->Product ) LocalFree( pLicInfo1->Product );
+ if ( NULL != pLicInfo1->Vendor ) LocalFree( pLicInfo1->Vendor );
+ if ( NULL != pLicInfo1->Admin ) LocalFree( pLicInfo1->Admin );
+ if ( NULL != pLicInfo1->Comment ) LocalFree( pLicInfo1->Comment );
+ if ( NULL != pLicInfo1->Source ) LocalFree( pLicInfo1->Source );
+}
+
+
+CString CLicense::GetAllowedModesString()
+
+/*++
+
+Routine Description:
+
+ Get a string corresponding to the license mode(s) for which this license
+ was installed.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ CString.
+
+--*/
+
+{
+ if ( m_strAllowedModes.IsEmpty() )
+ {
+ UINT uStringID;
+
+ switch ( m_dwAllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) )
+ {
+ case ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ):
+ uStringID = IDS_LICENSE_MODE_EITHER;
+ break;
+ case LLS_LICENSE_MODE_ALLOW_PER_SEAT:
+ uStringID = IDS_LICENSE_MODE_PER_SEAT;
+ break;
+ case LLS_LICENSE_MODE_ALLOW_PER_SERVER:
+ uStringID = IDS_LICENSE_MODE_PER_SERVER;
+ break;
+ default:
+ uStringID = IDS_LICENSE_MODE_UNKNOWN;
+ break;
+ }
+
+ m_strAllowedModes.LoadString( uStringID );
+ }
+
+ return m_strAllowedModes;
+}
+
diff --git a/private/net/svcdlls/lls/ccfapi32/licobj.h b/private/net/svcdlls/lls/ccfapi32/licobj.h
new file mode 100644
index 000000000..ce62aa4d0
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/licobj.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ licobj.h
+
+Abstract:
+
+ License object implementation.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, converted to handle level 1 licenses,
+ removed OLE support.
+
+--*/
+
+#ifndef _LICOBJ_H_
+#define _LICOBJ_H_
+
+class CLicense : public CObject
+{
+ DECLARE_DYNCREATE(CLicense)
+
+public:
+ CString m_strAdmin;
+ CString m_strProduct;
+ CString m_strVendor;
+ CString m_strDescription;
+ CString m_strSource;
+ long m_lQuantity;
+ DWORD m_dwAllowedModes;
+ DWORD m_dwCertificateID;
+ DWORD m_dwPurchaseDate;
+ DWORD m_dwExpirationDate;
+ DWORD m_dwMaxQuantity;
+ DWORD m_adwSecrets[ LLS_NUM_SECRETS ];
+
+ // cache for derived values
+ CString m_strSourceDisplayName;
+ CString m_strAllowedModes;
+
+public:
+ CLicense( LPCTSTR pProduct = NULL,
+ LPCTSTR pVendor = NULL,
+ LPCTSTR pAdmin = NULL,
+ DWORD dwPurchaseDate = 0,
+ long lQuantity = 0,
+ LPCTSTR pDescription = NULL,
+ DWORD dwAllowedModes = LLS_LICENSE_MODE_ALLOW_PER_SEAT,
+ DWORD dwCertificateID = 0,
+ LPCTSTR pSource = TEXT("None"),
+ DWORD dwExpirationDate = 0,
+ DWORD dwMaxQuantity = 0,
+ LPDWORD pdwSecrets = NULL );
+
+ CString GetSourceDisplayName();
+ CString GetAllowedModesString();
+
+ DWORD CreateLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo );
+ void DestroyLicenseInfo( PLLS_LICENSE_INFO_1 pLicInfo );
+
+};
+
+#endif // _LICOBJ_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/makefile b/private/net/svcdlls/lls/ccfapi32/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/ccfapi32/md4.h b/private/net/svcdlls/lls/ccfapi32/md4.h
new file mode 100644
index 000000000..0f1905dc2
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/md4.h
@@ -0,0 +1,120 @@
+#ifndef _MD4_H_
+#define _MD4_H_
+
+/* MD4.H - header file for MD4C.C
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* This code differs from the MD4 implementation contained in Internet
+ RFC-1320 in the following respects:
+
+ 1. "global.h" is no longer needed.
+
+ 2. PROTO_LIST was removed from the function prototypes.
+
+ 3. Comments on the use of the main calls added to aid developers.
+ */
+
+/* ---------------------------------------------------------------------
+ * The procedure for using the following function calls to compute a
+ * digest is as follows:
+ *
+ * MD4_CTX context;
+ * // create a storage context that persistes between calls.
+ *
+ * MD4_Init (&context);
+ * // initialize context's initial digest and byte-count
+ *
+ * MD4Update (&context, inputString, inputLength);
+ * // input first or only block of data to be digested.
+ *
+ * MD4Update (&context, inputString, inputLength);
+ * // input subsequent blocks or last block of data to be digested.
+ *
+ * MD4Final (digest, &context);
+ * // compute and return final 16-byte digest
+ *
+ * --------------------------------------------------------------------- */
+
+typedef unsigned long UINT4;
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *POINTER;
+
+/* MD4 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} MD4_CTX;
+
+/* ---------------------------------------------------------------------
+ * This function initializes the context for the message digest.
+ * It must be called as the first step and before processing input data.
+ * --------------------------------------------------------------------- */
+
+void MD4Init ( MD4_CTX *context ) ; /* context */
+
+/* ---------------------------------------------------------------------
+ * This function accepts input data of length "inputLen" and digests it.
+ * All data to be digested is input via this function and no other.
+ * The running byte count and any fragment of undigested data is stored
+ * in the context for retention between calls.
+ * --------------------------------------------------------------------- */
+
+void MD4Update ( MD4_CTX *context, /* context */
+ POINTER input, /* input block */
+ unsigned int inputLen ) ; /* length of input block */
+
+/* ---------------------------------------------------------------------
+ * This function accepts not data, but finishes up the digest and
+ * returns the 16 byte resulting message digest. Finishing up includes
+ * taking any undigested fragment stored in the context, padding the
+ * message, appending the length and then digesting the resulting string.
+ * --------------------------------------------------------------------- */
+
+void MD4Final ( unsigned char *digest, /* 16-byte message digest */
+ MD4_CTX *context ) ; /* context */
+
+/* ---------------------------------------------------------------------
+
+ The MD4 test suite results, contained in appendix A.5 of RFC-1320,
+ are as listed below. They are printed by the "mddriver.c" test
+ driver contained in appendix A.4 of RFC-1320.
+
+ MD4 test suite:
+ MD4 ("") = 31d6cfe0d16ae931b73c59d7e0c089c0
+ MD4 ("a") = bde52cb31de33e46245e05fbdbd6fb24
+ MD4 ("abc") = a448017aaf21d8525fc10ae87aa6729d
+ MD4 ("message digest") = d9130a8164549fe818874806e1c7014b
+ MD4 ("abcdefghijklmnopqrstuvwxyz") = d79e1c308aa5bbcdeea8ed63df412da9
+ MD4 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ = 043f8582f241db351ce627e153e7f0e4
+ MD4 ("123456789012345678901234567890123456789012345678901234567890123
+ 45678901234567890") = e33b4ddc9c38f2199c3e7b164fcc0536
+
+ * --------------------------------------------------------------------- */
+
+#endif
+
diff --git a/private/net/svcdlls/lls/ccfapi32/md4c.cpp b/private/net/svcdlls/lls/ccfapi32/md4c.cpp
new file mode 100644
index 000000000..db1e8f56c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/md4c.cpp
@@ -0,0 +1,291 @@
+/* MD4C.C - RSA Data Security, Inc., MD4 message-digest algorithm
+ */
+
+/* Copyright (C) 1990-2, RSA Data Security, Inc. All rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD4 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD4 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+/* This code differs from the MD4 implementation contained in Internet
+ RFC-1320 in the following respects:
+
+ 1. Faster boolean computations for the "F" and "G" functions, as
+ suggested by Richard Schoeppel, rcs@cs.arizona.edu, have been included.
+ The original code contained in the RFC is retained as comments.
+
+ 2. PROTO_LIST was removed from the function prototypes.
+ */
+
+#include "stdafx.h"
+#include "md4.h"
+
+/* Constants for MD4Transform routine.
+ */
+#define S11 3
+#define S12 7
+#define S13 11
+#define S14 19
+#define S21 3
+#define S22 5
+#define S23 9
+#define S24 13
+#define S31 3
+#define S32 9
+#define S33 11
+#define S34 15
+
+static void MD4Transform ( UINT4 *state, unsigned char *block ) ;
+static void Encode ( unsigned char *output,
+ UINT4 *input, unsigned int len ) ;
+static void Decode ( UINT4 *output,
+ unsigned char *input, unsigned int len ) ;
+
+
+static unsigned char PADDING[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G and H are basic MD4 functions.
+ Faster methods suggested by Richard Schoeppel, rcs@cs.arizona.edu
+ */
+
+/* #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) original code */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) /* faster method */
+/* #define G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) original code */
+#define G(x, y, z) (((x) & (y)) | ((z) & ((x) | (y)))) /* faster method */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG and HH are transformations for rounds 1, 2 and 3 */
+/* Rotation is separate from addition to prevent recomputation */
+
+#define FF(a, b, c, d, x, s) { \
+ (a) += F ((b), (c), (d)) + (x); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define GG(a, b, c, d, x, s) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)0x5a827999; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+#define HH(a, b, c, d, x, s) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)0x6ed9eba1; \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ }
+
+/* MD4 initialization. Begins an MD4 operation, writing a new context.
+ */
+
+void MD4Init ( MD4_CTX *context ) /* context */
+
+{
+ context->count[0] = context->count[1] = 0;
+
+ /* Load magic initialization constants.
+ */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD4 block update operation. Continues an MD4 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+
+void MD4Update ( MD4_CTX *context, /* context */
+ POINTER input, /* input block */
+ unsigned int inputLen ) /* length of input block */
+
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+ */
+ if (inputLen >= partLen) {
+ memcpy((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD4Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD4Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+/* MD4 finalization. Ends an MD4 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+
+void MD4Final ( unsigned char *digest, /* 16-byte message digest */
+ MD4_CTX *context ) /* context */
+
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+ */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD4Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD4Update (context, bits, 8);
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)context, 0, sizeof (*context));
+
+}
+
+/* MD4 basic transformation. Transforms state based on block.
+ */
+
+static void MD4Transform ( UINT4 *state, /* 16-byte state */
+ unsigned char *block ) /* 64-byte block */
+
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11); /* 1 */
+ FF (d, a, b, c, x[ 1], S12); /* 2 */
+ FF (c, d, a, b, x[ 2], S13); /* 3 */
+ FF (b, c, d, a, x[ 3], S14); /* 4 */
+ FF (a, b, c, d, x[ 4], S11); /* 5 */
+ FF (d, a, b, c, x[ 5], S12); /* 6 */
+ FF (c, d, a, b, x[ 6], S13); /* 7 */
+ FF (b, c, d, a, x[ 7], S14); /* 8 */
+ FF (a, b, c, d, x[ 8], S11); /* 9 */
+ FF (d, a, b, c, x[ 9], S12); /* 10 */
+ FF (c, d, a, b, x[10], S13); /* 11 */
+ FF (b, c, d, a, x[11], S14); /* 12 */
+ FF (a, b, c, d, x[12], S11); /* 13 */
+ FF (d, a, b, c, x[13], S12); /* 14 */
+ FF (c, d, a, b, x[14], S13); /* 15 */
+ FF (b, c, d, a, x[15], S14); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 0], S21); /* 17 */
+ GG (d, a, b, c, x[ 4], S22); /* 18 */
+ GG (c, d, a, b, x[ 8], S23); /* 19 */
+ GG (b, c, d, a, x[12], S24); /* 20 */
+ GG (a, b, c, d, x[ 1], S21); /* 21 */
+ GG (d, a, b, c, x[ 5], S22); /* 22 */
+ GG (c, d, a, b, x[ 9], S23); /* 23 */
+ GG (b, c, d, a, x[13], S24); /* 24 */
+ GG (a, b, c, d, x[ 2], S21); /* 25 */
+ GG (d, a, b, c, x[ 6], S22); /* 26 */
+ GG (c, d, a, b, x[10], S23); /* 27 */
+ GG (b, c, d, a, x[14], S24); /* 28 */
+ GG (a, b, c, d, x[ 3], S21); /* 29 */
+ GG (d, a, b, c, x[ 7], S22); /* 30 */
+ GG (c, d, a, b, x[11], S23); /* 31 */
+ GG (b, c, d, a, x[15], S24); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 0], S31); /* 33 */
+ HH (d, a, b, c, x[ 8], S32); /* 34 */
+ HH (c, d, a, b, x[ 4], S33); /* 35 */
+ HH (b, c, d, a, x[12], S34); /* 36 */
+ HH (a, b, c, d, x[ 2], S31); /* 37 */
+ HH (d, a, b, c, x[10], S32); /* 38 */
+ HH (c, d, a, b, x[ 6], S33); /* 39 */
+ HH (b, c, d, a, x[14], S34); /* 40 */
+ HH (a, b, c, d, x[ 1], S31); /* 41 */
+ HH (d, a, b, c, x[ 9], S32); /* 42 */
+ HH (c, d, a, b, x[ 5], S33); /* 43 */
+ HH (b, c, d, a, x[13], S34); /* 44 */
+ HH (a, b, c, d, x[ 3], S31); /* 45 */
+ HH (d, a, b, c, x[11], S32); /* 46 */
+ HH (c, d, a, b, x[ 7], S33); /* 47 */
+ HH (b, c, d, a, x[15], S34); /* 48 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+ */
+ memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+
+static void Encode ( unsigned char *output,
+ UINT4 *input,
+ unsigned int len )
+
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+
+static void Decode ( UINT4 *output,
+ unsigned char *input,
+ unsigned int len )
+
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp b/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp
new file mode 100644
index 000000000..5d98de7b1
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/nlicdlg.cpp
@@ -0,0 +1,1386 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ nlicdlg.cpp
+
+Abstract:
+
+ 3.51-style license dialog implementation.
+
+Author:
+
+ Don Ryan (donryan) 02-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 14-Dec-1995
+ Moved over from LLSMGR, added ability to purchase per server licenses,
+ added license removal functionality.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "nlicdlg.h"
+#include "pseatdlg.h"
+#include "psrvdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CNewLicenseDialog, CDialog)
+ //{{AFX_MSG_MAP(CNewLicenseDialog)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_NEW_LICENSE_SPIN, OnDeltaPosSpin)
+ ON_EN_UPDATE(IDC_NEW_LICENSE_QUANTITY, OnUpdateQuantity)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_BN_CLICKED(IDC_PER_SEAT, OnPerSeat)
+ ON_BN_CLICKED(IDC_PER_SERVER, OnPerServer)
+ ON_WM_DESTROY()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CNewLicenseDialog::CNewLicenseDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CNewLicenseDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CNewLicenseDialog)
+ m_strComment = _T("");
+ m_nLicenses = 0;
+ m_nLicensesMin = 0;
+ m_strProduct = _T("");
+ m_nLicenseMode = -1;
+ //}}AFX_DATA_INIT
+
+ m_strServerName = _T("");
+ m_strProduct = _T("");
+ m_dwEnterFlags = 0;
+ m_nLicenseMode = 0; // per seat
+
+ m_bAreCtrlsInitialized = FALSE;
+
+ m_hLls = NULL;
+ m_hEnterpriseLls = NULL;
+}
+
+CNewLicenseDialog::~CNewLicenseDialog()
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+
+ if ( NULL != m_hEnterpriseLls )
+ {
+ LlsClose( m_hEnterpriseLls );
+ }
+}
+
+void CNewLicenseDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CNewLicenseDialog)
+ DDX_Control(pDX, IDC_NEW_LICENSE_COMMENT, m_comEdit);
+ DDX_Control(pDX, IDC_NEW_LICENSE_QUANTITY, m_licEdit);
+ DDX_Control(pDX, IDC_NEW_LICENSE_SPIN, m_spinCtrl);
+ DDX_Control(pDX, IDC_NEW_LICENSE_PRODUCT, m_productList);
+ DDX_Text(pDX, IDC_NEW_LICENSE_COMMENT, m_strComment);
+ DDX_Text(pDX, IDC_NEW_LICENSE_QUANTITY, m_nLicenses);
+ DDV_MinMaxLong(pDX, m_nLicenses, m_nLicensesMin, 999999);
+ DDX_CBString(pDX, IDC_NEW_LICENSE_PRODUCT, m_strProduct);
+ DDX_Radio(pDX, IDC_PER_SEAT, m_nLicenseMode);
+ //}}AFX_DATA_MAP
+}
+
+
+void CNewLicenseDialog::InitCtrls()
+
+/*++
+
+Routine Description:
+
+ Initializes dialog controls.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_licEdit.SetFocus();
+ m_licEdit.SetSel(0,-1);
+ m_licEdit.LimitText(6);
+
+ m_comEdit.LimitText(256);
+
+ m_spinCtrl.SetRange(0, UD_MAXVAL);
+
+ // if license mode set by application, don't let user change it
+ if ( m_dwEnterFlags & ( CCF_ENTER_FLAG_PER_SEAT_ONLY | CCF_ENTER_FLAG_PER_SERVER_ONLY ) )
+ {
+ if ( m_dwEnterFlags & CCF_ENTER_FLAG_PER_SEAT_ONLY )
+ {
+ m_nLicenseMode = 0;
+ OnPerSeat();
+ }
+ else
+ {
+ m_nLicenseMode = 1;
+ OnPerServer();
+ }
+
+ GetDlgItem( IDC_PER_SERVER )->EnableWindow( FALSE );
+ GetDlgItem( IDC_PER_SEAT )->EnableWindow( FALSE );
+ UpdateData( FALSE );
+ }
+
+ m_bAreCtrlsInitialized = TRUE;
+}
+
+
+void CNewLicenseDialog::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Displays status and aborts if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ AbortDialog(); // bail...
+ }
+}
+
+
+void CNewLicenseDialog::AbortDialog()
+
+/*++
+
+Routine Description:
+
+ Aborts dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EndDialog(IDABORT);
+}
+
+
+BOOL CNewLicenseDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ if (!m_bAreCtrlsInitialized)
+ {
+ InitCtrls();
+
+ if (!RefreshCtrls())
+ {
+ AbortDialogIfNecessary(); // display error...
+ }
+ }
+
+ return TRUE;
+}
+
+
+void CNewLicenseDialog::OnOK()
+
+/*++
+
+Routine Description:
+
+ Creates a new license for product.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ if (!IsQuantityValid())
+ return;
+
+ if (m_strProduct.IsEmpty())
+ return;
+
+ if ( m_nLicenseMode )
+ {
+ CPerServerLicensingDialog psLicDlg;
+ psLicDlg.m_strProduct = m_strProduct;
+ psLicDlg.m_strLicenses.Format( TEXT( "%u" ), m_nLicenses );
+
+ if ( psLicDlg.DoModal() != IDOK )
+ return;
+ }
+ else
+ {
+ CPerSeatLicensingDialog psLicDlg;
+ psLicDlg.m_strProduct = m_strProduct;
+
+ if ( psLicDlg.DoModal() != IDOK )
+ return;
+ }
+
+ NTSTATUS NtStatus = AddLicense();
+
+ if ( STATUS_SUCCESS == NtStatus )
+ {
+ EndDialog(IDOK);
+ }
+ else if ( ( ERROR_CANCELLED != NtStatus ) && ( STATUS_CANCELLED != NtStatus ) )
+ {
+ AbortDialogIfNecessary(); // display error...
+ }
+ }
+}
+
+
+BOOL CNewLicenseDialog::RefreshCtrls()
+
+/*++
+
+Routine Description:
+
+ Refreshs list of products available.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns true if controls refreshed.
+
+--*/
+
+{
+ int iProductInCB = CB_ERR;
+
+ BeginWaitCursor(); // hourglass...
+
+ if ( !m_strProduct.IsEmpty() )
+ {
+ iProductInCB = m_productList.AddString(m_strProduct);
+ }
+ else if ( ConnectServer() )
+ {
+ GetProductList();
+ }
+
+ m_productList.SetCurSel((iProductInCB == CB_ERR) ? 0 : iProductInCB);
+
+ EndWaitCursor(); // hourglass...
+
+ return TRUE;
+}
+
+
+void CNewLicenseDialog::OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Notification handler for UDN_DELTAPOS.
+
+Arguments:
+
+ pNMHDR - notification header.
+ pResult - return code.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateData(TRUE); // get data
+
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ if (m_nLicenses < 0)
+ {
+ m_nLicenses = 0;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > 999999)
+ {
+ m_nLicenses = 999999;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+void CNewLicenseDialog::OnUpdateQuantity()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ long nLicensesOld = m_nLicenses;
+
+ if (!IsQuantityValid())
+ {
+ m_nLicenses = nLicensesOld;
+
+ UpdateData(FALSE);
+
+ m_licEdit.SetFocus();
+ m_licEdit.SetSel(0,-1);
+
+ ::MessageBeep(MB_OK);
+ }
+}
+
+
+BOOL CNewLicenseDialog::IsQuantityValid()
+
+/*++
+
+Routine Description:
+
+ Wrapper around UpdateData(TRUE).
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ BOOL bIsValid;
+
+ m_nLicensesMin = 1; // raise minimum...
+
+ bIsValid = UpdateData(TRUE);
+
+ m_nLicensesMin = 0; // reset minimum...
+
+ return bIsValid;
+}
+
+
+BOOL CNewLicenseDialog::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ NTSTATUS nt = ConnectTo( FALSE, pszServerName, &m_hLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+BOOL CNewLicenseDialog::ConnectEnterprise()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the enterprise server
+ of the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hEnterpriseLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ NTSTATUS nt = ConnectTo( !( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ), pszServerName, &m_hEnterpriseLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hEnterpriseLls )
+ {
+ theApp.DisplayLastError();
+
+ // not being able to connect to the enterprise
+ // is not a fatal error
+ // EndDialog( IDABORT );
+ }
+
+ return ( NULL != m_hEnterpriseLls );
+}
+
+
+NTSTATUS CNewLicenseDialog::ConnectTo( BOOL bUseEnterprise, LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server or that
+ on the given server's enterprise server.
+
+Arguments:
+
+ bUseEnterprise (BOOL)
+ If TRUE, connect to the enterprise server of the target server, not to
+ the target server itself.
+ pszServerName (LPTSTR)
+ The target server. A NULL value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !bUseEnterprise )
+ {
+ nt = ::LlsConnect( pszServerName, phLls );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnect = NULL;
+
+ nt = ::LlsConnectEnterprise( pszServerName, phLls, 0, (LPBYTE *) &pConnect );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( pConnect );
+ }
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ theApp.SetLastLlsError( nt );
+
+ return nt;
+}
+
+
+void CNewLicenseDialog::GetProductList()
+
+/*++
+
+Routine Description:
+
+ Fill the product list box with the unsecure product names from the
+ target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ // get list of products from license server, inserting into listbox
+ m_productList.ResetContent();
+
+ DWORD dwResumeHandle = 0;
+ DWORD dwTotalEntries;
+ DWORD dwEntriesRead;
+ NTSTATUS nt;
+
+ do
+ {
+ LPBYTE pReturnBuffer = NULL;
+ BOOL bListProduct;
+
+ nt = ::LlsProductEnum( m_hLls,
+ 0,
+ &pReturnBuffer,
+ 0x4000,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &dwResumeHandle );
+ theApp.SetLastLlsError( nt );
+
+ if ( ( STATUS_SUCCESS == nt ) || ( STATUS_MORE_ENTRIES == nt ) )
+ {
+ LLS_PRODUCT_INFO_0 * pProductInfo = (LLS_PRODUCT_INFO_0 *) pReturnBuffer;
+
+ for ( DWORD i=0; i < dwEntriesRead; i++ )
+ {
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // 3.51-level server; all products are unsecure, so add all
+ bListProduct = TRUE;
+ }
+ else
+ {
+ // only list this product if it's unsecure
+ BOOL bIsSecure;
+
+ bListProduct = ( STATUS_SUCCESS != ::LlsProductSecurityGet( m_hLls, pProductInfo[i].Product, &bIsSecure ) )
+ || ( !bIsSecure );
+ }
+
+ if ( bListProduct )
+ {
+ m_productList.AddString( pProductInfo[i].Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo[i].Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo );
+ }
+
+ } while ( STATUS_MORE_ENTRIES == nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ // still connected?
+ AbortDialogIfNecessary();
+ }
+
+ // restore previous edit selection
+ UpdateData( FALSE );
+ }
+}
+
+
+NTSTATUS CNewLicenseDialog::AddLicense()
+
+/*++
+
+Routine Description:
+
+ Add the license described in the dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ STATUS_SUCCESS
+ ERROR_CANCELLED
+ ERROR_NOT_ENOUGH_MEMORY
+ NT status code
+ Win error
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !ConnectServer() )
+ {
+ nt = ERROR_CANCELLED;
+ // don't set last error
+ // (preserve that set by the failed connect attempt)
+ }
+ else
+ {
+ LPTSTR pszProductName = m_strProduct.GetBuffer(0);
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+ LPTSTR pszComment = m_strComment.GetBuffer(0);
+
+ if ( ( NULL == pszProductName ) || ( NULL == pszServerName ) || ( NULL == pszComment ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ LLS_HANDLE hLls = m_hLls;
+
+ if ( ( 0 != m_nLicenseMode )
+ || ( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ) )
+ {
+ // per server mode, or per seat installed on ES; target server correct
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ // per seat mode; make sure we're installing on the enterprise server
+ PLLS_CONNECT_INFO_0 pConnectInfo = NULL;
+
+ BeginWaitCursor();
+
+ nt = ::LlsEnterpriseServerFind( pszServerName, 0, (LPBYTE *) &pConnectInfo );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( lstrcmpi( pszServerName, pConnectInfo->EnterpriseServer ) )
+ {
+ // not the enterprise server; make sure that per seat
+ // licenses are being sent to the right place (i.e., the
+ // enterprise server)
+ int nResponse;
+
+ nResponse = AfxMessageBox( IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE, MB_ICONINFORMATION | MB_OKCANCEL, 0 );
+
+ if ( IDOK == nResponse )
+ {
+ if ( !ConnectEnterprise() )
+ {
+ nt = ERROR_CANCELLED;
+ // don't set last error
+ // (preserve that set by the failed connect attempt)
+ }
+ else
+ {
+ hLls = m_hEnterpriseLls;
+ }
+ }
+ else
+ {
+ nt = ERROR_CANCELLED;
+ theApp.SetLastError( nt );
+ }
+ }
+ }
+
+ // free memory allocated for us by Lls
+ LlsFreeMemory( pConnectInfo );
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // we've determined the real target server
+
+ // get name of user entering the certificate
+ TCHAR szUserName[ 64 ];
+ DWORD cchUserName;
+ BOOL ok;
+
+ cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+ ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ // enter certificate into system
+ if ( 0 == m_nLicenseMode )
+ {
+ // add 3.51 style per seat license
+ LLS_LICENSE_INFO_0 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszProductName;
+ lic.Comment = pszComment;
+ lic.Admin = szUserName;
+ lic.Quantity = m_nLicenses;
+ lic.Date = 0;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( hLls, 0, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+ }
+ else
+ {
+ // add 3.51 style per server license (blek)
+ HKEY hKeyLocalMachine;
+
+ nt = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ nt = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+ else
+ {
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // okay, now we have to find the product corresponding to this display name (ickie)
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ nt = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ HKEY hKeyProduct;
+
+ nt = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ nt = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ( ERROR_SUCCESS == nt )
+ && ( REG_SZ == dwType )
+ && !lstrcmpi( szDisplayName, pszProductName ) )
+ {
+ // YES! we found the product key
+ // now add the concurrent licenses
+ bFoundKey = TRUE;
+
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ nt = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_FILE_NOT_FOUND == nt ) || ( ERROR_PATH_NOT_FOUND == nt ) )
+ {
+ // okay if the value doesn't exist
+ dwConcurrentLimit = 0;
+ nt = ERROR_SUCCESS;
+ }
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ if ( (LONG)dwConcurrentLimit + (LONG)m_nLicenses > 0 )
+ {
+ dwConcurrentLimit += m_nLicenses;
+
+ nt = RegSetValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), 0, REG_DWORD, (LPBYTE) &dwConcurrentLimit, sizeof( dwConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+
+ // even if an error occurred while trying to find the right product key,
+ // we should continue to search the rest
+ if ( !bFoundKey )
+ {
+ nt = ERROR_SUCCESS;
+ }
+ }
+ } while ( !bFoundKey && ( ERROR_SUCCESS == nt ) );
+
+ if ( ERROR_NO_MORE_ITEMS == nt )
+ {
+ // trying to install per server licenses for this box, but
+ // the application isn't installed locally
+ AfxMessageBox( IDS_PER_SERVER_APP_NOT_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+
+ nt = ERROR_CANCELLED;
+ }
+ else if ( ERROR_SUCCESS != nt )
+ {
+ theApp.SetLastError( nt );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+ }
+ }
+ }
+
+ if ( NULL != pszProductName ) m_strProduct.ReleaseBuffer();
+ if ( NULL != pszServerName ) m_strServerName.ReleaseBuffer();
+ if ( NULL != pszComment ) m_strComment.ReleaseBuffer();
+ }
+
+ return nt;
+}
+
+
+void CNewLicenseDialog::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CNewLicenseDialog::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CNewLicenseDialog::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CNewLicenseDialog::OnPerSeat()
+
+/*++
+
+Routine Description:
+
+ Handler for per seat radio button selection.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NEW_LICENSE_COMMENT )->EnableWindow( TRUE );
+}
+
+
+void CNewLicenseDialog::OnPerServer()
+
+/*++
+
+Routine Description:
+
+ Handler for per server radio button selection.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NEW_LICENSE_COMMENT )->EnableWindow( FALSE );
+}
+
+
+DWORD CNewLicenseDialog::CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with no certificate (3.51-style).
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProduct = pszProductName ? pszProductName : "";
+ // pszVendor is not used
+ m_dwEnterFlags = dwFlags;
+
+ if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
+
+
+DWORD CNewLicenseDialog::CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via 3.51 or CertificateEnter().
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProduct = pLicenseInfo->Product;
+
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ // error message already displayed
+ }
+ else
+ {
+ if ( LLS_LICENSE_MODE_ALLOW_PER_SERVER & pLicenseInfo->AllowedModes )
+ {
+ // remove per server licenses
+ HKEY hKeyLocalMachine;
+
+ LPTSTR pszUniServerName = m_strServerName.GetBuffer(0);
+
+ if ( NULL == pszUniServerName )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ dwError = RegConnectRegistry( pszUniServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ dwError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+ else
+ {
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // okay, now we have to find the product corresponding to this display name (ickie)
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ dwError = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ HKEY hKeyProduct;
+
+ dwError = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ dwError = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ( ERROR_SUCCESS == dwError )
+ && ( REG_SZ == dwType )
+ && !lstrcmpi( szDisplayName, m_strProduct ) )
+ {
+ // YES! we found the product key
+ // now subtract the concurrent licenses
+ bFoundKey = TRUE;
+
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ dwError = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_SUCCESS == dwError ) && ( REG_DWORD == dwType ) )
+ {
+ if ( (LONG)dwConcurrentLimit + (LONG)m_nLicenses > 0 )
+ {
+ if ( pLicenseInfo->Quantity > (LONG)dwConcurrentLimit )
+ {
+ dwConcurrentLimit = 0;
+ }
+ else
+ {
+ dwConcurrentLimit -= pLicenseInfo->Quantity;
+ }
+
+ dwError = RegSetValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), 0, REG_DWORD, (LPBYTE) &dwConcurrentLimit, sizeof( dwConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+
+ // even if an error occurred while trying to find the right product key,
+ // we should continue to search the rest
+ if ( !bFoundKey )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ }
+ } while ( !bFoundKey && ( ERROR_SUCCESS == dwError ) );
+
+ if ( ERROR_SUCCESS != dwError )
+ {
+ theApp.SetLastError( dwError );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+ }
+ else
+ {
+ // remove per seat licenses
+ CString strComment;
+ strComment.LoadString( IDS_NO_REMOVE_COMMENT );
+
+ LPTSTR pszUniProductName = m_strProduct.GetBuffer(0);
+ LPTSTR pszUniComment = strComment.GetBuffer(0);
+
+ if ( ( NULL == pszUniProductName ) || ( NULL == pszUniComment ) )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ TCHAR szUserName[ 256 ];
+ DWORD cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+
+ BOOL ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ dwError = GetLastError();
+ }
+ else
+ {
+ NTSTATUS nt;
+ LLS_LICENSE_INFO_0 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszUniProductName;
+ lic.Comment = pszUniComment;
+ lic.Admin = szUserName;
+ lic.Quantity = -pLicenseInfo->Quantity;
+ lic.Date = 0;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( m_hLls, 0, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ dwError = (DWORD) nt;
+ }
+ }
+
+ if ( NULL != pszUniProductName ) m_strProduct.ReleaseBuffer();
+ if ( NULL != pszUniComment ) strComment.ReleaseBuffer();
+ }
+
+ if ( ( ERROR_SUCCESS != dwError ) && ( ERROR_CANCELLED != dwError ) )
+ {
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/nlicdlg.h b/private/net/svcdlls/lls/ccfapi32/nlicdlg.h
new file mode 100644
index 000000000..e45efafda
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/nlicdlg.h
@@ -0,0 +1,102 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ nlicdlg.h
+
+Abstract:
+
+ New license dialog implementation.
+
+Author:
+
+ Don Ryan (donryan) 02-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 14-Dec-1995
+ Moved over from LLSMGR, added ability to purchase per server licenses,
+ added license removal functionality.
+
+--*/
+
+#ifndef _NLICDLG_H_
+#define _NLICDLG_H_
+
+class CNewLicenseDialog : public CDialog
+{
+private:
+ BOOL m_bAreCtrlsInitialized;
+
+public:
+ CString m_strServerName;
+
+ LLS_HANDLE m_hLls;
+ LLS_HANDLE m_hEnterpriseLls;
+
+ DWORD m_dwEnterFlags;
+
+ //{{AFX_DATA(CNewLicenseDialog)
+ enum { IDD = IDD_NEW_LICENSE };
+ CEdit m_comEdit;
+ CEdit m_licEdit;
+ CSpinButtonCtrl m_spinCtrl;
+ CComboBox m_productList;
+ CString m_strComment;
+ long m_nLicenses;
+ long m_nLicensesMin;
+ CString m_strProduct;
+ int m_nLicenseMode;
+ //}}AFX_DATA
+
+public:
+ CNewLicenseDialog(CWnd* pParent = NULL);
+ ~CNewLicenseDialog();
+
+ // CCF API
+ DWORD CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo );
+
+ NTSTATUS ConnectTo( BOOL bUseEnterprise, LPTSTR pszServerName, PLLS_HANDLE phLls );
+ BOOL ConnectServer();
+ BOOL ConnectEnterprise();
+
+ void GetProductList();
+ NTSTATUS AddLicense();
+
+ void AbortDialogIfNecessary();
+ void AbortDialog();
+
+ void InitCtrls();
+ BOOL RefreshCtrls();
+
+ BOOL IsQuantityValid();
+
+ //{{AFX_VIRTUAL(CNewLicenseDialog)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CNewLicenseDialog)
+ virtual void OnOK();
+ virtual BOOL OnInitDialog();
+ afx_msg void OnDestroy();
+ afx_msg void OnDeltaPosSpin(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnUpdateQuantity();
+ afx_msg void OnHelp();
+ afx_msg void OnPerSeat();
+ afx_msg void OnPerServer();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _NLICDLG_H_
diff --git a/private/net/svcdlls/lls/ccfapi32/paper.cpp b/private/net/svcdlls/lls/ccfapi32/paper.cpp
new file mode 100644
index 000000000..919db7d6c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/paper.cpp
@@ -0,0 +1,1389 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.cpp
+
+Abstract:
+
+ Paper certificate dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include <stdlib.h>
+
+#include "resource.h"
+#include "ccfapi.h"
+#include "paper.h"
+#include "md4.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+
+static void MD4UpdateDword( MD4_CTX * pCtx, DWORD dwValue );
+
+
+CPaperSourceDlg::CPaperSourceDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CPaperSourceDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPaperSourceDlg)
+ m_strActivationCode = _T("");
+ m_strKeyCode = _T("");
+ m_strSerialNumber = _T("");
+ m_strVendor = _T("");
+ m_strProductName = _T("");
+ m_strComment = _T("");
+ m_nDontInstallAllLicenses = -1;
+ m_nLicenses = 0;
+ m_nLicenseMode = -1;
+ //}}AFX_DATA_INIT
+
+ m_bProductListRetrieved = FALSE;
+ m_hLls = NULL;
+ m_hEnterpriseLls = NULL;
+ m_dwEnterFlags = 0;
+
+ m_nLicenses = 1;
+ m_nDontInstallAllLicenses = 0;
+
+ m_strServerName = _T("");
+}
+
+
+CPaperSourceDlg::~CPaperSourceDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+
+ if ( NULL != m_hEnterpriseLls )
+ {
+ LlsClose( m_hEnterpriseLls );
+ }
+}
+
+
+void CPaperSourceDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPaperSourceDlg)
+ DDX_Control(pDX, IDC_SPIN_LICENSES, m_spinLicenses);
+ DDX_Control(pDX, IDC_PRODUCT_NAME, m_cboxProductName);
+ DDX_Text(pDX, IDC_ACTIVATION_CODE, m_strActivationCode);
+ DDX_Text(pDX, IDC_KEY_CODE, m_strKeyCode);
+ DDX_Text(pDX, IDC_SERIAL_NUMBER, m_strSerialNumber);
+ DDX_Text(pDX, IDC_VENDOR, m_strVendor);
+ DDX_CBString(pDX, IDC_PRODUCT_NAME, m_strProductName);
+ DDX_Text(pDX, IDC_COMMENT, m_strComment);
+ DDX_Radio(pDX, IDC_ALL_LICENSES, m_nDontInstallAllLicenses);
+ DDX_Text(pDX, IDC_NUM_LICENSES, m_nLicenses);
+ DDX_Radio(pDX, IDC_PER_SEAT, m_nLicenseMode);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CPaperSourceDlg, CDialog)
+ //{{AFX_MSG_MAP(CPaperSourceDlg)
+ ON_EN_UPDATE(IDC_ACTIVATION_CODE, OnUpdateActivationCode)
+ ON_EN_UPDATE(IDC_KEY_CODE, OnUpdateKeyCode)
+ ON_EN_UPDATE(IDC_VENDOR, OnUpdateVendor)
+ ON_EN_UPDATE(IDC_SERIAL_NUMBER, OnUpdateSerialNumber)
+ ON_CBN_EDITUPDATE(IDC_PRODUCT_NAME, OnUpdateProductName)
+ ON_CBN_DROPDOWN(IDC_PRODUCT_NAME, OnDropDownProductName)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_WM_DESTROY()
+ ON_BN_CLICKED(IDC_ALL_LICENSES, OnAllLicenses)
+ ON_BN_CLICKED(IDC_SOME_LICENSES, OnSomeLicenses)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LICENSES, OnDeltaPosSpinLicenses)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void CPaperSourceDlg::OnUpdateActivationCode()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of activation code.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateKeyCode()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of key code.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateProductName()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of product name.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateSerialNumber()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of serial number.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::OnUpdateVendor()
+
+/*++
+
+Routine Description:
+
+ Message handler for EN_UPDATE of vendor.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ EnableOrDisableOK();
+}
+
+
+void CPaperSourceDlg::EnableOrDisableOK()
+
+/*++
+
+Routine Description:
+
+ Enable or diable OK button depending upon whether all necessary dialog data
+ has been supplied by the user.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL bEnableOK;
+
+ UpdateData( TRUE );
+
+ bEnableOK = !m_strActivationCode.IsEmpty()
+ && !m_strKeyCode.IsEmpty()
+ && !m_strProductName.IsEmpty()
+ && !m_strSerialNumber.IsEmpty()
+ && !m_strVendor.IsEmpty()
+ && ( ( 0 == m_nLicenseMode )
+ || ( 1 == m_nLicenseMode ) );
+
+ GetDlgItem( IDOK )->EnableWindow( bEnableOK );
+}
+
+
+BOOL CPaperSourceDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ EnableOrDisableOK();
+
+ if ( m_nDontInstallAllLicenses )
+ {
+ OnSomeLicenses();
+ }
+ else
+ {
+ OnAllLicenses();
+ }
+
+ m_spinLicenses.SetRange( 1, MAX_NUM_LICENSES );
+
+ // ghost out items that were passed to us from the application
+ if ( !m_strProductName.IsEmpty() )
+ GetDlgItem( IDC_PRODUCT_NAME )->EnableWindow( FALSE );
+ if ( !m_strVendor.IsEmpty() )
+ GetDlgItem( IDC_VENDOR )->EnableWindow( FALSE );
+
+ // if license mode set by application, don't let user change it
+ if ( m_dwEnterFlags & ( CCF_ENTER_FLAG_PER_SEAT_ONLY | CCF_ENTER_FLAG_PER_SERVER_ONLY ) )
+ {
+ m_nLicenseMode = ( m_dwEnterFlags & CCF_ENTER_FLAG_PER_SEAT_ONLY ) ? 0 : 1;
+ GetDlgItem( IDC_PER_SERVER )->EnableWindow( FALSE );
+ GetDlgItem( IDC_PER_SEAT )->EnableWindow( FALSE );
+ UpdateData( FALSE );
+ }
+
+ return TRUE;
+}
+
+
+void CPaperSourceDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Creates a new license for product.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if( UpdateData( TRUE ) )
+ {
+ // verify activation code
+ DWORD nActivationCode = wcstoul( m_strActivationCode, NULL, 16 );
+
+ if ( nActivationCode != ComputeActivationCode() )
+ {
+ AfxMessageBox( IDS_BAD_ACTIVATION_CODE, MB_ICONEXCLAMATION | MB_OK, 0 );
+ }
+ else if ( ( m_nLicenses < 1 ) || ( m_nLicenses > MAX_NUM_LICENSES ) )
+ {
+ AfxMessageBox( IDS_INVALID_NUM_LICENSES, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_NUM_LICENSES )->SetActiveWindow();
+ }
+ else
+ {
+ DWORD dwKeyCode = KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 );
+
+ if ( m_nDontInstallAllLicenses
+ && ( (DWORD)m_nLicenses > KeyCodeToNumLicenses( dwKeyCode ) ) )
+ {
+ // can't install more licenses than are in the certificate
+ AfxMessageBox( IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_NUM_LICENSES )->SetActiveWindow();
+ }
+ else if ( !( ( 1 << m_nLicenseMode ) & KeyCodeToModesAllowed( dwKeyCode ) ) )
+ {
+ // can't install certificate in a mode that's not allowed by the key code
+ AfxMessageBox( IDS_LICENSE_MODE_NOT_ALLOWED, MB_ICONEXCLAMATION | MB_OK, 0 );
+
+ GetDlgItem( IDC_PER_SEAT )->SetActiveWindow();
+ }
+ else
+ {
+ nt = AddLicense();
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ CDialog::OnOK();
+ }
+ else if ( ( ERROR_CANCELLED != nt ) && ( STATUS_CANCELLED != nt ) )
+ {
+ AbortDialogIfNecessary();
+ }
+ }
+ }
+ }
+}
+
+
+void CPaperSourceDlg::GetProductList()
+
+/*++
+
+Routine Description:
+
+ Retrieves the list of installed product from the license server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( ConnectServer() )
+ {
+ // save edit selection
+ UpdateData( TRUE );
+
+ // get list of products from license server, inserting into listbox
+ m_cboxProductName.ResetContent();
+
+ DWORD dwResumeHandle = 0;
+ DWORD dwTotalEntries;
+ DWORD dwEntriesRead;
+ NTSTATUS nt;
+
+ do
+ {
+ LPBYTE pReturnBuffer = NULL;
+
+ nt = ::LlsProductEnum( m_hLls,
+ 0,
+ &pReturnBuffer,
+ LLS_PREFERRED_LENGTH,
+ &dwEntriesRead,
+ &dwTotalEntries,
+ &dwResumeHandle );
+ theApp.SetLastLlsError( nt );
+
+ if ( ( STATUS_SUCCESS == nt ) || ( STATUS_MORE_ENTRIES == nt ) )
+ {
+ LLS_PRODUCT_INFO_0 * pProductInfo = (LLS_PRODUCT_INFO_0 *) pReturnBuffer;
+
+ for ( DWORD i=0; i < dwEntriesRead; i++ )
+ {
+ m_cboxProductName.AddString( pProductInfo[i].Product );
+
+ ::LlsFreeMemory( pProductInfo->Product );
+ }
+
+ ::LlsFreeMemory( pProductInfo );
+ }
+
+ } while ( STATUS_MORE_ENTRIES == nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ // still connected?
+ AbortDialogIfNecessary();
+ }
+
+ // restore previous edit selection
+ UpdateData( FALSE );
+ }
+}
+
+
+void CPaperSourceDlg::OnDropDownProductName()
+
+/*++
+
+Routine Description:
+
+ Handler for CBN_DROPDOWN of product name combo box.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( !m_bProductListRetrieved )
+ {
+ BeginWaitCursor();
+ GetProductList();
+ EndWaitCursor();
+
+ m_bProductListRetrieved = TRUE;
+ }
+}
+
+
+BOOL CPaperSourceDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ ConnectTo( FALSE, m_strServerName, &m_hLls );
+
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // we connected to the machine, but it doesn't support secure certificates
+ // we should not get here under normal circumstances, since the select
+ // source dialog should not allow the user to choose the paper source
+ // under such circumstances
+ LlsClose( m_hLls );
+ m_hLls = NULL;
+
+ theApp.SetLastLlsError( STATUS_INVALID_LEVEL );
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+BOOL CPaperSourceDlg::ConnectEnterprise()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the enterprise server
+ of the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hEnterpriseLls )
+ {
+ ConnectTo( !( m_dwEnterFlags & CCF_ENTER_FLAG_SERVER_IS_ES ), m_strServerName, &m_hEnterpriseLls );
+
+ if ( NULL == m_hEnterpriseLls )
+ {
+ theApp.DisplayLastError();
+ }
+ }
+
+ return ( NULL != m_hEnterpriseLls );
+}
+
+
+NTSTATUS CPaperSourceDlg::ConnectTo( BOOL bUseEnterprise, CString strServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server or that
+ on the given server's enterprise server.
+
+Arguments:
+
+ bUseEnterprise (BOOL)
+ If TRUE, connect to the enterprise server of the target server, not to
+ the target server itself.
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ LPTSTR pszServerName = NULL;
+
+ if ( !strServerName.IsEmpty() )
+ {
+ pszServerName = strServerName.GetBuffer(0);
+
+ if ( NULL == pszServerName )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( !bUseEnterprise )
+ {
+ nt = ::LlsConnect( pszServerName, phLls );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnect = NULL;
+
+ nt = ::LlsConnectEnterprise( pszServerName, phLls, 0, (LPBYTE *) &pConnect );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( pConnect );
+ }
+ }
+
+ theApp.SetLastLlsError( nt );
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+void CPaperSourceDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CPaperSourceDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CPaperSourceDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CPaperSourceDlg::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Displays status and aborts if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ EndDialog( IDABORT );
+ }
+}
+
+
+DWORD CPaperSourceDlg::ComputeActivationCode()
+
+/*++
+
+Routine Description:
+
+ Return the computed activation code corresponding to the entered
+ certificate.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ DWORD.
+
+--*/
+
+{
+ MD4_CTX ctx;
+ DWORD dw;
+ UCHAR digest[ 16 ];
+ DWORD adwCodeSeg[ sizeof( digest ) / sizeof( DWORD ) ];
+ int nCodeSeg;
+ int nCodeSegByte;
+ DWORD dwActivationCode ;
+ CHAR szAnsiName[ 128 ];
+
+ MD4Init( &ctx );
+
+ ZeroMemory( szAnsiName, sizeof( szAnsiName ) );
+ wcstombs( szAnsiName, m_strProductName, sizeof( szAnsiName ) - 1 );
+ MD4Update( &ctx, (LPBYTE)szAnsiName, strlen( szAnsiName ) );
+
+ ZeroMemory( szAnsiName, sizeof( szAnsiName ) );
+ wcstombs( szAnsiName, m_strVendor, sizeof( szAnsiName ) - 1 );
+ MD4Update( &ctx, (LPBYTE)szAnsiName, strlen( szAnsiName ) );
+
+ MD4UpdateDword( &ctx, 14721776 );
+ MD4UpdateDword( &ctx, wcstoul( m_strSerialNumber, NULL, 10 ) );
+ MD4UpdateDword( &ctx, 19721995 );
+ MD4UpdateDword( &ctx, KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 ) );
+
+ MD4Final( digest, &ctx );
+
+ // convert digest into platform-independent array of DWORDs
+ for ( nCodeSeg=0; nCodeSeg < sizeof( adwCodeSeg ) / sizeof( *adwCodeSeg ); nCodeSeg++ )
+ {
+ adwCodeSeg[ nCodeSeg ] = 0;
+
+ for ( nCodeSegByte=0; nCodeSegByte < sizeof( *adwCodeSeg ); nCodeSegByte++ )
+ {
+ adwCodeSeg[ nCodeSeg ] <<= 8;
+ adwCodeSeg[ nCodeSeg ] |= digest[ nCodeSeg * sizeof( *adwCodeSeg ) + nCodeSegByte ];
+ }
+ }
+
+ dwActivationCode = ( adwCodeSeg[ 0 ] + adwCodeSeg[ 1 ] ) ^ ( adwCodeSeg[ 2 ] - adwCodeSeg[ 3 ] );
+
+ return dwActivationCode;
+}
+
+
+NTSTATUS CPaperSourceDlg::AddLicense()
+
+/*++
+
+Routine Description:
+
+ Enter a new license into the system.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ STATUS_SUCCESS
+ ERROR_NOT_ENOUGH_MEMORY
+ ERROR_CANCELLED
+ NT status code
+ Win error
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( !ConnectServer() )
+ {
+ nt = ERROR_CANCELLED;
+ }
+ else
+ {
+ LPTSTR pszProductName = m_strProductName.GetBuffer(0);
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+ LPTSTR pszComment = m_strComment.GetBuffer(0);
+ LPTSTR pszVendor = m_strVendor.GetBuffer(0);
+
+ if ( ( NULL == pszProductName ) || ( NULL == pszServerName ) || ( NULL == pszComment ) || ( NULL == pszVendor ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ // handles for LLS on machine to receive licenses
+ // (if per seat, these will be changed to correspond to the enterprise server)
+ LLS_HANDLE hLls = NULL;
+
+ if ( 0 == m_nLicenseMode )
+ {
+ // per seat mode; install on enterprise server
+ BeginWaitCursor();
+ BOOL ok = ConnectEnterprise();
+ EndWaitCursor();
+
+ if ( !ok )
+ {
+ // can't connect to enterprise server
+ nt = ERROR_CANCELLED;
+ }
+ else if ( !LlsCapabilityIsSupported( m_hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // enterprise server doesn't support secure certificates
+ AfxMessageBox( IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT, MB_ICONSTOP | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ }
+ else
+ {
+ hLls = m_hEnterpriseLls;
+ nt = STATUS_SUCCESS;
+ }
+ }
+ else
+ {
+ // per server mode; install on target server
+ hLls = m_hLls;
+ nt = STATUS_SUCCESS;
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ TCHAR szUserName[ 64 ];
+ DWORD cchUserName;
+ BOOL ok;
+
+ cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+ ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // enter certificate into system
+ DWORD nKeyCode = KEY_CODE_MASK ^ wcstoul( m_strKeyCode, NULL, 10 );
+
+ // --------- fill in certificate info ---------
+ LLS_LICENSE_INFO_1 lic;
+
+ ZeroMemory( &lic, sizeof( lic ) );
+
+ lic.Product = pszProductName;
+ lic.Vendor = pszVendor;
+ lic.Comment = pszComment;
+ lic.Admin = szUserName;
+ lic.Quantity = m_nDontInstallAllLicenses ? m_nLicenses : KeyCodeToNumLicenses( nKeyCode );
+ lic.Date = 0;
+ lic.AllowedModes = 1 << m_nLicenseMode;
+ lic.CertificateID = wcstoul( m_strSerialNumber, NULL, 10 );
+ lic.Source = TEXT("Paper");
+ lic.ExpirationDate = KeyCodeToExpirationDate( nKeyCode );
+ lic.MaxQuantity = KeyCodeToNumLicenses( nKeyCode );
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( hLls, 1, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ if ( ( STATUS_OBJECT_NAME_EXISTS == nt )
+ || ( STATUS_ALREADY_COMMITTED == nt ) )
+ {
+ LLS_HANDLE hLlsForTargets = NULL;
+
+ // too many licenses of this certificate
+ if ( STATUS_OBJECT_NAME_EXISTS == nt )
+ {
+ // denied by target's local database
+ hLlsForTargets = hLls;
+ }
+ else if ( ConnectEnterprise() )
+ {
+ // denied by target's enterprise server; we're connected!
+ hLlsForTargets = m_hEnterpriseLls;
+ }
+
+ if ( NULL == hLlsForTargets )
+ {
+ // denied by enterprise server, and can't connect to it (?!)
+ AfxMessageBox( IDS_NET_LICENSES_ALREADY_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+ }
+ else
+ {
+ // too many licenses of this certificate exist in the enterprise
+ LPBYTE ReturnBuffer = NULL;
+ DWORD dwNumTargets = 0;
+
+ // get list of machines on which licenses from this certificate have been installed
+ nt = ::LlsCertificateClaimEnum( hLlsForTargets, 1, (LPBYTE) &lic, 0, &ReturnBuffer, &dwNumTargets );
+
+ if ( ( STATUS_SUCCESS == nt ) && ( dwNumTargets > 0 ) )
+ {
+ PLLS_CERTIFICATE_CLAIM_INFO_0 pTarget = (PLLS_CERTIFICATE_CLAIM_INFO_0) ReturnBuffer;
+ CString strLicenses;
+ CString strTarget;
+ CString strTargetList;
+
+ while ( dwNumTargets-- )
+ {
+ strLicenses.Format( TEXT("%d"), pTarget->Quantity );
+ AfxFormatString2( strTarget, IDS_NET_CERTIFICATE_TARGET_ENTRY, pTarget->ServerName, strLicenses );
+
+ strTargetList = strTargetList.IsEmpty() ? strTarget : ( TEXT("\n") + strTarget );
+
+ pTarget++;
+ }
+
+ CString strMessage;
+
+ AfxFormatString1( strMessage, IDS_NET_LICENSES_ALREADY_INSTALLED_ON, strTargetList );
+ AfxMessageBox( strMessage, MB_ICONSTOP | MB_OK );
+ }
+ else
+ {
+ AfxMessageBox( IDS_NET_LICENSES_ALREADY_INSTALLED, MB_ICONSTOP | MB_OK, 0 );
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ ::LlsFreeMemory( ReturnBuffer );
+ }
+ }
+
+ nt = ERROR_CANCELLED;
+ }
+ }
+ }
+ }
+
+ // don't set if !ConnectServer() -- otherwise we'll clobber the LLS error
+ theApp.SetLastError( nt );
+
+ if ( NULL != pszProductName )
+ m_strProductName.ReleaseBuffer();
+ if ( NULL != pszServerName )
+ m_strServerName.ReleaseBuffer();
+ if ( NULL != pszComment )
+ m_strComment.ReleaseBuffer();
+ if ( NULL != pszVendor )
+ m_strVendor.ReleaseBuffer();
+ }
+
+ return nt;
+}
+
+
+DWORD CPaperSourceDlg::KeyCodeToExpirationDate( DWORD dwKeyCode )
+
+/*++
+
+Routine Description:
+
+ Derive the license expiration date from the key code.
+
+Arguments:
+
+ dwKeyCode (DWORD)
+
+Return Values:
+
+ DWORD.
+
+--*/
+
+{
+ DWORD dwExpirationDate = 0;
+ USHORT usWinDate = (USHORT)( ( dwKeyCode >> 12 ) & 0x0000FFFF );
+
+ if ( 0 != usWinDate )
+ {
+ TIME_FIELDS tf;
+ LARGE_INTEGER li;
+
+ ZeroMemory( &tf, sizeof( tf ) );
+
+ tf.Year = 1980 + ( usWinDate & 0x7F );
+ tf.Month = ( ( usWinDate >> 7 ) & 0x0F );
+ tf.Day = ( usWinDate >> 11 );
+ tf.Hour = 23;
+ tf.Minute = 59;
+ tf.Second = 59;
+
+ BOOL ok;
+
+ ok = RtlTimeFieldsToTime( &tf, &li );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ ok = RtlTimeToSecondsSince1980( &li, &dwExpirationDate );
+ ASSERT( ok );
+ }
+ }
+
+ return dwExpirationDate;
+}
+
+
+void CPaperSourceDlg::OnAllLicenses()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of "install all licenses".
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NUM_LICENSES )->EnableWindow( FALSE );
+ GetDlgItem( IDC_SPIN_LICENSES )->EnableWindow( FALSE );
+}
+
+
+void CPaperSourceDlg::OnSomeLicenses()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of "install only x licenses".
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ GetDlgItem( IDC_NUM_LICENSES )->EnableWindow( TRUE );
+ GetDlgItem( IDC_SPIN_LICENSES )->EnableWindow( TRUE );
+}
+
+
+void CPaperSourceDlg::OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for UDN_DELTAPOS of number of licenses.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( UpdateData(TRUE) ) // get data
+ {
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ if (m_nLicenses < 1)
+ {
+ m_nLicenses = 1;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > MAX_NUM_LICENSES )
+ {
+ m_nLicenses = MAX_NUM_LICENSES;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+ }
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+DWORD CPaperSourceDlg::CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system with a paper certificate.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pszProductName ? pszProductName : "";
+ m_strVendor = pszVendor ? pszVendor : "";
+ m_dwEnterFlags = dwFlags;
+
+ if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
+
+
+DWORD CPaperSourceDlg::CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Remove licenses previously installed via PaperCertificateEnter().
+
+Arguments:
+
+ hWndParent (HWND)
+ HWND to the client's main window, for use as the parent window to any
+ opened dialogs. May be NULL.
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ dwLicenseLevel (DWORD)
+ Level of the LLS_LICENSE_INFO_X structure pointed to by pvLicenseInfo.
+ pvLicenseInfo (LPVOID)
+ Points to a LLS_LICENSE_INFO_X (where X is determined by dwLicenseLevel)
+ describing the licenses to be removed.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ // dwFlags unused
+
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pLicenseInfo->Product;
+
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ // error message already displayed
+ }
+ else
+ {
+ CString strComment;
+ strComment.LoadString( IDS_PAPER_REMOVE_COMMENT );
+
+ LPTSTR pszComment = strComment.GetBuffer(0);
+
+ if ( NULL == pszComment )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ TCHAR szUserName[ 256 ];
+ DWORD cchUserName = sizeof( szUserName ) / sizeof( *szUserName );
+
+ BOOL ok = GetUserName( szUserName, &cchUserName );
+
+ if ( !ok )
+ {
+ dwError = GetLastError();
+ }
+ else
+ {
+ NTSTATUS nt;
+ LLS_LICENSE_INFO_1 lic;
+
+ memcpy( &lic, pLicenseInfo, sizeof( lic ) );
+ lic.Admin = szUserName;
+ lic.Comment = pszComment;
+ lic.Date = 0;
+ lic.Quantity = -pLicenseInfo->Quantity;
+
+ BeginWaitCursor();
+
+ nt = ::LlsLicenseAdd( m_hLls, 1, (LPBYTE) &lic );
+ theApp.SetLastLlsError( nt );
+
+ EndWaitCursor();
+
+ dwError = (DWORD) nt;
+ }
+ }
+
+ strComment.ReleaseBuffer();
+
+ if ( ( ERROR_SUCCESS != dwError ) && ( ERROR_CANCELLED != dwError ) )
+ {
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ }
+
+ return dwError;
+}
+
+// MD4Update on a DWORD that is platform-independent
+static void MD4UpdateDword( MD4_CTX * pCtx, DWORD dwValue )
+{
+ BYTE b;
+
+ b = (BYTE) ( ( dwValue & 0xFF000000 ) >> 24 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x00FF0000 ) >> 16 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x0000FF00 ) >> 8 );
+ MD4Update( pCtx, &b, 1 );
+
+ b = (BYTE) ( ( dwValue & 0x000000FF ) );
+ MD4Update( pCtx, &b, 1 );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/paper.h b/private/net/svcdlls/lls/ccfapi32/paper.h
new file mode 100644
index 000000000..4f234a22e
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/paper.h
@@ -0,0 +1,145 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.h
+
+Abstract:
+
+ Paper certificate dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+// constrained by number of bits allowed for it in KeyCode (see below)
+#define MAX_NUM_LICENSES ( 4095 )
+
+// XOR mask for key code value; this is so that the high bits that are
+// usually set for the key code need not be entered -- they only need
+// be entered if the high bits are _not_ set
+#define KEY_CODE_MASK ( 0xF0000000 )
+
+
+class CPaperSourceDlg : public CDialog
+{
+// Construction
+public:
+ CPaperSourceDlg(CWnd* pParent = NULL); // standard constructor
+ ~CPaperSourceDlg();
+
+ DWORD CertificateEnter( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags );
+ DWORD CertificateRemove( LPCSTR pszServerName, DWORD dwFlags, PLLS_LICENSE_INFO_1 pLicenseInfo );
+
+ BOOL ConnectServer();
+ BOOL ConnectEnterprise();
+ NTSTATUS ConnectTo( BOOL bUseEnterprise, CString strServerName, PLLS_HANDLE phLls );
+ void AbortDialogIfNecessary();
+
+ void GetProductList();
+
+ DWORD ComputeActivationCode();
+ NTSTATUS AddLicense();
+
+ BOOL m_bProductListRetrieved;
+ DWORD m_dwEnterFlags;
+
+ CString m_strServerName;
+
+ LLS_HANDLE m_hLls;
+ LLS_HANDLE m_hEnterpriseLls;
+
+ // KeyCode format:
+ //
+ // 31....................................0
+ // | 2 | 2 | 16 | 12 |
+ // A B C D
+ //
+ // ModesAllowed = A
+ // Bit field: bit 0 = allow per seat
+ // bit 1 = allow per server
+ //
+ // FlipsAllowed = B
+ // Bit field: bit 0 = allow flip from per seat
+ // bit 1 = allow flip from per server
+ // (THIS IS CURRENTLY UNIMPLEMENTED.)
+ //
+ // ExpirationDate = C
+ // Bit field: bits 0-6 = years since 1980
+ // bits 7-10 = month (1-12)
+ // bits 11-15 = day (1-31)
+ //
+ // NumLicenses = D
+
+ DWORD KeyCodeToNumLicenses( DWORD dwKeyCode );
+ DWORD KeyCodeToFlipsAllowed( DWORD dwKeyCode );
+ DWORD KeyCodeToModesAllowed( DWORD dwKeyCode );
+ DWORD KeyCodeToExpirationDate( DWORD dwKeyCode );
+
+// Dialog Data
+ //{{AFX_DATA(CPaperSourceDlg)
+ enum { IDD = IDD_CERT_SOURCE_PAPER };
+ CSpinButtonCtrl m_spinLicenses;
+ CComboBox m_cboxProductName;
+ CString m_strActivationCode;
+ CString m_strKeyCode;
+ CString m_strSerialNumber;
+ CString m_strVendor;
+ CString m_strProductName;
+ CString m_strComment;
+ int m_nDontInstallAllLicenses;
+ int m_nLicenses;
+ int m_nLicenseMode;
+ //}}AFX_DATA
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CPaperSourceDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ void EnableOrDisableOK();
+
+ // Generated message map functions
+ //{{AFX_MSG(CPaperSourceDlg)
+ afx_msg void OnUpdateActivationCode();
+ afx_msg void OnUpdateKeyCode();
+ afx_msg void OnUpdateVendor();
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ afx_msg void OnUpdateSerialNumber();
+ afx_msg void OnUpdateProductName();
+ afx_msg void OnDropDownProductName();
+ afx_msg void OnHelp();
+ afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
+ afx_msg void OnDestroy();
+ afx_msg void OnAllLicenses();
+ afx_msg void OnSomeLicenses();
+ afx_msg void OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+///////////////////////////////////
+
+inline DWORD CPaperSourceDlg::KeyCodeToNumLicenses( DWORD dwKeyCode )
+ { return ( dwKeyCode & 0x00000FFF ); }
+
+inline DWORD CPaperSourceDlg::KeyCodeToModesAllowed( DWORD dwKeyCode )
+ { return ( dwKeyCode >> 30 ); }
+
+inline DWORD CPaperSourceDlg::KeyCodeToFlipsAllowed( DWORD dwKeyCode )
+ { return ( ( dwKeyCode >> 28 ) & 0x3 ); }
diff --git a/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp b/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp
new file mode 100644
index 000000000..0e9db97c3
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/pseatdlg.cpp
@@ -0,0 +1,154 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ pseatdlg.cpp
+
+Abstract:
+
+ Per seat confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "pseatdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CPerSeatLicensingDialog, CDialog)
+ //{{AFX_MSG_MAP(CPerSeatLicensingDialog)
+ ON_BN_CLICKED(IDC_PER_SEAT_AGREE, OnAgree)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+CPerSeatLicensingDialog::CPerSeatLicensingDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CPerSeatLicensingDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPerSeatLicensingDialog)
+ m_strStaticClients = _T("");
+ //}}AFX_DATA_INIT
+
+ m_strProduct = _T("");
+}
+
+
+void CPerSeatLicensingDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPerSeatLicensingDialog)
+ DDX_Control(pDX, IDC_PER_SEAT_AGREE, m_agreeBtn);
+ DDX_Control(pDX, IDOK, m_okBtn);
+ DDX_Text(pDX, IDC_PER_SEAT_STATIC_CLIENTS, m_strStaticClients);
+ //}}AFX_DATA_MAP
+}
+
+
+BOOL CPerSeatLicensingDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ AfxFormatString1(
+ m_strStaticClients,
+ IDS_PER_SEAT_LICENSING_1,
+ m_strProduct
+ );
+
+ CDialog::OnInitDialog();
+
+ m_agreeBtn.SetCheck(0);
+ m_okBtn.EnableWindow(FALSE);
+
+ return TRUE;
+}
+
+
+void CPerSeatLicensingDialog::OnAgree()
+
+/*++
+
+Routine Description:
+
+ Toggle okay button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_okBtn.EnableWindow(!m_okBtn.IsWindowEnabled());
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/pseatdlg.h b/private/net/svcdlls/lls/ccfapi32/pseatdlg.h
new file mode 100644
index 000000000..705c66df8
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/pseatdlg.h
@@ -0,0 +1,62 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ pseatdlg.h
+
+Abstract:
+
+ Per seat confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#ifndef _PSEATDLG_H_
+#define _PSEATDLG_H_
+
+class CPerSeatLicensingDialog : public CDialog
+{
+public:
+ CString m_strProduct;
+
+public:
+ CPerSeatLicensingDialog(CWnd* pParent = NULL);
+
+ //{{AFX_DATA(CPerSeatLicensingDialog)
+ enum { IDD = IDD_PER_SEAT_LICENSING };
+ CButton m_agreeBtn;
+ CButton m_okBtn;
+ CString m_strStaticClients;
+ //}}AFX_DATA
+
+ //{{AFX_VIRTUAL(CPerSeatLicensingDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CPerSeatLicensingDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnAgree();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _PSEATDLG_H_
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp b/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp
new file mode 100644
index 000000000..636ce7640
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/psrvdlg.cpp
@@ -0,0 +1,152 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ psrvdlg.cpp
+
+Abstract:
+
+ Per server confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "psrvdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+BEGIN_MESSAGE_MAP(CPerServerLicensingDialog, CDialog)
+ //{{AFX_MSG_MAP(CPerServerLicensingDialog)
+ ON_BN_CLICKED(IDC_PER_SERVER_AGREE, OnAgree)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+CPerServerLicensingDialog::CPerServerLicensingDialog(CWnd* pParent /*=NULL*/)
+ : CDialog(CPerServerLicensingDialog::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CPerServerLicensingDialog)
+ m_strStaticClients = _T("");
+ //}}AFX_DATA_INIT
+}
+
+
+void CPerServerLicensingDialog::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CPerServerLicensingDialog)
+ DDX_Control(pDX, IDC_PER_SERVER_AGREE, m_agreeBtn);
+ DDX_Control(pDX, IDOK, m_okBtn);
+ DDX_Text(pDX, IDC_PER_SERVER_STATIC_CLIENTS, m_strStaticClients);
+ //}}AFX_DATA_MAP
+}
+
+
+BOOL CPerServerLicensingDialog::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Message handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ AfxFormatString2(
+ m_strStaticClients,
+ IDS_PER_SERVER_LICENSING_1,
+ m_strLicenses,
+ m_strProduct
+ );
+
+ CDialog::OnInitDialog();
+
+ m_agreeBtn.SetCheck(0);
+ m_okBtn.EnableWindow(FALSE);
+
+ return TRUE;
+}
+
+
+void CPerServerLicensingDialog::OnAgree()
+
+/*++
+
+Routine Description:
+
+ Toggle okay button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_okBtn.EnableWindow(!m_okBtn.IsWindowEnabled());
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/psrvdlg.h b/private/net/svcdlls/lls/ccfapi32/psrvdlg.h
new file mode 100644
index 000000000..cc0a26553
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/psrvdlg.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ psrvdlg.h
+
+Abstract:
+
+ Per server confirmation dialog.
+
+Author:
+
+ Don Ryan (donryan) 28-Feb-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+ Robbed from LLSMGR.
+
+--*/
+
+#ifndef _PSRVDLG_H_
+#define _PSRVDLG_H_
+
+class CPerServerLicensingDialog : public CDialog
+{
+public:
+ CString m_strProduct;
+ CString m_strLicenses;
+
+public:
+ CPerServerLicensingDialog(CWnd* pParent = NULL);
+
+ //{{AFX_DATA(CPerServerLicensingDialog)
+ enum { IDD = IDD_PER_SERVER_LICENSING };
+ CButton m_agreeBtn;
+ CButton m_okBtn;
+ CString m_strStaticClients;
+ //}}AFX_DATA
+
+ //{{AFX_VIRTUAL(CPerServerLicensingDialog)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX);
+ //}}AFX_VIRTUAL
+
+protected:
+ //{{AFX_MSG(CPerServerLicensingDialog)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnAgree();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#endif // _PSRVDLG_H_
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/remdlg.cpp b/private/net/svcdlls/lls/ccfapi32/remdlg.cpp
new file mode 100644
index 000000000..2d9af28c8
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/remdlg.cpp
@@ -0,0 +1,1364 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ remdlg.cpp
+
+Abstract:
+
+ Remove licenses dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "remdlg.h"
+#include "utils.h"
+#include "licobj.h"
+#include "imagelst.h"
+#include "nlicdlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+// describes the list view layout
+static LV_COLUMN_INFO g_removeColumnInfo =
+{
+ 0, 1, LVID_REMOVE_TOTAL_COLUMNS,
+
+ {{LVID_REMOVE_SERIAL_NUMBER, IDS_SERIAL_NUMBER, LVCX_REMOVE_SERIAL_NUMBER },
+ {LVID_REMOVE_PRODUCT_NAME, IDS_PRODUCT_NAME, LVCX_REMOVE_PRODUCT_NAME },
+ {LVID_REMOVE_LICENSE_MODE, IDS_LICENSE_MODE, LVCX_REMOVE_LICENSE_MODE },
+ {LVID_REMOVE_NUM_LICENSES, IDS_QUANTITY, LVCX_REMOVE_NUM_LICENSES },
+ {LVID_REMOVE_SOURCE, IDS_SOURCE, LVCX_REMOVE_SOURCE }},
+};
+
+
+CCertRemoveSelectDlg::CCertRemoveSelectDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CCertRemoveSelectDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CCertRemoveSelectDlg)
+ m_nLicenses = 0;
+ //}}AFX_DATA_INIT
+
+ m_hLls = NULL;
+ m_bLicensesRefreshed = FALSE;
+ m_dwRemoveFlags = 0;
+}
+
+
+CCertRemoveSelectDlg::~CCertRemoveSelectDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+}
+
+
+void CCertRemoveSelectDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCertRemoveSelectDlg)
+ DDX_Control(pDX, IDC_SPIN_LICENSES, m_spinLicenses);
+ DDX_Control(pDX, IDC_CERTIFICATE_LIST, m_listCertificates);
+ DDX_Text(pDX, IDC_NUM_LICENSES, m_nLicenses);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CCertRemoveSelectDlg, CDialog)
+ //{{AFX_MSG_MAP(CCertRemoveSelectDlg)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_NOTIFY(LVN_COLUMNCLICK, IDC_CERTIFICATE_LIST, OnColumnClickCertificateList)
+ ON_NOTIFY(LVN_GETDISPINFO, IDC_CERTIFICATE_LIST, OnGetDispInfoCertificateList)
+ ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LICENSES, OnDeltaPosSpinLicenses)
+ ON_NOTIFY(NM_DBLCLK, IDC_CERTIFICATE_LIST, OnDblClkCertificateList)
+ ON_NOTIFY(NM_RETURN, IDC_CERTIFICATE_LIST, OnReturnCertificateList)
+ ON_WM_DESTROY()
+ ON_NOTIFY(NM_CLICK, IDC_CERTIFICATE_LIST, OnClickCertificateList)
+ ON_NOTIFY(LVN_KEYDOWN, IDC_CERTIFICATE_LIST, OnKeyDownCertificateList)
+ ON_BN_CLICKED(IDC_REFRESH, OnRefresh)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+BOOL CCertRemoveSelectDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ LoadImages();
+
+ m_listCertificates.SetImageList( &m_smallImages, LVSIL_SMALL );
+
+ ::LvInitColumns( &m_listCertificates, &g_removeColumnInfo );
+
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+
+void CCertRemoveSelectDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of OK.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+}
+
+
+void CCertRemoveSelectDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CCertRemoveSelectDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+
+void CCertRemoveSelectDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ResetLicenses();
+
+ ::WinHelp( m_hWnd, theApp.GetHelpFileName(), HELP_QUIT, 0 );
+
+ CDialog::OnDestroy();
+}
+
+
+void CCertRemoveSelectDlg::OnColumnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_COLUMNCLICK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ g_removeColumnInfo.bSortOrder = GetKeyState(VK_CONTROL) < 0;
+ g_removeColumnInfo.nSortedItem = ((NM_LISTVIEW*)pNMHDR)->iSubItem;
+
+ m_listCertificates.SortItems( CompareLicenses, 0 ); // use column info
+
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnGetDispInfoCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_GETDISPINFO of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ LV_ITEM* plvItem = &((LV_DISPINFO*)pNMHDR)->item;
+ ASSERT(plvItem);
+
+ CLicense* pLicense = (CLicense*)plvItem->lParam;
+ VALIDATE_OBJECT(pLicense, CLicense);
+
+ switch (plvItem->iSubItem)
+ {
+ case LVID_REMOVE_SERIAL_NUMBER:
+ plvItem->iImage = BMPI_CERTIFICATE;
+ {
+ CString strSerialNumber;
+
+ strSerialNumber.Format( TEXT("%ld"), (LONG) ( pLicense->m_dwCertificateID ) );
+ lstrcpyn( plvItem->pszText, strSerialNumber, plvItem->cchTextMax );
+ }
+ break;
+
+ case LVID_REMOVE_PRODUCT_NAME:
+ lstrcpyn( plvItem->pszText, pLicense->m_strProduct, plvItem->cchTextMax );
+ break;
+
+ case LVID_REMOVE_LICENSE_MODE:
+ lstrcpyn( plvItem->pszText, pLicense->GetAllowedModesString(), plvItem->cchTextMax );
+ break;
+
+ case LVID_REMOVE_NUM_LICENSES:
+ {
+ CString strLicenses;
+
+ strLicenses.Format( TEXT("%ld"), (LONG) ( pLicense->m_lQuantity ) );
+ lstrcpyn( plvItem->pszText, strLicenses, plvItem->cchTextMax );
+ }
+ break;
+
+ case LVID_REMOVE_SOURCE:
+ lstrcpyn( plvItem->pszText, pLicense->GetSourceDisplayName(), plvItem->cchTextMax );
+ break;
+
+ default:
+ ASSERT( FALSE );
+ break;
+ }
+
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for UDN_DELTAPOS of number of licenses.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( UpdateData(TRUE) ) // get data
+ {
+ m_nLicenses += ((NM_UPDOWN*)pNMHDR)->iDelta;
+
+ int nLow;
+ int nHigh;
+
+ m_spinLicenses.GetRange( nLow, nHigh );
+
+ if (m_nLicenses < nLow)
+ {
+ m_nLicenses = nLow;
+
+ ::MessageBeep(MB_OK);
+ }
+ else if (m_nLicenses > nHigh )
+ {
+ m_nLicenses = nHigh;
+
+ ::MessageBeep(MB_OK);
+ }
+
+ UpdateData(FALSE); // set data
+ }
+
+ *pResult = 1; // handle ourselves...
+}
+
+
+void CCertRemoveSelectDlg::OnDblClkCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_DBLCLK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::OnReturnCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_RETURN of certificate list view.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSelectedCertificate();
+ *pResult = 0;
+}
+
+
+void CCertRemoveSelectDlg::ResetLicenses()
+
+/*++
+
+Routine Description:
+
+ Remove all licenses from internal list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CLicense* pLicense;
+ int iLicense = m_licenseArray.GetSize();
+
+ while (iLicense--)
+ {
+ if (pLicense = (CLicense*)m_licenseArray[iLicense])
+ {
+ ASSERT(pLicense->IsKindOf(RUNTIME_CLASS(CLicense)));
+ delete pLicense;
+ }
+ }
+
+ m_licenseArray.RemoveAll();
+ m_listCertificates.DeleteAllItems();
+
+ m_bLicensesRefreshed = FALSE;
+}
+
+
+BOOL CCertRemoveSelectDlg::RefreshLicenses()
+
+/*++
+
+Routine Description:
+
+ Refresh internal license list with data from license server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ ResetLicenses();
+
+ if ( ConnectServer() )
+ {
+ NTSTATUS NtStatus;
+ DWORD ResumeHandle = 0L;
+
+ int iLicense = 0;
+
+ do
+ {
+ DWORD EntriesRead;
+ DWORD TotalEntries;
+ LPBYTE ReturnBuffer = NULL;
+ DWORD Level = LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ? 1 : 0;
+
+ BeginWaitCursor();
+ NtStatus = ::LlsLicenseEnum( m_hLls,
+ Level,
+ &ReturnBuffer,
+ LLS_PREFERRED_LENGTH,
+ &EntriesRead,
+ &TotalEntries,
+ &ResumeHandle );
+ EndWaitCursor();
+
+ if ( ( STATUS_SUCCESS == NtStatus )
+ || ( STATUS_MORE_ENTRIES == NtStatus ) )
+ {
+ CLicense* pLicense;
+ PLLS_LICENSE_INFO_0 pLicenseInfo0;
+ PLLS_LICENSE_INFO_1 pLicenseInfo1;
+
+ pLicenseInfo0 = (PLLS_LICENSE_INFO_0)ReturnBuffer;
+ pLicenseInfo1 = (PLLS_LICENSE_INFO_1)ReturnBuffer;
+
+ while (EntriesRead--)
+ {
+ if ( ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( Level ? pLicenseInfo1->Product : pLicenseInfo0->Product ) )
+ && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( Level ? pLicenseInfo1->Source : TEXT("None") ) ) )
+ {
+ // we want to list this license
+
+ // have we seen this certificate yet?
+ for ( int i=0; i < m_licenseArray.GetSize(); i++ )
+ {
+ pLicense = (CLicense*) m_licenseArray[ i ];
+
+ VALIDATE_OBJECT( pLicense, CLicense );
+
+ if ( ( ( 1 == Level )
+ && ( pLicense->m_dwCertificateID == pLicenseInfo1->CertificateID )
+ && ( pLicense->m_dwAllowedModes == pLicenseInfo1->AllowedModes )
+ && ( pLicense->m_dwMaxQuantity == pLicenseInfo1->MaxQuantity )
+ && ( !pLicense->m_strSource.CompareNoCase( pLicenseInfo1->Source ) )
+ && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo1->Product ) )
+ && ( !memcmp( pLicense->m_adwSecrets,
+ pLicenseInfo1->Secrets,
+ sizeof( pLicense->m_adwSecrets ) ) ) )
+ || ( ( 0 == Level )
+ && ( !pLicense->m_strProduct.CompareNoCase( pLicenseInfo0->Product ) ) ) )
+ {
+ // we've seen this certificate before; update the tally
+ pLicense->m_lQuantity += ( Level ? pLicenseInfo1->Quantity : pLicenseInfo0->Quantity );
+ break;
+ }
+ }
+
+ if ( i >= m_licenseArray.GetSize() )
+ {
+ // we haven't seen this certificate yet; create a new license for it
+ if ( 1 == Level )
+ {
+ pLicense = new CLicense( pLicenseInfo1->Product,
+ pLicenseInfo1->Vendor,
+ pLicenseInfo1->Admin,
+ pLicenseInfo1->Date,
+ pLicenseInfo1->Quantity,
+ pLicenseInfo1->Comment,
+ pLicenseInfo1->AllowedModes,
+ pLicenseInfo1->CertificateID,
+ pLicenseInfo1->Source,
+ pLicenseInfo1->ExpirationDate,
+ pLicenseInfo1->MaxQuantity,
+ pLicenseInfo1->Secrets );
+
+ ::LlsFreeMemory( pLicenseInfo1->Product );
+ ::LlsFreeMemory( pLicenseInfo1->Admin );
+ ::LlsFreeMemory( pLicenseInfo1->Comment );
+ ::LlsFreeMemory( pLicenseInfo1->Source );
+ }
+ else
+ {
+ ASSERT( 0 == Level );
+
+ pLicense = new CLicense( pLicenseInfo0->Product,
+ TEXT( "Microsoft" ),
+ pLicenseInfo0->Admin,
+ pLicenseInfo0->Date,
+ pLicenseInfo0->Quantity,
+ pLicenseInfo0->Comment );
+
+ ::LlsFreeMemory( pLicenseInfo0->Product );
+ ::LlsFreeMemory( pLicenseInfo0->Admin );
+ ::LlsFreeMemory( pLicenseInfo0->Comment );
+ }
+
+ if ( NULL == pLicense )
+ {
+ NtStatus = ERROR_OUTOFMEMORY;
+ break;
+ }
+
+ m_licenseArray.Add( pLicense );
+ }
+ }
+
+ pLicenseInfo1++;
+ pLicenseInfo0++;
+ }
+
+ ::LlsFreeMemory(ReturnBuffer);
+ }
+
+ } while ( STATUS_MORE_ENTRIES == NtStatus );
+
+ theApp.SetLastLlsError( NtStatus ); // called api
+
+ if ( STATUS_SUCCESS == NtStatus )
+ {
+ // add per server entries
+ LPTSTR pszServerName = m_strServerName.GetBuffer(0);
+
+ if ( NULL != pszServerName )
+ {
+ BeginWaitCursor();
+
+ HKEY hKeyLocalMachine;
+
+ NtStatus = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != NtStatus )
+ {
+ theApp.SetLastError( NtStatus );
+ }
+ else
+ {
+ HKEY hKeyLicenseInfo;
+
+ NtStatus = RegOpenKeyEx( hKeyLocalMachine, TEXT( "SYSTEM\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_SET_VALUE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS != NtStatus )
+ {
+ theApp.SetLastError( NtStatus );
+ }
+ else
+ {
+ NTSTATUS ntEnum;
+ BOOL bFoundKey = FALSE;
+ DWORD iSubKey = 0;
+
+ // if the service is 3.51-style per server, add it to the list
+ do
+ {
+ TCHAR szKeyName[ 128 ];
+ DWORD cchKeyName = sizeof( szKeyName ) / sizeof( *szKeyName );
+
+ ntEnum = RegEnumKeyEx( hKeyLicenseInfo, iSubKey++, szKeyName, &cchKeyName, NULL, NULL, NULL, NULL );
+
+ if ( ERROR_SUCCESS == ntEnum )
+ {
+ HKEY hKeyProduct;
+
+ NtStatus = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hKeyProduct );
+
+ if ( ERROR_SUCCESS == NtStatus )
+ {
+ DWORD dwType;
+ TCHAR szDisplayName[ 128 ];
+ DWORD cbDisplayName = sizeof( szDisplayName );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "DisplayName" ), NULL, &dwType, (LPBYTE) szDisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == NtStatus )
+ {
+ // is this product secure?
+ BOOL bIsSecure = FALSE;
+
+ if ( LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ NtStatus = ::LlsProductSecurityGet( m_hLls, szDisplayName, &bIsSecure );
+ theApp.SetLastLlsError( NtStatus );
+
+ if ( STATUS_SUCCESS != NtStatus )
+ {
+ bIsSecure = FALSE;
+ }
+ }
+
+ if ( !bIsSecure )
+ {
+#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
+ // not secure; is it in per server mode?
+ DWORD dwMode;
+ DWORD cbMode = sizeof( dwMode );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "Mode" ), NULL, &dwType, (LPBYTE) &dwMode, &cbMode );
+
+ if ( ( ERROR_SUCCESS == NtStatus ) && dwMode )
+ {
+ // per server mode; add to list
+#endif
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+
+ NtStatus = RegQueryValueEx( hKeyProduct, TEXT( "ConcurrentLimit" ), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ( ERROR_SUCCESS == NtStatus )
+ && ( 0 < dwConcurrentLimit )
+ && ( m_strProductName.IsEmpty() || !m_strProductName.CompareNoCase( szDisplayName ) )
+ && ( m_strSourceToUse.IsEmpty() || !m_strSourceToUse.CompareNoCase( TEXT("None") ) ) )
+ {
+ CLicense * pLicense = new CLicense( szDisplayName,
+ TEXT(""),
+ TEXT(""),
+ 0,
+ dwConcurrentLimit,
+ TEXT(""),
+ LLS_LICENSE_MODE_ALLOW_PER_SERVER );
+
+ if ( NULL != pLicense )
+ {
+ m_licenseArray.Add( pLicense );
+ }
+ }
+ }
+#ifdef REMOVE_CONCURRENT_ONLY_IF_PER_SERVER_MODE
+ }
+#endif
+ }
+
+ RegCloseKey( hKeyProduct );
+ }
+ }
+ } while ( ERROR_SUCCESS == ntEnum );
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ m_strServerName.ReleaseBuffer();
+ }
+
+ EndWaitCursor();
+
+ m_bLicensesRefreshed = TRUE;
+
+ // remove any entries from the list that aren't removable
+ for ( int i=0; i < m_licenseArray.GetSize(); )
+ {
+ CLicense* pLicense = (CLicense*) m_licenseArray[ i ];
+
+ VALIDATE_OBJECT( pLicense, CLicense );
+
+ if ( pLicense->m_lQuantity <= 0 )
+ {
+ delete pLicense;
+ m_licenseArray.RemoveAt( i );
+ }
+ else
+ {
+ i++;
+ }
+ }
+ }
+ else
+ {
+ theApp.DisplayLastError();
+ ResetLicenses();
+ }
+ }
+
+ return m_bLicensesRefreshed;
+}
+
+
+BOOL CCertRemoveSelectDlg::RefreshCertificateList()
+
+/*++
+
+Routine Description:
+
+ Refresh certificate list view from internal license list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ BeginWaitCursor();
+
+ BOOL ok = ::LvRefreshObArray( &m_listCertificates, &g_removeColumnInfo, &m_licenseArray );
+
+ EndWaitCursor();
+
+ return ok;
+}
+
+
+int CALLBACK CompareLicenses(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+
+/*++
+
+Routine Description:
+
+ Notification handler for LVM_SORTITEMS.
+
+Arguments:
+
+ lParam1 - object to sort.
+ lParam2 - object to sort.
+ lParamSort - sort criteria.
+
+Return Values:
+
+ Same as lstrcmp.
+
+--*/
+
+{
+ CLicense * pLic1 = (CLicense *) lParam1;
+ CLicense * pLic2 = (CLicense *) lParam2;
+
+ VALIDATE_OBJECT( pLic1, CLicense );
+ VALIDATE_OBJECT( pLic2, CLicense );
+
+ int iResult;
+
+ switch (g_removeColumnInfo.nSortedItem)
+ {
+ case LVID_REMOVE_SERIAL_NUMBER:
+ iResult = pLic1->m_dwCertificateID - pLic2->m_dwCertificateID;
+ break;
+
+ case LVID_REMOVE_PRODUCT_NAME:
+ iResult = pLic1->m_strProduct.CompareNoCase( pLic2->m_strProduct );
+ break;
+
+ case LVID_REMOVE_NUM_LICENSES:
+ iResult = pLic1->m_lQuantity - pLic2->m_lQuantity;
+ break;
+
+ case LVID_REMOVE_SOURCE:
+ iResult = pLic1->GetSourceDisplayName().CompareNoCase( pLic2->GetSourceDisplayName() );
+ break;
+
+ default:
+ iResult = 0;
+ break;
+ }
+
+ return g_removeColumnInfo.bSortOrder ? -iResult : iResult;
+}
+
+
+void CCertRemoveSelectDlg::UpdateSpinControlRange()
+
+/*++
+
+Routine Description:
+
+ Update range of spin control for number of licenses.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CLicense * pLicense;
+
+ UpdateData( TRUE );
+
+ if ( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) )
+ {
+ m_spinLicenses.SetRange( 1, pLicense->m_lQuantity );
+ m_nLicenses = pLicense->m_lQuantity;
+ GetDlgItem( IDOK )->EnableWindow( TRUE );
+ }
+ else
+ {
+ m_spinLicenses.SetRange( 0, 0 );
+ m_nLicenses = 0;
+ GetDlgItem( IDOK )->EnableWindow( FALSE );
+ }
+
+ UpdateData( FALSE );
+}
+
+
+DWORD CCertRemoveSelectDlg::RemoveSelectedCertificate()
+
+/*++
+
+Routine Description:
+
+ Remove the given number of licenses from the selected certificate.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ ERROR_SUCCESS
+ NT status code
+ Win error
+
+--*/
+
+{
+ BOOL bDisplayError = TRUE;
+ NTSTATUS nt = STATUS_SUCCESS;
+ CLicense * pLicense;
+
+ UpdateData( TRUE );
+
+ if ( !( pLicense = (CLicense*)::LvGetSelObj( &m_listCertificates ) ) )
+ {
+ // no certificate selected
+ bDisplayError = FALSE;
+ }
+ else if ( ( m_nLicenses < 1 ) || ( m_nLicenses > pLicense->m_lQuantity ) )
+ {
+ // invalid number of licenses to remove
+ AfxMessageBox( IDS_REMOVE_INVALID_NUM_LICENSES, MB_ICONEXCLAMATION | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ CString strLicenses;
+ CString strConfirm;
+
+ strLicenses.Format( TEXT("%d"), m_nLicenses );
+ AfxFormatString2( strConfirm, IDS_REMOVE_CERTIFICATE_CONFIRM, strLicenses, pLicense->m_strProduct );
+
+ int nResponse = AfxMessageBox( strConfirm, MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2 );
+
+ if ( IDYES != nResponse )
+ {
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ // delete certificate
+ LPSTR pszAscServerName = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strServerName.GetLength() );
+ LPSTR pszAscProductName = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + pLicense->m_strProduct.GetLength() );
+ LPSTR pszAscVendor = (LPSTR) LocalAlloc( LMEM_FIXED, 1 + m_strVendor.GetLength() );
+
+ if ( ( NULL == pszAscServerName ) || ( NULL == pszAscProductName ) || ( NULL == pszAscVendor ) )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ wsprintfA( pszAscServerName, "%ls", (LPCWSTR) m_strServerName );
+ wsprintfA( pszAscProductName, "%ls", (LPCWSTR) pLicense->m_strProduct );
+ wsprintfA( pszAscVendor, "%ls", (LPCWSTR) m_strVendor );
+
+ LLS_LICENSE_INFO_1 lic;
+
+ nt = pLicense->CreateLicenseInfo( &lic );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // only remove as many licenses as requested
+ lic.Quantity = m_nLicenses;
+
+ if ( !pLicense->m_strSource.CompareNoCase( TEXT( "None" ) ) )
+ {
+ nt = NoCertificateRemove( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
+ bDisplayError = FALSE;
+ }
+ else
+ {
+ // get certificate source DLL path
+ CString strKeyName = TEXT( "Software\\LSAPI\\Microsoft\\CertificateSources\\" )
+ + pLicense->m_strSource;
+ HKEY hKeySource;
+
+ nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, strKeyName, 0, KEY_READ, &hKeySource );
+
+ if ( ( ERROR_PATH_NOT_FOUND == nt ) || ( ERROR_FILE_NOT_FOUND == nt ) )
+ {
+ AfxMessageBox( IDS_CERT_SOURCE_NOT_AVAILABLE, MB_ICONSTOP | MB_OK, 0 );
+ nt = ERROR_CANCELLED;
+ bDisplayError = FALSE;
+ }
+ else if ( ERROR_SUCCESS == nt )
+ {
+ TCHAR szImagePath[ 1 + _MAX_PATH ];
+ DWORD cbImagePath = sizeof( szImagePath );
+ DWORD dwType;
+
+ nt = RegQueryValueEx( hKeySource, TEXT( "ImagePath" ), NULL, &dwType, (LPBYTE) szImagePath, &cbImagePath );
+
+ if ( ERROR_SUCCESS == nt )
+ {
+ TCHAR szExpandedImagePath[ 1 + _MAX_PATH ];
+
+ BOOL ok = ExpandEnvironmentStrings( szImagePath, szExpandedImagePath, sizeof( szExpandedImagePath ) / sizeof( *szExpandedImagePath ) );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // load certificate source DLL
+ HINSTANCE hDll = ::LoadLibrary( szExpandedImagePath );
+
+ if ( NULL == hDll )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // get certificate remove function
+ CHAR szExportName[ 256 ];
+ PCCF_REMOVE_API pRemoveFn;
+
+ wsprintfA( szExportName, "%lsCertificateRemove", (LPCWSTR) pLicense->m_strSource );
+ pRemoveFn = (PCCF_REMOVE_API) GetProcAddress( hDll, szExportName );
+
+ if ( NULL == pRemoveFn )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ // remove certificate
+ nt = (*pRemoveFn)( m_hWnd, pszAscServerName, m_dwRemoveFlags, 1, &lic );
+ bDisplayError = FALSE;
+ }
+
+ ::FreeLibrary( hDll );
+ }
+ }
+ }
+
+ RegCloseKey( hKeySource );
+ }
+ }
+
+ pLicense->DestroyLicenseInfo( &lic );
+ }
+ }
+
+ if ( NULL != pszAscServerName ) LocalFree( pszAscServerName );
+ if ( NULL != pszAscProductName ) LocalFree( pszAscProductName );
+ if ( NULL != pszAscVendor ) LocalFree( pszAscVendor );
+
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+ }
+ }
+
+ if ( bDisplayError && ( ERROR_SUCCESS != nt ) )
+ {
+ theApp.SetLastError( nt );
+ theApp.DisplayLastError();
+ }
+
+ return nt;
+}
+
+
+BOOL CCertRemoveSelectDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszServerName;
+
+ if ( m_strServerName.IsEmpty() )
+ {
+ pszServerName = NULL;
+ }
+ else
+ {
+ pszServerName = m_strServerName.GetBuffer( 0 );
+ }
+
+ ConnectTo( pszServerName, &m_hLls );
+
+ if ( NULL != pszServerName )
+ {
+ m_strServerName.ReleaseBuffer();
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+
+ if ( ( NULL != m_hWnd ) && IsWindow( m_hWnd ) )
+ {
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+NTSTATUS CCertRemoveSelectDlg::ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server.
+
+Arguments:
+
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ nt = ::LlsConnect( pszServerName, phLls );
+ theApp.SetLastLlsError( nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+void CCertRemoveSelectDlg::OnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for NM_CLICK of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateSpinControlRange();
+ *pResult = 1; // not handled...
+}
+
+void CCertRemoveSelectDlg::OnKeyDownCertificateList(NMHDR* pNMHDR, LRESULT* pResult)
+
+/*++
+
+Routine Description:
+
+ Handler for LVN_KEYDOWN of certificate list view.
+
+Arguments:
+
+ pNMHDR (NMHDR*)
+ pResult (LRESULT*)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ UpdateSpinControlRange();
+ *pResult = 1; // not handled...
+}
+
+
+BOOL CCertRemoveSelectDlg::LoadImages()
+
+/*++
+
+Routine Description:
+
+ Load icons for the list view.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ BOOL bImagesLoaded = m_smallImages.Create( IDB_SMALL_ICONS, BMPI_SMALL_SIZE, 0, BMPI_RGB_BKGND );
+ ASSERT( bImagesLoaded );
+
+ return bImagesLoaded;
+}
+
+
+void CCertRemoveSelectDlg::OnRefresh()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICK of refresh button.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RefreshLicenses();
+ RefreshCertificateList();
+ UpdateSpinControlRange();
+}
+
+
+DWORD CCertRemoveSelectDlg::CertificateRemove( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to remove one or more license
+ certificates from the system.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server on which licenses are to be removed. A NULL value
+ indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be removed. A NULL value indicates
+ that the user should be allowed to remove licenses from any product.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ Certificate removal options. As of this writing, no flags are
+ supported.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source by which licenses are to be
+ removed, e.g., "Paper". A NULL value indicates that the user should
+ be allowed to remove licenses that were installed with any source.
+
+Return Value:
+
+ ERROR_SUCCESS
+ Win error
+
+--*/
+
+{
+ m_strServerName = pszServerName ? pszServerName : "";
+ m_strProductName = pszProductName ? pszProductName : "";
+ m_strVendor = pszVendor ? pszVendor : "";
+ m_dwRemoveFlags = dwFlags;
+ m_strSourceToUse = pszSourceToUse ? pszSourceToUse : "";
+
+ DoModal();
+
+ return ERROR_SUCCESS;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/remdlg.h b/private/net/svcdlls/lls/ccfapi32/remdlg.h
new file mode 100644
index 000000000..58295c979
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/remdlg.h
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ paper.cpp
+
+Abstract:
+
+ Remove licenses dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+class CCertRemoveSelectDlg : public CDialog
+{
+public:
+ CCertRemoveSelectDlg(CWnd* pParent = NULL); // standard constructor
+ ~CCertRemoveSelectDlg();
+
+ void UpdateSpinControlRange();
+ BOOL LoadImages();
+
+// Dialog Data
+ //{{AFX_DATA(CCertRemoveSelectDlg)
+ enum { IDD = IDD_CERT_REMOVE_SELECT };
+ CSpinButtonCtrl m_spinLicenses;
+ CListCtrl m_listCertificates;
+ int m_nLicenses;
+ //}}AFX_DATA
+
+ CObArray m_licenseArray;
+ LLS_HANDLE m_hLls;
+ BOOL m_bLicensesRefreshed;
+ CString m_strSourceToUse;
+ CString m_strProductName;
+ CString m_strServerName;
+ CString m_strVendor;
+ CImageList m_smallImages;
+ DWORD m_dwRemoveFlags;
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCertRemoveSelectDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+public:
+ DWORD CertificateRemove( LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+ void ResetLicenses();
+ BOOL RefreshLicenses();
+ BOOL RefreshCertificateList();
+ DWORD RemoveSelectedCertificate();
+
+ BOOL ConnectServer();
+ NTSTATUS ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls );
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CCertRemoveSelectDlg)
+ afx_msg void OnHelp();
+ afx_msg void OnColumnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnGetDispInfoCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnDeltaPosSpinLicenses(NMHDR* pNMHDR, LRESULT* pResult);
+ virtual void OnOK();
+ virtual BOOL OnInitDialog();
+ afx_msg void OnDblClkCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnReturnCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnDestroy();
+ afx_msg void OnClickCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnKeyDownCertificateList(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnRefresh();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+#define LVID_REMOVE_SERIAL_NUMBER 0
+#define LVID_REMOVE_PRODUCT_NAME 1
+#define LVID_REMOVE_LICENSE_MODE 2
+#define LVID_REMOVE_NUM_LICENSES 3
+#define LVID_REMOVE_SOURCE 4
+
+#define LVID_REMOVE_TOTAL_COLUMNS 5
+
+#define LVCX_REMOVE_SERIAL_NUMBER 20
+#define LVCX_REMOVE_PRODUCT_NAME 35
+#define LVCX_REMOVE_LICENSE_MODE 16
+#define LVCX_REMOVE_NUM_LICENSES 10
+#define LVCX_REMOVE_SOURCE -1
+
+int CALLBACK CompareLicenses(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
diff --git a/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2 b/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2
new file mode 100644
index 000000000..15a197155
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/ccfapi32.rc2
@@ -0,0 +1,24 @@
+//
+// CCFAPI32.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+#include <winver.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "License Certificate API"
+#define VER_INTERNALNAME_STR "CCFAPI32.DLL"
+#define VER_ORIGINALFILENAME_STR "CCFAPI32.DLL"
+
+#include "common.ver"
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/private/net/svcdlls/lls/ccfapi32/res/license.ico b/private/net/svcdlls/lls/ccfapi32/res/license.ico
new file mode 100644
index 000000000..2570c2c25
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/license.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp b/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp
new file mode 100644
index 000000000..08fd32c5c
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/smicons.bmp
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/res/warning.ico b/private/net/svcdlls/lls/ccfapi32/res/warning.ico
new file mode 100644
index 000000000..e8e1f6fba
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/res/warning.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/ccfapi32/resource.h b/private/net/svcdlls/lls/ccfapi32/resource.h
new file mode 100644
index 000000000..9ce11dde3
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/resource.h
@@ -0,0 +1,105 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by CCFApi.rc
+//
+
+// special case error messages
+#define IDS_ERROR_DROPPED_LINK 2000
+#define IDS_ERROR_NO_RPC_SERVER 2001
+#define IDS_ERROR_UNSUCCESSFUL 2002
+#define IDS_ERROR_DOWNLEVEL_SERVER 2003
+#define IDS_ERROR_ACCESS_DENIED 2004
+#define IDS_ERROR_CERTIFICATE_EXPIRED 2005
+
+// dialog messages
+#define IDS_BAD_ACTIVATION_CODE 2100
+#define IDS_NO_PRODUCT_SEND_TO_ENTERPRISE 2101
+#define IDS_PER_SEAT_SEND_TO_ENTERPRISE 2102
+#define IDS_PER_SERVER_APP_NOT_INSTALLED 2103
+#define IDS_PER_SEAT_CHOSEN_SEND_TO_ENTERPRISE 2104
+#define IDS_NOT_ENOUGH_LICENSES_ON_CERTIFICATE 2105
+#define IDS_INVALID_NUM_LICENSES 2106
+#define IDS_REMOVE_CERTIFICATE_CONFIRM 2107
+#define IDS_CERT_SOURCE_NOT_AVAILABLE 2108
+#define IDS_ENTERPRISE_SERVER_BACKLEVEL_CANT_ADD_CERT 2109
+#define IDS_LOCAL_LICENSES_ALREADY_INSTALLED 2110
+#define IDS_NET_CERTIFICATE_TARGET_ENTRY 2111
+#define IDS_NET_LICENSES_ALREADY_INSTALLED_ON 2112
+#define IDS_NET_LICENSES_ALREADY_INSTALLED 2113
+#define IDS_PER_SEAT_LICENSING_1 2114
+#define IDS_PER_SERVER_LICENSING_1 2115
+#define IDS_REMOVE_INVALID_NUM_LICENSES 2116
+#define IDS_LICENSE_MODE_NOT_ALLOWED 2117
+#define IDS_NO_PRODUCT_CERTIFICATE_SOURCES 2118
+
+// certificate removal comments
+#define IDS_NO_REMOVE_COMMENT 2200
+#define IDS_PAPER_REMOVE_COMMENT 2201
+
+// dialog data values
+#define IDS_NO_CERTIFICATE_SOURCE_NAME 2300
+#define IDS_SOURCE_NONE 2301
+#define IDS_LICENSE_MODE_EITHER 2302
+#define IDS_LICENSE_MODE_PER_SEAT 2303
+#define IDS_LICENSE_MODE_PER_SERVER 2304
+#define IDS_LICENSE_MODE_UNKNOWN 2305
+
+// dialog data headers
+#define IDS_SERIAL_NUMBER 2400
+#define IDS_PRODUCT_NAME 2401
+#define IDS_QUANTITY 2402
+#define IDS_SOURCE 2403
+#define IDS_LICENSE_MODE 2404
+
+// icons
+#define IDI_LICENSE 2500
+#define IDI_PHONE 2501
+#define IDI_MY_WARNING 2502
+
+// dialogs
+#define IDD_CERT_SOURCE_SELECT 2600
+#define IDD_CERT_SOURCE_PAPER 2601
+#define IDD_CERT_REMOVE_SELECT 2602
+#define IDD_NEW_LICENSE 2603
+#define IDD_PER_SERVER_LICENSING 2604
+#define IDD_PER_SEAT_LICENSING 2605
+
+// bitmaps
+#define IDB_SMALL_ICONS 2700
+
+// dialog controls
+#define IDC_CERT_SOURCE 2800
+#define IDC_PRODUCT_NAME 2801
+#define IDC_PER_SEAT 2802
+#define IDC_VENDOR 2803
+#define IDC_PER_SERVER 2804
+#define IDC_SERIAL_NUMBER 2805
+#define IDC_KEY_CODE 2806
+#define IDC_ALL_LICENSES 2807
+#define IDC_ACTIVATION_CODE 2808
+#define IDC_COMMENT 2809
+#define IDC_MY_HELP 2810
+#define IDC_SOME_LICENSES 2811
+#define IDC_SPIN_LICENSES 2812
+#define IDC_NUM_LICENSES 2813
+#define IDC_CERTIFICATE_LIST 2814
+#define IDC_NEW_LICENSE_PRODUCT 2815
+#define IDC_NEW_LICENSE_COMMENT 2816
+#define IDC_NEW_LICENSE_QUANTITY 2817
+#define IDC_NEW_LICENSE_SPIN 2818
+#define IDC_PER_SERVER_STATIC_CLIENTS 2819
+#define IDC_PER_SERVER_AGREE 2820
+#define IDC_PER_SEAT_AGREE 2821
+#define IDC_PER_SEAT_STATIC_CLIENTS 2822
+#define IDC_REFRESH 2823
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 133
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1017
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/private/net/svcdlls/lls/ccfapi32/source.cpp b/private/net/svcdlls/lls/ccfapi32/source.cpp
new file mode 100644
index 000000000..9fa4a12f5
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/source.cpp
@@ -0,0 +1,706 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ source.cpp
+
+Abstract:
+
+ Select certificate source dialog implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "source.h"
+#include "paper.h"
+#include "nlicdlg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char BASED_CODE THIS_FILE[] = __FILE__;
+#endif
+
+
+// 3.51-style
+static CString l_strOldEntryName;
+static const DWORD l_dwOldEntryIndex = (DWORD) (-1L);
+
+
+CCertSourceSelectDlg::CCertSourceSelectDlg(CWnd* pParent /*=NULL*/)
+ : CDialog(CCertSourceSelectDlg::IDD, pParent)
+
+/*++
+
+Routine Description:
+
+ Constructor for dialog.
+
+Arguments:
+
+ pParent - owner window.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ //{{AFX_DATA_INIT(CCertSourceSelectDlg)
+ m_strSource = _T("");
+ //}}AFX_DATA_INIT
+
+ m_dwEnterFlags = 0;
+ m_pszProductName = NULL;
+ m_pszServerName = NULL;
+ m_pszVendor = NULL;
+
+ l_strOldEntryName.LoadString( IDS_NO_CERTIFICATE_SOURCE_NAME );
+
+ m_hLls = NULL;
+}
+
+
+CCertSourceSelectDlg::~CCertSourceSelectDlg()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != m_hLls )
+ {
+ LlsClose( m_hLls );
+ }
+}
+
+
+void CCertSourceSelectDlg::DoDataExchange(CDataExchange* pDX)
+
+/*++
+
+Routine Description:
+
+ Called by framework to exchange dialog data.
+
+Arguments:
+
+ pDX - data exchange object.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CCertSourceSelectDlg)
+ DDX_Control(pDX, IDC_CERT_SOURCE, m_cboxSource);
+ DDX_CBString(pDX, IDC_CERT_SOURCE, m_strSource);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CCertSourceSelectDlg, CDialog)
+ //{{AFX_MSG_MAP(CCertSourceSelectDlg)
+ ON_BN_CLICKED(IDC_MY_HELP, OnHelp)
+ ON_WM_DESTROY()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+BOOL CCertSourceSelectDlg::OnInitDialog()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_INITDIALOG.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ Returns false if focus set manually.
+
+--*/
+
+{
+ CDialog::OnInitDialog();
+
+ GetSourceList();
+
+ m_cboxSource.SetCurSel( 0 );
+
+ return TRUE;
+}
+
+
+void CCertSourceSelectDlg::OnOK()
+
+/*++
+
+Routine Description:
+
+ Handler for BN_CLICKED of OK.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ if ( NULL != GetParent() )
+ GetParent()->EnableWindow();
+
+ ShowWindow( FALSE );
+
+ if ( ERROR_SUCCESS == CallCertificateSource( m_cboxSource.GetItemData( m_cboxSource.GetCurSel() ) ) )
+ CDialog::OnOK();
+ else
+ ShowWindow( TRUE );
+}
+
+
+void CCertSourceSelectDlg::OnHelp()
+
+/*++
+
+Routine Description:
+
+ Handler for help button click.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( IDD, HELP_CONTEXT );
+}
+
+
+void CCertSourceSelectDlg::WinHelp(DWORD dwData, UINT nCmd)
+
+/*++
+
+Routine Description:
+
+ Call WinHelp for this dialog.
+
+Arguments:
+
+ dwData (DWORD)
+ nCmd (UINT)
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = ::WinHelp( m_hWnd, theApp.GetHelpFileName(), nCmd, dwData );
+ ASSERT( ok );
+}
+
+void CCertSourceSelectDlg::OnDestroy()
+
+/*++
+
+Routine Description:
+
+ Handler for WM_DESTROY.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ WinHelp( 0, HELP_QUIT );
+
+ CDialog::OnDestroy();
+}
+
+
+void CCertSourceSelectDlg::GetSourceList()
+
+/*++
+
+Routine Description:
+
+ Insert list of valid certificate sources into list box.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ BOOL ok = TRUE;
+ int nCboxIndex;
+
+ if ( NULL == m_pszProductName )
+ {
+ // otherwise we know that the product is secure, otherwise it would have
+ // been handed to the unsecure product entry dialog already, and we
+ // wouldn't offer to let the user use the unsecure entry dialog
+
+ // add standard non-secure certificate source to possible choices
+ nCboxIndex = m_cboxSource.AddString( l_strOldEntryName );
+
+ ok = ( 0 <= nCboxIndex )
+ && ( CB_ERR != m_cboxSource.SetItemData( nCboxIndex, l_dwOldEntryIndex ) );
+ }
+
+ if ( ok
+ && ConnectServer()
+ && LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // secure certificates supported on the target server (post-3.51 license server)
+
+ // add secure certificate sources to source list
+ for ( int nSourceIndex=0; ok && ( nSourceIndex < m_cslSourceList.GetNumSources() ); nSourceIndex++ )
+ {
+ nCboxIndex = m_cboxSource.AddString( m_cslSourceList.GetSourceDisplayName( nSourceIndex ) );
+
+ if ( nCboxIndex < 0 )
+ {
+ // couldn't add string to combo box
+ ok = FALSE;
+ }
+ else
+ {
+ // string added; associate index of source with it
+ ok = ( CB_ERR != m_cboxSource.SetItemData( nCboxIndex, nSourceIndex ) );
+ }
+ }
+ }
+
+ if ( !ok )
+ {
+ theApp.SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+ theApp.DisplayLastError();
+ EndDialog( IDABORT );
+ }
+ else if ( m_cboxSource.GetCount() == 0 )
+ {
+ AfxMessageBox( IDS_NO_PRODUCT_CERTIFICATE_SOURCES, MB_OK | MB_ICONSTOP, 0 );
+ EndDialog( IDABORT );
+ }
+}
+
+
+DWORD CCertSourceSelectDlg::CallCertificateSource( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Call the certificate source with the specified index into the source list.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ ERROR_SUCCESS
+ ERROR_SERVICE_NOT_FOUND
+ Win error
+
+--*/
+
+{
+ DWORD dwError = ERROR_SERVICE_NOT_FOUND;
+
+ if ( l_dwOldEntryIndex == nIndex )
+ {
+ dwError = NoCertificateEnter( m_hWnd, m_pszServerName, m_pszProductName, m_pszVendor, m_dwEnterFlags );
+ }
+ else
+ {
+ HMODULE hDll;
+
+ hDll = ::LoadLibrary( m_cslSourceList.GetSourceImagePath( nIndex ) );
+
+ if ( NULL == hDll )
+ {
+ dwError = GetLastError();
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ CHAR szExportName[ 256 ];
+ PCCF_ENTER_API pfn;
+
+ wsprintfA( szExportName, "%lsCertificateEnter", m_cslSourceList.GetSourceName( nIndex ) );
+
+ pfn = (PCCF_ENTER_API) GetProcAddress( hDll, szExportName );
+
+ if ( NULL == pfn )
+ {
+ dwError = GetLastError();
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ dwError = (*pfn)( m_hWnd, m_pszServerName, m_pszProductName, m_pszVendor, m_dwEnterFlags );
+ }
+
+ ::FreeLibrary( hDll );
+ }
+ }
+
+ return dwError;
+}
+
+
+void CCertSourceSelectDlg::AbortDialogIfNecessary()
+
+/*++
+
+Routine Description:
+
+ Display error message and abort dialog if connection lost.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ theApp.DisplayLastError();
+
+ if ( theApp.IsConnectionDropped() )
+ {
+ EndDialog( IDABORT );
+ }
+}
+
+
+BOOL CCertSourceSelectDlg::ConnectServer()
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the target server.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( NULL == m_hLls )
+ {
+ LPTSTR pszUniServerName = NULL;
+
+ if ( NULL == m_pszServerName )
+ {
+ pszUniServerName = NULL;
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ pszUniServerName = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + strlen( m_pszServerName ) ) );
+
+ if ( NULL == pszUniServerName )
+ {
+ nt = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( (DWORD) nt );
+ }
+ else
+ {
+ wsprintf( pszUniServerName, TEXT( "%hs" ), m_pszServerName );
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = ConnectTo( pszUniServerName, &m_hLls );
+ }
+
+ if ( NULL != pszUniServerName )
+ {
+ LocalFree( pszUniServerName );
+ }
+ }
+
+ if ( NULL == m_hLls )
+ {
+ theApp.DisplayLastError();
+
+ if ( ( NULL != m_hWnd ) && IsWindow( m_hWnd ) )
+ {
+ EndDialog( IDABORT );
+ }
+ }
+
+ return ( NULL != m_hLls );
+}
+
+
+NTSTATUS CCertSourceSelectDlg::ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls )
+
+/*++
+
+Routine Description:
+
+ Establish a connection to the license service on the given server.
+
+Arguments:
+
+ pszServerName (CString)
+ The target server. An empty value indicates the local server.
+ phLls (PLLS_HANDLE)
+ On return, holds the handle to the standard LLS RPC.
+
+Return Values:
+
+ STATUS_SUCCESS or NT status code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ nt = ::LlsConnect( pszServerName, phLls );
+ theApp.SetLastLlsError( nt );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ *phLls = NULL;
+ }
+
+ return nt;
+}
+
+
+DWORD CCertSourceSelectDlg::CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse )
+
+/*++
+
+Routine Description:
+
+ Display a dialog allowing the user to enter a license certificate
+ into the system.
+
+Arguments:
+
+ pszServerName (LPCSTR)
+ Name of the server for which licenses are to be installed. Note that
+ this may not be the same as the server on which licenses are actually
+ installed, as, for example, per seat licenses are always installed on
+ the enterprise server. A NULL value indicates the local server.
+ pszProductName (LPCSTR)
+ Product for which licenses are to be installed. A NULL value indicates
+ that the user should be allowed to choose.
+ pszVendor (LPCSTR)
+ Name of the vendor of the product. This value should be NULL if
+ pszProductName is NULL, and should be non-NULL if pszProductName is
+ non-NULL.
+ dwFlags (DWORD)
+ A bitfield containing one or more of the following:
+ CCF_ENTER_FLAG_PER_SEAT_ONLY
+ Allow the user to enter only per seat licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SERVER_ONLY.
+ CCF_ENTER_FLAG_PER_SERVER_ONLY
+ Allow the user to enter only per server licenses. Not valid in
+ combination with CCF_ENTER_FLAG_PER_SEAT_ONLY.
+ pszSourceToUse (LPCSTR)
+ Name of the secure certificate source to use to install the certificate,
+ e.g., "Paper". A NULL value indicates that the user should be allowed
+ to choose.
+
+Return Value:
+
+ ERROR_SUCCESS (A certificate was successfully entered into the system.)
+ ERROR_CANCELLED (The user cancelled without installing a certificate.)
+ other Win error
+
+--*/
+
+{
+ DWORD dwError;
+
+ m_pszServerName = pszServerName;
+ m_pszProductName = pszProductName;
+ m_pszVendor = pszVendor;
+ m_dwEnterFlags = dwFlags;
+
+ if ( pszSourceToUse != NULL )
+ {
+ CString strSourceToUse = pszSourceToUse;
+ int nSrcIndex;
+
+ for ( nSrcIndex = 0; nSrcIndex < m_cslSourceList.GetNumSources(); nSrcIndex++ )
+ {
+ if ( !strSourceToUse.CompareNoCase( m_cslSourceList.GetSourceDisplayName( nSrcIndex ) ) )
+ {
+ // use this certificate source
+ break;
+ }
+ }
+
+ if ( m_cslSourceList.GetNumSources() == nSrcIndex )
+ {
+ // requested certificate source is not available
+ dwError = ERROR_SERVICE_NOT_FOUND;
+ }
+ else
+ {
+ // don't display dialog, just use the indicated source
+ dwError = CallCertificateSource( nSrcIndex );
+ }
+ }
+ else if ( pszProductName != NULL )
+ {
+ // find out if this is a secure product
+ if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ }
+ else
+ {
+ BOOL bProductIsSecure;
+
+ if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ // no extended RPC, so all products on this server must be unsecure
+ bProductIsSecure = FALSE;
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ LPTSTR pszUniProductName;
+
+ pszUniProductName = (LPTSTR) LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * ( 1 + strlen( pszProductName ) ) );
+
+ if ( NULL == pszUniProductName )
+ {
+ dwError = ERROR_NOT_ENOUGH_MEMORY;
+ theApp.SetLastError( dwError );
+ theApp.DisplayLastError();
+ }
+ else
+ {
+ dwError = ERROR_SUCCESS;
+
+ wsprintf( pszUniProductName, TEXT( "%hs" ), pszProductName );
+
+ BOOL bIsSecure;
+
+ bProductIsSecure = ( STATUS_SUCCESS == ::LlsProductSecurityGet( m_hLls, pszUniProductName, &bIsSecure ) )
+ && bIsSecure;
+
+ LocalFree( pszUniProductName );
+ }
+ }
+
+ if ( ERROR_SUCCESS == dwError )
+ {
+ if ( !bProductIsSecure )
+ {
+ // unsecure product; no need to select source
+ dwError = NoCertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+ else if ( 1 == m_cslSourceList.GetNumSources() )
+ {
+ // product is secure and there is only one source to choose from; use it!
+ dwError = CallCertificateSource( 0 );
+ }
+ else if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+ }
+ }
+ }
+ else if ( !ConnectServer() )
+ {
+ dwError = theApp.GetLastError();
+ }
+ else if ( !LlsCapabilityIsSupported( m_hLls, LLS_CAPABILITY_SECURE_CERTIFICATES )
+ || !m_cslSourceList.GetNumSources() )
+ {
+ // secure certificates not supported or no sources available; use unsecure source
+ dwError = NoCertificateEnter( hWndParent, pszServerName, pszProductName, pszVendor, dwFlags );
+ }
+ else if ( IDOK == DoModal() )
+ {
+ dwError = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwError = ERROR_CANCELLED;
+ }
+
+ return dwError;
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/source.h b/private/net/svcdlls/lls/ccfapi32/source.h
new file mode 100644
index 000000000..ae54e1035
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/source.h
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ source.h
+
+Abstract:
+
+ Select certificate source dialog prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 13-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "afxwin.h"
+#include "srclist.h"
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CCertSourceSelect dialog
+
+class CCertSourceSelectDlg : public CDialog
+{
+public:
+ CCertSourceSelectDlg(CWnd* pParent = NULL);
+ ~CCertSourceSelectDlg();
+
+ DWORD CertificateEnter( HWND hWndParent, LPCSTR pszServerName, LPCSTR pszProductName, LPCSTR pszVendor, DWORD dwFlags, LPCSTR pszSourceToUse );
+
+ void AbortDialogIfNecessary();
+
+ DWORD CallCertificateSource( int nIndex );
+
+ BOOL ConnectServer();
+ NTSTATUS ConnectTo( LPTSTR pszServerName, PLLS_HANDLE phLls );
+
+ void GetSourceList();
+
+ LLS_HANDLE m_hLls;
+
+ LPCSTR m_pszServerName;
+ LPCSTR m_pszProductName;
+ LPCSTR m_pszVendor;
+ DWORD m_dwEnterFlags;
+
+ CCertSourceList m_cslSourceList;
+
+// Dialog Data
+ //{{AFX_DATA(CCertSourceSelectDlg)
+ enum { IDD = IDD_CERT_SOURCE_SELECT };
+ CComboBox m_cboxSource;
+ CString m_strSource;
+ //}}AFX_DATA
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CCertSourceSelectDlg)
+ public:
+ virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ // Generated message map functions
+ //{{AFX_MSG(CCertSourceSelectDlg)
+ virtual BOOL OnInitDialog();
+ virtual void OnOK();
+ afx_msg void OnHelp();
+ afx_msg void OnDestroy();
+ afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
diff --git a/private/net/svcdlls/lls/ccfapi32/sources b/private/net/svcdlls/lls/ccfapi32/sources
new file mode 100644
index 000000000..7d0ca9d63
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/sources
@@ -0,0 +1,51 @@
+MAJORCOMP=lls
+MINORCOMP=ccfapi32
+
+TARGETNAME=ccfapi32
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+USE_MFCUNICODE=1
+
+PRECOMPILED_INCLUDE=stdafx.h
+PRECOMPILED_CXX=1
+
+C_DEFINES=-DUNICODE -D_UNICODE
+
+DLLENTRY=_DllMainCRTStartup
+
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\llsrpc.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\uuid.lib \
+ $(BASEDIR)\public\sdk\lib\*\ole32.lib \
+ $(BASEDIR)\public\sdk\lib\*\oleaut32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comctl32.lib \
+ $(BASEDIR)\public\sdk\lib\*\winspool.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\gdi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib
+
+INCLUDES= \
+ $(BASEDIR)\private\inc; \
+ $(BASEDIR)\private\net\inc; \
+ $(BASEDIR)\private\net\svcdlls\lls\inc
+
+SOURCES= \
+ ccfapi.cpp \
+ ccfapi32.rc \
+ exports.cpp \
+ licobj.cpp \
+ md4c.cpp \
+ nlicdlg.cpp \
+ paper.cpp \
+ pseatdlg.cpp \
+ psrvdlg.cpp \
+ remdlg.cpp \
+ source.cpp \
+ srclist.cpp \
+ utils.cpp
diff --git a/private/net/svcdlls/lls/ccfapi32/srclist.cpp b/private/net/svcdlls/lls/ccfapi32/srclist.cpp
new file mode 100644
index 000000000..9160c8591
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/srclist.cpp
@@ -0,0 +1,393 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ srclist.cpp
+
+Abstract:
+
+ Certificate source list object implementation.
+
+Author:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include "stdafx.h"
+#include "srclist.h"
+
+// key name under which the individual source key names may be found
+#define KEY_CERT_SOURCE_LIST "Software\\LSAPI\\Microsoft\\CertificateSources"
+
+// value name for the path to the certificate source DLL (REG_EXPAND_SZ)
+#define VALUE_CERT_SOURCE_PATH "ImagePath"
+
+// value name for the display name of the certificate source
+#define VALUE_CERT_DISPLAY_NAME "DisplayName"
+
+
+CCertSourceList::CCertSourceList()
+
+/*++
+
+Routine Description:
+
+ Constructor for object.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ m_dwNumSources = 0;
+ m_ppcsiSourceList = NULL;
+
+ RefreshSources();
+}
+
+
+CCertSourceList::~CCertSourceList()
+
+/*++
+
+Routine Description:
+
+ Destructor for dialog.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ RemoveSources();
+}
+
+
+BOOL CCertSourceList::RefreshSources()
+
+/*++
+
+Routine Description:
+
+ Refresh source list from configuration stored in the registry.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ LONG lError;
+ LONG lEnumError;
+ HKEY hKeyCertSourceList;
+ int iSubKey;
+ HKEY hKeyCertSource;
+ DWORD cb;
+ BOOL ok;
+ PCERT_SOURCE_INFO pcsiSourceInfo;
+ DWORD cch;
+ TCHAR szExpImagePath[ _MAX_PATH ];
+
+ RemoveSources();
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT( KEY_CERT_SOURCE_LIST ), 0, KEY_READ, &hKeyCertSourceList );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ iSubKey = 0;
+
+ do
+ {
+ ok = FALSE;
+
+ pcsiSourceInfo = (PCERT_SOURCE_INFO) LocalAlloc( LPTR, sizeof( *pcsiSourceInfo ) );
+
+ if ( NULL != pcsiSourceInfo )
+ {
+ // determine next certificate source
+ cch = sizeof( pcsiSourceInfo->szName ) / sizeof( pcsiSourceInfo->szName[0] );
+ lEnumError = RegEnumKeyEx( hKeyCertSourceList, iSubKey, pcsiSourceInfo->szName, &cch, NULL, NULL, NULL, NULL );
+ iSubKey++;
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // open certificate source's key
+ lError = RegOpenKeyEx( hKeyCertSourceList, pcsiSourceInfo->szName, 0, KEY_READ, &hKeyCertSource );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // certificate source key opened; get its REG_EXPAND_SZ image path
+ cb = sizeof( szExpImagePath );
+ lError = RegQueryValueEx( hKeyCertSource, TEXT( VALUE_CERT_SOURCE_PATH ), NULL, NULL, (LPBYTE) szExpImagePath, &cb );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // translate environment variables in path
+ cch = ExpandEnvironmentStrings( szExpImagePath, pcsiSourceInfo->szImagePath, sizeof( pcsiSourceInfo->szImagePath ) / sizeof( pcsiSourceInfo->szImagePath[0] ) );
+
+ if ( ( 0 != cch ) && ( cch < sizeof( pcsiSourceInfo->szImagePath ) / sizeof( pcsiSourceInfo->szImagePath[0] ) ) )
+ {
+ // get display name
+ cb = sizeof( pcsiSourceInfo->szDisplayName );
+ lError = RegQueryValueEx( hKeyCertSource, TEXT( VALUE_CERT_DISPLAY_NAME ), NULL, NULL, (LPBYTE) pcsiSourceInfo->szDisplayName, &cb );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ // default display name is the key name
+ lstrcpy( pcsiSourceInfo->szDisplayName, pcsiSourceInfo->szName );
+ }
+
+ // add the certificate source to our list
+ AddSource( pcsiSourceInfo );
+
+ ok = TRUE;
+ }
+ }
+
+ RegCloseKey( hKeyCertSource );
+ }
+ }
+
+ if ( !ok )
+ {
+ // an error occurred before saving our pointer; don't leak!
+ LocalFree( pcsiSourceInfo );
+ }
+ }
+
+ } while ( ( NULL != pcsiSourceInfo ) && ( ERROR_SUCCESS == lEnumError ) );
+
+ RegCloseKey( hKeyCertSourceList );
+ }
+
+ // 'salright
+ return TRUE;
+}
+
+
+BOOL CCertSourceList::RemoveSources()
+
+/*++
+
+Routine Description:
+
+ Free internal certificate source list.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( NULL != m_ppcsiSourceList )
+ {
+ for ( DWORD i=0; i < m_dwNumSources; i++ )
+ {
+ LocalFree( m_ppcsiSourceList[i] );
+ }
+
+ LocalFree( m_ppcsiSourceList );
+ }
+
+ m_ppcsiSourceList = NULL;
+ m_dwNumSources = 0;
+
+ return TRUE;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceName( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the name (e.g., "Paper") of the source at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszName;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszName = NULL;
+ }
+ else
+ {
+ pszName = m_ppcsiSourceList[ nIndex ]->szName;
+ }
+
+ return pszName;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceDisplayName( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the display name (e.g., "Paper Certificate") of the source
+ at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszDisplayName;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszDisplayName = NULL;
+ }
+ else
+ {
+ pszDisplayName = m_ppcsiSourceList[ nIndex ]->szDisplayName;
+ }
+
+ return pszDisplayName;
+}
+
+
+LPCTSTR CCertSourceList::GetSourceImagePath( int nIndex )
+
+/*++
+
+Routine Description:
+
+ Get the image path name (e.g., "C:\WINNT35\SYSTEM32\CCFAPI32.DLL") of
+ the source at the given index.
+
+Arguments:
+
+ nIndex (int)
+
+Return Values:
+
+ LPCTSTR.
+
+--*/
+
+{
+ LPTSTR pszImagePath;
+
+ if ( ( nIndex < 0 ) || ( nIndex >= (int) m_dwNumSources ) )
+ {
+ pszImagePath = NULL;
+ }
+ else
+ {
+ pszImagePath = m_ppcsiSourceList[ nIndex ]->szImagePath;
+ }
+
+ return pszImagePath;
+}
+
+
+int CCertSourceList::GetNumSources()
+
+/*++
+
+Routine Description:
+
+ Get the number of certificate sources available.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ int.
+
+--*/
+
+{
+ return m_dwNumSources;
+}
+
+
+BOOL CCertSourceList::AddSource( PCERT_SOURCE_INFO pcsiNewSource )
+
+/*++
+
+Routine Description:
+
+ Add a source to the internal list.
+
+Arguments:
+
+ pcsiNewSource (PCERT_SOURCE_INFO)
+
+Return Values:
+
+ BOOL.
+
+--*/
+
+{
+ if ( 0 == m_dwNumSources )
+ {
+ m_ppcsiSourceList = (PCERT_SOURCE_INFO *) LocalAlloc( LMEM_FIXED, sizeof( pcsiNewSource ) );
+ }
+ else
+ {
+ m_ppcsiSourceList = (PCERT_SOURCE_INFO *) LocalReAlloc( m_ppcsiSourceList, ( 1 + m_dwNumSources ) * sizeof( pcsiNewSource ), 0 );
+ }
+
+ if ( NULL != m_ppcsiSourceList )
+ {
+ m_ppcsiSourceList[ m_dwNumSources ] = pcsiNewSource;
+ m_dwNumSources++;
+ }
+ else
+ {
+ m_dwNumSources = 0;
+ }
+
+ return ( NULL != m_ppcsiSourceList );
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/srclist.h b/private/net/svcdlls/lls/ccfapi32/srclist.h
new file mode 100644
index 000000000..19d146a36
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/srclist.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ srclist.h
+
+Abstract:
+
+ Certificate source list object prototype.
+
+Author:
+
+ Jeff Parham (jeffparh) 15-Dec-1995
+
+Revision History:
+
+--*/
+
+
+typedef struct _CERT_SOURCE_INFO
+{
+ TCHAR szName[ 64 ];
+ TCHAR szDisplayName[ 64 ];
+ TCHAR szImagePath[ _MAX_PATH ];
+} CERT_SOURCE_INFO, *PCERT_SOURCE_INFO;
+
+class CCertSourceList
+{
+public:
+ CCertSourceList();
+ ~CCertSourceList();
+
+ BOOL RefreshSources();
+ LPCTSTR GetSourceName( int nIndex );
+ LPCTSTR GetSourceDisplayName( int nIndex );
+ LPCTSTR GetSourceImagePath( int nIndex );
+ int GetNumSources();
+
+private:
+ BOOL RemoveSources();
+ BOOL AddSource( PCERT_SOURCE_INFO pcsiNewSource );
+
+ PCERT_SOURCE_INFO * m_ppcsiSourceList;
+ DWORD m_dwNumSources;
+};
diff --git a/private/net/svcdlls/lls/ccfapi32/stdafx.h b/private/net/svcdlls/lls/ccfapi32/stdafx.h
new file mode 100644
index 000000000..cfc26a5de
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/stdafx.h
@@ -0,0 +1,46 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+// #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+// #define _AFX_NO_OLE_SUPPORT
+// #define _AFX_NO_DB_SUPPORT
+#define _AFX_NO_DAO_SUPPORT
+// #define _AFX_NO_AFXCMN_SUPPORT
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#ifdef ASSERT
+# undef ASSERT
+#endif
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+
+#include <llsapi.h>
+
+#ifndef _AFX_NO_OLE_SUPPORT
+#include <afxole.h> // MFC OLE classes
+#include <afxodlgs.h> // MFC OLE dialog classes
+#include <afxdisp.h> // MFC OLE automation classes
+#endif // _AFX_NO_OLE_SUPPORT
+
+
+#ifndef _AFX_NO_DB_SUPPORT
+#include <afxdb.h> // MFC ODBC database classes
+#endif // _AFX_NO_DB_SUPPORT
+
+#ifndef _AFX_NO_DAO_SUPPORT
+#include <afxdao.h> // MFC DAO database classes
+#endif // _AFX_NO_DAO_SUPPORT
+
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows 95 Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+
+
+
diff --git a/private/net/svcdlls/lls/ccfapi32/utils.cpp b/private/net/svcdlls/lls/ccfapi32/utils.cpp
new file mode 100644
index 000000000..ffdec9ba0
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/utils.cpp
@@ -0,0 +1,713 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ utils.cpp
+
+Abstract:
+
+ Utiltities.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, stripped Tv (Tree view) functions,
+ removed OLE support
+
+--*/
+
+#include "stdafx.h"
+#include "ccfapi.h"
+#include "utils.h"
+
+#define _AFX_NO_OLE_SUPPORT
+
+//
+// List view utilities
+//
+
+void LvInitColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo)
+
+/*++
+
+Routine Description:
+
+ Initializes list view columns.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column information.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ int nStringId;
+ CString strText;
+ LV_COLUMN lvColumn;
+
+ int nColumns = plvColumnInfo->nColumns;
+ PLV_COLUMN_ENTRY plvColumnEntry = plvColumnInfo->lvColumnEntry;
+
+ lvColumn.mask = LVCF_FMT|
+ LVCF_TEXT|
+ LVCF_SUBITEM;
+
+ lvColumn.fmt = LVCFMT_LEFT;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nColumns--)
+ {
+ lvColumn.iSubItem = plvColumnEntry->iSubItem;
+
+ if (nStringId = plvColumnEntry->nStringId)
+ {
+ strText.LoadString(nStringId);
+ }
+ else
+ {
+ strText = _T("");
+ }
+
+ lvColumn.pszText = strText.GetBuffer(0);
+
+ int nColumnInserted = pListCtrl->InsertColumn( lvColumn.iSubItem, &lvColumn );
+ ASSERT( -1 != nColumnInserted );
+
+ plvColumnEntry++;
+
+ strText.ReleaseBuffer();
+ }
+
+ SetDefaultFont(pListCtrl);
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvResizeColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo)
+
+/*++
+
+Routine Description:
+
+ Resizes list view columns.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column information.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ int nColumnWidth;
+ int nRelativeWidth;
+ int nEntireWidthSoFar = 0;
+ int nColumns = plvColumnInfo->nColumns;
+ PLV_COLUMN_ENTRY plvColumnEntry = plvColumnInfo->lvColumnEntry;
+
+ CRect clientRect;
+ pListCtrl->GetClientRect(clientRect);
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while ((nRelativeWidth = plvColumnEntry->nRelativeWidth) != -1)
+ {
+ nColumnWidth = (nRelativeWidth * clientRect.Width()) / 100;
+ pListCtrl->SetColumnWidth(plvColumnEntry->iSubItem, nColumnWidth);
+ nEntireWidthSoFar += nColumnWidth;
+ plvColumnEntry++;
+ }
+
+ nColumnWidth = clientRect.Width() - nEntireWidthSoFar;
+ pListCtrl->SetColumnWidth(plvColumnEntry->iSubItem, nColumnWidth);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvChangeFormat(CListCtrl* pListCtrl, UINT nFormatId)
+
+/*++
+
+Routine Description:
+
+ Changes window style of list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ nFormatId - format specification.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ DWORD dwStyle = ::GetWindowLong(pListCtrl->GetSafeHwnd(), GWL_STYLE);
+
+ pListCtrl->BeginWaitCursor();
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ if ((dwStyle & LVS_TYPEMASK) != nFormatId)
+ {
+ ::SetWindowLong(
+ pListCtrl->GetSafeHwnd(),
+ GWL_STYLE,
+ (dwStyle & ~LVS_TYPEMASK) | nFormatId
+ );
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+ pListCtrl->EndWaitCursor();
+}
+
+
+LPVOID LvGetSelObj(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Retrieves the object selected (assumes one) from list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ Same as LvGetNextObj.
+
+--*/
+
+{
+ int iItem = -1;
+ return LvGetNextObj(pListCtrl, &iItem);
+}
+
+
+LPVOID LvGetNextObj(CListCtrl* pListCtrl, LPINT piItem, int nType)
+
+/*++
+
+Routine Description:
+
+ Retrieves the next object selected from list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ piItem - starting index (updated).
+ nType - specifies search criteria.
+
+Return Values:
+
+ Returns object pointer or null.
+
+--*/
+
+{
+ ASSERT(piItem);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ if ((lvItem.iItem = pListCtrl->GetNextItem(*piItem, nType)) != -1)
+ {
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iSubItem = 0;
+
+ if (pListCtrl->GetItem(&lvItem))
+ {
+ *piItem = lvItem.iItem;
+ return (LPVOID)lvItem.lParam;
+ }
+ }
+
+ return NULL;
+}
+
+
+BOOL LvInsertObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray)
+
+/*++
+
+Routine Description:
+
+ Insert object array into list view.
+ Note list view must be unsorted and support LVN_GETDISPINFO.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column info.
+ pObArray - object array.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pObArray, CObArray);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ ASSERT(plvColumnInfo);
+ ASSERT(pListCtrl->GetItemCount() == 0);
+
+ BOOL bItemsInserted = FALSE;
+
+ LV_ITEM lvItem;
+
+ lvItem.mask = LVIF_TEXT|
+ LVIF_PARAM|
+ LVIF_IMAGE;
+
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.iImage = I_IMAGECALLBACK;
+ lvItem.iSubItem = 0;
+
+ int iItem;
+ int iSubItem;
+
+ int nItems = pObArray->GetSize();
+ ASSERT(nItems != -1); // iItem is -1 if error...
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ pListCtrl->SetItemCount(nItems);
+
+ CObject* pObject = NULL;
+
+ for (iItem = 0; (-1 != iItem) && (iItem < nItems) && (pObject = pObArray->GetAt(iItem)); iItem++)
+ {
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.iItem = iItem;
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ iItem = pListCtrl->InsertItem(&lvItem);
+ ASSERT((iItem == lvItem.iItem) || (iItem == -1));
+
+ if ( -1 != iItem )
+ {
+ for (iSubItem = 1; iSubItem < plvColumnInfo->nColumns; iSubItem++)
+ {
+ BOOL ok = pListCtrl->SetItemText(iItem, iSubItem, LPSTR_TEXTCALLBACK);
+ ASSERT( ok );
+ }
+ }
+ }
+
+ if (iItem == nItems)
+ {
+ bItemsInserted = TRUE;
+ VERIFY(pListCtrl->SetItemState(
+ 0,
+ LVIS_FOCUSED|
+ LVIS_SELECTED,
+ LVIS_FOCUSED|
+ LVIS_SELECTED
+ ));
+ }
+ else
+ {
+ theApp.SetLastError(ERROR_OUTOFMEMORY);
+ VERIFY(pListCtrl->DeleteAllItems());
+ }
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+
+ return bItemsInserted;
+}
+
+
+BOOL
+LvRefreshObArray(
+ CListCtrl* pListCtrl,
+ PLV_COLUMN_INFO plvColumnInfo,
+ CObArray* pObArray
+ )
+
+/*++
+
+Routine Description:
+
+ Refresh object array in list view.
+
+Arguments:
+
+ pListCtrl - list control.
+ plvColumnInfo - column info.
+ pObArray - object array.
+
+Return Values:
+
+ VT_BOOL.
+
+--*/
+
+{
+ ASSERT(plvColumnInfo);
+ VALIDATE_OBJECT(pObArray, CObArray);
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ long nObjects = pObArray->GetSize();
+ long nObjectsInList = pListCtrl->GetItemCount();
+
+ if (!nObjects)
+ {
+ LvReleaseObArray(pListCtrl);
+ return TRUE;
+ }
+ else if (!nObjectsInList)
+ {
+ return LvInsertObArray(
+ pListCtrl,
+ plvColumnInfo,
+ pObArray
+ );
+ }
+
+ CObject* pObject;
+
+ int iObject = 0;
+ int iObjectInList = 0;
+
+ LV_ITEM lvItem;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iItem = iObjectInList;
+ lvItem.iSubItem = 0;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ if (iObject < nObjects)
+ {
+ pObject = pObArray->GetAt(iObject++);
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.mask = LVIF_TEXT|LVIF_PARAM;
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ VERIFY(pListCtrl->SetItem(&lvItem)); // overwrite...
+
+ iObjectInList++; // increment count...
+ }
+ else
+ {
+ VERIFY(pListCtrl->DeleteItem(iObjectInList));
+ }
+ }
+
+ lvItem.mask = LVIF_TEXT|
+ LVIF_PARAM|
+ LVIF_IMAGE;
+
+ lvItem.pszText = LPSTR_TEXTCALLBACK;
+ lvItem.cchTextMax = LPSTR_TEXTCALLBACK_MAX;
+ lvItem.iImage = I_IMAGECALLBACK;
+ lvItem.iSubItem = 0;
+
+ int iItem;
+ int iSubItem;
+
+ while (iObject < nObjects)
+ {
+ lvItem.iItem = iObject;
+
+ pObject = pObArray->GetAt(iObject++);
+ VALIDATE_OBJECT(pObject, CObject);
+
+ lvItem.lParam = (LPARAM)(LPVOID)pObject;
+
+ iItem = pListCtrl->InsertItem(&lvItem);
+ ASSERT((iItem == lvItem.iItem) && (iItem != -1));
+
+ for (iSubItem = 1; iSubItem < plvColumnInfo->nColumns; iSubItem++)
+ {
+ VERIFY(pListCtrl->SetItemText(iItem, iSubItem, LPSTR_TEXTCALLBACK));
+ }
+ }
+
+ LvResizeColumns(pListCtrl, plvColumnInfo);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+
+ return TRUE;
+}
+
+
+void LvReleaseObArray(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release objects inserted into list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ CObject* pObject;
+
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iItem = 0;
+ lvItem.iSubItem = 0;
+
+ int nObjectsInList = pListCtrl->GetItemCount();
+
+ pListCtrl->BeginWaitCursor();
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ VERIFY(pListCtrl->DeleteItem(lvItem.iItem));
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+ pListCtrl->EndWaitCursor();
+}
+
+
+void LvReleaseSelObjs(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release selected objects in list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ lvItem.mask = LVIF_PARAM;
+ lvItem.iSubItem = 0;
+
+ CObject* pObject;
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ int iItem = pListCtrl->GetNextItem(-1, LVNI_ALL|LVNI_SELECTED);
+
+ while (iItem != -1)
+ {
+ lvItem.iItem = iItem;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ iItem = pListCtrl->GetNextItem(lvItem.iItem, LVNI_ALL|LVNI_SELECTED);
+
+ VERIFY(pListCtrl->DeleteItem(lvItem.iItem));
+ }
+
+ LvSelObjIfNecessary(pListCtrl);
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+
+void LvSelObjIfNecessary(CListCtrl* pListCtrl, BOOL bSetFocus)
+
+/*++
+
+Routine Description:
+
+ Ensure that object selected.
+
+Arguments:
+
+ pListCtrl - list control.
+ bSetFocus - true if focus to be set focus as well.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ if (!IsItemSelectedInList(pListCtrl) && pListCtrl->GetItemCount())
+ {
+ pListCtrl->SendMessage(WM_KEYDOWN, VK_RIGHT); // HACKHACK...
+
+ int iItem = pListCtrl->GetNextItem(-1, LVNI_FOCUSED|LVNI_ALL);
+ int nState = bSetFocus ? (LVIS_SELECTED|LVIS_FOCUSED) : LVIS_SELECTED;
+
+ VERIFY(pListCtrl->SetItemState((iItem == -1) ? 0 : iItem, nState, nState));
+ }
+}
+
+
+#ifdef _DEBUG
+
+void LvDumpObArray(CListCtrl* pListCtrl)
+
+/*++
+
+Routine Description:
+
+ Release objects inserted into list view.
+
+Arguments:
+
+ pListCtrl - list control.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pListCtrl, CListCtrl);
+
+ LV_ITEM lvItem;
+
+ CString strDump;
+ CObject* pObject;
+
+ lvItem.mask = LVIF_STATE|LVIF_PARAM;
+ lvItem.stateMask = (DWORD)-1;
+ lvItem.iSubItem = 0;
+
+ int nObjectsInList = pListCtrl->GetItemCount();
+
+ pListCtrl->SetRedraw(FALSE); // turn off drawing...
+
+ while (nObjectsInList--)
+ {
+ lvItem.iItem = nObjectsInList;
+
+ VERIFY(pListCtrl->GetItem(&lvItem));
+
+ pObject = (CObject*)lvItem.lParam;
+ VALIDATE_OBJECT(pObject, CObject);
+
+ strDump.Format(_T("iItem %d"), lvItem.iItem);
+ strDump += (lvItem.state & LVIS_CUT) ? _T(" LVIS_CUT ") : _T("");
+ strDump += (lvItem.state & LVIS_FOCUSED) ? _T(" LVIS_FOCUSED ") : _T("");
+ strDump += (lvItem.state & LVIS_SELECTED) ? _T(" LVIS_SELECTED ") : _T("");
+ strDump += _T("\r\n");
+
+ afxDump << strDump;
+ }
+
+ pListCtrl->SetRedraw(TRUE); // turn on drawing...
+}
+
+#endif
+
+
+void SetDefaultFont(CWnd* pWnd)
+
+/*++
+
+Routine Description:
+
+ Set default font.
+
+Arguments:
+
+ pWnd - window to change font.
+
+Return Values:
+
+ None.
+
+--*/
+
+{
+ VALIDATE_OBJECT(pWnd, CWnd);
+
+ HFONT hFont;
+ LOGFONT lFont;
+
+ memset(&lFont, 0, sizeof(LOGFONT)); // initialize
+
+ lFont.lfHeight = 13;
+ lFont.lfWeight = 200; // non-bold
+
+ hFont = ::CreateFontIndirect(&lFont);
+ pWnd->SetFont(CFont::FromHandle(hFont));
+}
diff --git a/private/net/svcdlls/lls/ccfapi32/utils.h b/private/net/svcdlls/lls/ccfapi32/utils.h
new file mode 100644
index 000000000..09f42b879
--- /dev/null
+++ b/private/net/svcdlls/lls/ccfapi32/utils.h
@@ -0,0 +1,93 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ utils.h
+
+Abstract:
+
+ Utilities.
+
+Author:
+
+ Don Ryan (donryan) 04-Jan-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Nov-1995
+ Copied from LLSMGR, stripped Tv (Tree view) functions,
+ removed OLE support
+
+--*/
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#define LPSTR_TEXTCALLBACK_MAX 260
+
+//
+// List view utilities
+//
+
+#define LVID_SEPARATOR 0
+#define LVID_UNSORTED_LIST -1
+
+typedef struct _LV_COLUMN_ENTRY {
+
+ int iSubItem; // column index
+ int nStringId; // header string id
+ int nRelativeWidth; // header width
+
+} LV_COLUMN_ENTRY, *PLV_COLUMN_ENTRY;
+
+#pragma warning(disable:4200)
+typedef struct _LV_COLUMN_INFO {
+
+ BOOL bSortOrder; // sort order (ascending false)
+ int nSortedItem; // column sorted (default none)
+
+ int nColumns;
+ LV_COLUMN_ENTRY lvColumnEntry[];
+
+} LV_COLUMN_INFO, *PLV_COLUMN_INFO;
+#pragma warning(default:4200)
+
+void LvInitColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo);
+void LvResizeColumns(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo);
+void LvChangeFormat(CListCtrl* pListCtrl, UINT nFormatId);
+
+LPVOID LvGetSelObj(CListCtrl* pListCtrl);
+LPVOID LvGetNextObj(CListCtrl* pListCtrl, LPINT piItem, int nType = LVNI_ALL|LVNI_SELECTED);
+void LvSelObjIfNecessary(CListCtrl* pListCtrl, BOOL bSetFocus = FALSE);
+
+BOOL LvInsertObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray);
+BOOL LvRefreshObArray(CListCtrl* pListCtrl, PLV_COLUMN_INFO plvColumnInfo, CObArray* pObArray);
+void LvReleaseObArray(CListCtrl* pListCtrl);
+void LvReleaseSelObjs(CListCtrl* pListCtrl);
+
+#ifdef _DEBUG
+void LvDumpObArray(CListCtrl* pListCtrl);
+#endif
+
+#define IsItemSelectedInList(plv) (::LvGetSelObj((CListCtrl*)(plv)) != NULL)
+
+//
+// Other stuff...
+//
+
+void SetDefaultFont(CWnd* pWnd);
+
+#ifdef _DEBUG
+#define VALIDATE_OBJECT(pOb, ObClass) \
+ { ASSERT_VALID((pOb)); ASSERT((pOb)->IsKindOf(RUNTIME_CLASS(ObClass))); }
+#else
+#define VALIDATE_OBJECT(pOb, ObClass)
+#endif
+
+#endif // _UTILS_H_
diff --git a/private/net/svcdlls/lls/client/llsevent.mc b/private/net/svcdlls/lls/client/llsevent.mc
new file mode 100644
index 000000000..7ba61c628
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsevent.mc
@@ -0,0 +1,176 @@
+;//=============================================================================
+;// Microsoft (R) License Logging Service (tm). Copyright (C) 1991-1995.
+;//
+;// MODULE: llsevent.mc
+;//
+;// Modification History
+;//
+;// arth 10-Mar-1995 Created
+;// jeffparh 05-Nov-1995 Added replication events.
+;// jeffparh 16-Nov-1995 Added certificate database events.
+;//=============================================================================
+
+;//
+;#ifndef _LLSEVENT_
+;#define _LLSEVENT_
+;//
+
+MessageIdTypedef=DWORD
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+
+
+;
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// LLS Events messages 1-100 are informational
+;//
+;/////////////////////////////////////////////////////////////////////////
+;
+
+MessageId=1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_STARTED
+Language=English
+The License Logging Service has started successfully.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_STOPPED
+Language=English
+The License Logging Service has stopped successfully.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_BACKOFF
+Language=English
+Server %1 has requested that license database replication to it be delayed.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_START
+Language=English
+License database replication to server %1 has started.
+.
+
+MessageId=+1 Severity=Informational Facility=Application SymbolicName=LLS_EVENT_REPL_END
+Language=English
+License database replication to server %1 has completed successfully.
+.
+
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// LLS messages 200+ are warnings and errors
+;//
+
+MessageId=200 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_NO_MEMORY
+Language=English
+The License Logging Service was unable to allocate memory.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_USER_NO_LICENSE
+Language=English
+No license was available for user %1 using product %2.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_PRODUCT_NO_LICENSE
+Language=English
+The product %1 is out of licenses. Use License Manager from the Administrative Tools folder for more information on which users are out of compliance and how many licenses should be purchased.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_USER
+Language=English
+The user data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_MAPPING
+Language=English
+The license group data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_LICENSE
+Language=English
+The purchased license data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_PRODUCT
+Language=English
+The product data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_SERVER
+Language=English
+The replicated server data could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_USER
+Language=English
+The saved user data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_MAPPING
+Language=English
+The saved license group data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_LICENSE
+Language=English
+The saved purchased license data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_PRODUCT
+Language=English
+The saved product data could not be restored.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_SERVER
+Language=English
+The saved replicated server data could not be restored.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_NO_CONNECTION
+Language=English
+Replication of license information failed because the License Logging Service on server %1 could not be contacted.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_REQUEST_FAILED
+Language=English
+The License Logging Service encountered an error while initiating replication to server %1.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_FAILED
+Language=English
+License database replication to server %1 was unsuccessful.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_CERT_VIOLATION_SERVER_ENTRY
+Language=English
+%1\t(%2 licenses)
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_CERT_VIOLATION
+Language=English
+The license certificate for product %1 with serial number %2 is in violation. There are currently %3 licenses installed from this certificate, while only %4 are allowed by the license agreement. The servers with this certificate installed are as follows:
+
+%5
+
+Use License Manager to remove licenses in order to comply with the license agreement.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_SAVE_CERT_DB
+Language=English
+The certificate database could not be saved.
+.
+
+MessageId=+1 Severity=Error Facility=Application SymbolicName=LLS_EVENT_LOAD_CERT_DB
+Language=English
+The certificate database could not be restored.
+.
+
+MessageId=+1 Severity=Warning Facility=Application SymbolicName=LLS_EVENT_REPL_DOWNLEVEL_TARGET
+Language=English
+License database replication cannot be performed to server %1 because the version of Windows NT installed there does not support the License Logging Service.
+.
+
+;
+;#endif // _LLSEVENT.H_
+;
diff --git a/private/net/svcdlls/lls/client/llsrpc.c b/private/net/svcdlls/lls/client/llsrpc.c
new file mode 100644
index 000000000..da941db5d
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.c
@@ -0,0 +1,6600 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsrpc.c
+
+Abstract:
+
+ Client side RPC wrappers for License Logging Service.
+
+Author:
+
+ Arthur Hanson (arth) 30-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 04-Dec-1995
+ o Forced include of LLS API prototypes, exposing an incorrect prototype
+ in LLSAPI.H.
+ o Fixed case where an LSA access denied was interpreted as implying the
+ server had no DC, rather than properly bubbling the access denied back
+ to the caller (of LlsConnectEnterprise()). This plugs a security hole
+ wherein a non-admin user with the ability to read the System registry
+ key would be allowed to administer domain licenses through
+ License Manager. (Bug #11441.)
+ o Added functions to support extended LLSRPC API.
+ o Removed replication dependency on no further LlsConnect()'s being made
+ until replication was completed.
+ o Installed lock around llsrpc_handle global binding variable. Required
+ addition of DllMain() function.
+ o Added LLSRPC capabilities detection. Upon connection, the client
+ requests the server's capabilities (an RPC call which itself will fail
+ when connected to a 3.51 server). The capabilities set is an
+ arbitrary bit field, but individual bits are normally defined to
+ indicate that a specific feature has been implemented at the server.
+ o Added szServerName filed to LOCAL_HANDLE to remember the name of the
+ machine to which we're connected.
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "debug.h"
+
+#include "llsapi.h"
+#include "llsrpc_c.h"
+
+// #define API_TRACE
+
+typedef struct _GENERIC_INFO_CONTAINER {
+ DWORD EntriesRead;
+ LPBYTE Buffer;
+} GENERIC_INFO_CONTAINER, *PGENERIC_INFO_CONTAINER, *LPGENERIC_INFO_CONTAINER ;
+
+typedef struct _GENERIC_ENUM_STRUCT {
+ DWORD Level;
+ PGENERIC_INFO_CONTAINER Container;
+} GENERIC_ENUM_STRUCT, *PGENERIC_ENUM_STRUCT, *LPGENERIC_ENUM_STRUCT ;
+
+
+typedef struct _LOCAL_HANDLE {
+ TCHAR szServerName[ 3 + MAX_COMPUTERNAME_LENGTH ];
+ LPTSTR pszStringBinding;
+ handle_t llsrpc_handle;
+ LLS_HANDLE Handle;
+ BYTE Capabilities[ ( LLS_CAPABILITY_MAX + 7 ) / 8 ];
+} LOCAL_HANDLE, *PLOCAL_HANDLE;
+
+
+LPTSTR pszStringBinding = NULL;
+
+
+RTL_CRITICAL_SECTION g_RpcHandleLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL APIENTRY DllMain( HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved )
+
+/*++
+
+Routine Description:
+
+ Standard DLL entry point.
+
+Arguments:
+
+ hInstance (HINSTANCE)
+ dwReason (DWORD)
+ lpReserved (LPVOID)
+
+Return Value:
+
+ TRUE if successful.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ nt = RtlInitializeCriticalSection( &g_RpcHandleLock );
+ break;
+
+ case DLL_PROCESS_DETACH:
+ nt = RtlDeleteCriticalSection( &g_RpcHandleLock );
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ default:
+ break;
+ }
+
+ return NT_SUCCESS( nt );
+}
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTDomainGet(
+ LPTSTR ServerName,
+ LPTSTR Domain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer;
+
+ lstrcpy(Domain, TEXT(""));
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ if ( (ServerName == NULL) || (ServerName[0] == TEXT('\0')) )
+ ret = LsaOpenPolicy(NULL, &oa, am, &hLSA);
+ else {
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+ }
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL) && (pvBuffer->Sid != NULL)) {
+ lstrcpy(Domain, pvBuffer->Name.Buffer);
+ LsaFreeMemory((PVOID) pvBuffer);
+ } else
+ if (!ret)
+ ret = STATUS_UNSUCCESSFUL;
+ }
+
+ return ret;
+
+} // NTDomainGet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+EnterpriseServerGet(
+ LPTSTR ServerName,
+ LPTSTR pEnterpriseServer
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey = NULL;
+ HKEY hKey2 = NULL;
+ BOOL Enterprise = FALSE;
+ DWORD dwType, dwSize;
+ TCHAR RegKeyText[512];
+ NTSTATUS Status;
+ DWORD UseEnterprise;
+ TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 1];
+ LPTSTR pName = ServerName;
+
+ Status = RegConnectRegistry(ServerName, HKEY_LOCAL_MACHINE, &hKey);
+ if (Status == ERROR_SUCCESS) {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"));
+
+ if ((Status = RegOpenKeyEx(hKey, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(UseEnterprise);
+ Status = RegQueryValueEx(hKey2, TEXT("UseEnterprise"), NULL, &dwType, (LPBYTE) &UseEnterprise, &dwSize);
+
+ if ((Status == ERROR_SUCCESS) && (UseEnterprise == 1)) {
+ dwSize = sizeof(EnterpriseServer);
+ Status = RegQueryValueEx(hKey2, TEXT("EnterpriseServer"), NULL, &dwType, (LPBYTE) &EnterpriseServer, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ pName = EnterpriseServer;
+
+ }
+
+ RegCloseKey(hKey2);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ if (*pName != TEXT('\\')) {
+ lstrcpy(pEnterpriseServer, TEXT("\\\\"));
+ lstrcat(pEnterpriseServer, pName);
+ } else
+ lstrcpy(pEnterpriseServer, pName);
+
+ return STATUS_SUCCESS;
+} // EnterpriseServerGet
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindW(
+ LPTSTR Focus,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ LPTSTR pFocus;
+ BOOL Domain = TRUE;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 3];
+ TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1];
+ NET_API_STATUS uRet;
+ LPBYTE pbBuffer = NULL;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+ ULONG Size;
+
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ lstrcpy(pDomain, TEXT(""));
+ lstrcpy(EnterpriseServer, TEXT(""));
+
+ //
+ // Figure out if doing domain or server
+ //
+ pFocus = Focus;
+ if (pFocus !=NULL)
+ while ((*pFocus != TEXT('\0')) && (*pFocus == TEXT('\\'))) {
+ Domain = FALSE;
+ pFocus++;
+ }
+
+ if (!Domain) {
+ //
+ // If we are given a server, find the domain it is in
+ //
+ Status = NTDomainGet((LPWSTR) Focus, pDomain);
+
+ //
+ // If we got a domain find the DC of it, else find DC of server
+ //
+ if (Status == STATUS_SUCCESS) {
+ Domain = TRUE;
+ uRet = NetGetDCName(NULL, pDomain, &pbBuffer);
+ } else if ( STATUS_ACCESS_DENIED == Status ) {
+ goto LlsEnterpriseServerFindWExit;
+ } else {
+ Domain = FALSE;
+ uRet = 1;
+ }
+
+ } else {
+ //
+ // Get the DC name of wherever we are going
+ //
+ if ((pFocus == NULL) || (*pFocus == TEXT('\0')))
+ uRet = NetGetDCName(NULL, NULL, &pbBuffer);
+ else
+ uRet = NetGetDCName(NULL, pFocus, &pbBuffer);
+ }
+
+ if (uRet) {
+ //
+ // If we focus on a server and can't find a domain then look for an
+ // enterprise server. This is the case if the focus server is a
+ // standalone system.
+ //
+ if (Domain == FALSE) {
+ Status = EnterpriseServerGet((LPTSTR) Focus, EnterpriseServer);
+ goto LlsEnterpriseServerFindWExit;
+ }
+
+ return STATUS_NO_SUCH_DOMAIN;
+ }
+
+ //
+ // pbBuffer contains the DC name - if we don't have the domain yet, then
+ // get that.
+ //
+ Status = NTDomainGet((LPWSTR) pbBuffer, pDomain);
+ if (Status != STATUS_SUCCESS) {
+ NetApiBufferFree(pbBuffer);
+ return Status;
+ }
+
+ //
+ // Go to PDC and figure out if they are replicating anywhere, if so go
+ // to that system.
+ //
+ Status = EnterpriseServerGet((LPTSTR) pbBuffer, EnterpriseServer);
+
+ if (pbBuffer != NULL)
+ NetApiBufferFree(pbBuffer);
+
+LlsEnterpriseServerFindWExit:
+ if (Status != STATUS_SUCCESS)
+ return Status;
+
+ Size = sizeof(LLS_CONNECT_INFO_0);
+ Size += ((lstrlen(pDomain) + 1) * sizeof(TCHAR));
+ Size += ((lstrlen(EnterpriseServer) + 1) * sizeof(TCHAR));
+
+ pConnectInfo = (PLLS_CONNECT_INFO_0) MIDL_user_allocate(Size);
+ if (pConnectInfo == NULL)
+ return STATUS_NO_MEMORY;
+
+ pConnectInfo->Domain = (LPTSTR) (((PBYTE) pConnectInfo) + sizeof(LLS_CONNECT_INFO_0));
+ pConnectInfo->EnterpriseServer = (LPTSTR) &pConnectInfo->Domain[lstrlen(pDomain) + 1];
+
+ lstrcpy(pConnectInfo->Domain, pDomain);
+ lstrcpy(pConnectInfo->EnterpriseServer, EnterpriseServer);
+ *BufPtr = (LPBYTE) pConnectInfo;
+ return Status;
+
+} // LlsEnterpriseServerFindW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindA(
+ LPSTR Focus,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsEnterpriseServerFindA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsEnterpriseServerFindA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectW(
+ LPTSTR Server,
+ LLS_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+ PLOCAL_HANDLE pLocalHandle;
+ handle_t prev_llsrpc_handle;
+
+#ifdef API_TRACE
+ if (Server == NULL)
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectW: <NULL>\n"));
+ else
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectW: %s\n"), Server);
+#endif
+
+ if ((Server != NULL) && ( (Server[0] != TEXT('\\')) || (Server[1] != TEXT('\\')) ) )
+ return STATUS_INVALID_PARAMETER;
+
+ if (Handle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ *Handle = NULL;
+ Size = sizeof(pComputer);
+ GetComputerName(pComputer, &Size);
+
+ if ((Server == NULL) || (*Server == TEXT('\0'))) {
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+ } else {
+ pszProtocolSequence = TEXT("ncacn_np");
+ pszEndpoint = TEXT(LLS_NP_ENDPOINT);
+ pszNetworkAddress = Server;
+ }
+
+ pLocalHandle = MIDL_user_allocate(sizeof(LOCAL_HANDLE));
+ if (pLocalHandle == NULL)
+ return STATUS_NO_MEMORY;
+
+ pLocalHandle->pszStringBinding = NULL;
+ pLocalHandle->llsrpc_handle = NULL;
+ pLocalHandle->Handle = NULL;
+
+ ZeroMemory( pLocalHandle->szServerName, sizeof( pLocalHandle->szServerName ) );
+ if ( NULL != Server )
+ {
+ lstrcpyn( pLocalHandle->szServerName, Server, sizeof( pLocalHandle->szServerName ) / sizeof( *pLocalHandle->szServerName ) - 1 );
+ }
+ else
+ {
+ wsprintf( pLocalHandle->szServerName, TEXT( "\\\\%s" ), pComputer );
+ }
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pLocalHandle->pszStringBinding);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ RtlEnterCriticalSection( &g_RpcHandleLock );
+ prev_llsrpc_handle = llsrpc_handle;
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pLocalHandle->pszStringBinding, &llsrpc_handle);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ Status = I_RpcMapWin32Status(Status);
+ }
+
+ if ( NT_SUCCESS( Status ) )
+ {
+ pLocalHandle->llsrpc_handle = llsrpc_handle;
+
+ try {
+ LlsrConnect(&pLocalHandle->Handle, pComputer);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("LLSRPC ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ( NT_SUCCESS( Status ) )
+ {
+ // get server capabilities
+ try {
+ LlsrCapabilityGet( pLocalHandle->Handle, sizeof( pLocalHandle->Capabilities ), pLocalHandle->Capabilities );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+
+ if ( RPC_NT_PROCNUM_OUT_OF_RANGE == Status )
+ {
+ // 'salright; API doesn't exist at target server (it's running 3.51)
+ ZeroMemory( pLocalHandle->Capabilities, sizeof( pLocalHandle->Capabilities ) );
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+#if DBG
+ dprintf(TEXT("LLSRPC ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+
+ if ( !NT_SUCCESS( Status ) )
+ {
+ LlsClose( pLocalHandle );
+ }
+ else
+ {
+ *Handle = (LLS_HANDLE) pLocalHandle;
+ }
+ }
+ }
+
+ llsrpc_handle = prev_llsrpc_handle;
+
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+
+ return Status;
+} // LlsConnectW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectA(
+ LPSTR Server,
+ LLS_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsConnectA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseW(
+ LPTSTR Focus,
+ LLS_HANDLE* Handle,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Status = LlsEnterpriseServerFindW(Focus, Level, BufPtr);
+
+ if (Status)
+ return Status;
+
+ pConnectInfo = (PLLS_CONNECT_INFO_0) *BufPtr;
+ Status = LlsConnectW(pConnectInfo->EnterpriseServer, Handle);
+
+ return Status;
+
+} // LlsConnectEnterpriseW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseA(
+ LPSTR Focus,
+ LLS_HANDLE* Handle,
+ DWORD Level,
+ LPBYTE *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsConnectEnterpriseA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsConnectEnterpriseA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsClose(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ PLOCAL_HANDLE pLocalHandle;
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ NtStatus = LlsrClose(pLocalHandle->Handle);
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ try {
+ Status = RpcStringFree(&pLocalHandle->pszStringBinding);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcStringFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ Status = RpcBindingFree(&pLocalHandle->llsrpc_handle);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcBindingFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ MIDL_user_free(pLocalHandle);
+ return NtStatus;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsFreeMemory(
+ PVOID BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ MIDL_user_free( BufPtr );
+ return STATUS_SUCCESS;
+} // LlsFreeMemory
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLicenseEnumW(
+ pLocalHandle->Handle,
+ (PLLS_LICENSE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLicenseEnumA(
+ pLocalHandle->Handle,
+ (PLLS_LICENSE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+
+} // LlsLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLicenseAddW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLicenseAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLicenseAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLicenseAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLicenseAddA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLicenseAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductEnumW(
+ pLocalHandle->Handle,
+ (PLLS_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+
+} // LlsProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductEnumA(
+ pLocalHandle->Handle,
+ (PLLS_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsProductAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN LPWSTR ProductFamily,
+ IN LPWSTR Product,
+ IN LPWSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductAddW(pLocalHandle->Handle, ProductFamily, Product, Version);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsProductAddA(
+ IN LLS_REPL_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductAddA(pLocalHandle->Handle, ProductFamily, Product, Version);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductUserEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductUserEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductUserEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductServerEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductServerEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductServerEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductServerEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductServerEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicenseEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductLicenseEnumW(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_LICENSE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicenseEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrProductLicenseEnumA(
+ pLocalHandle->Handle,
+ Product,
+ (PLLS_PRODUCT_LICENSE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsProductLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserEnumW(
+ pLocalHandle->Handle,
+ (PLLS_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserEnumA(
+ pLocalHandle->Handle,
+ (PLLS_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrUserInfoGetW(pLocalHandle->Handle, User, Level, (PLLS_USER_INFOW *) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrUserInfoGetA(pLocalHandle->Handle, User, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserInfoSetW(pLocalHandle->Handle, User, Level, (PLLS_USER_INFOW) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserInfoSetA(pLocalHandle->Handle, User, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserDeleteW(pLocalHandle->Handle, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserDeleteA(pLocalHandle->Handle, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR User,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserProductEnumW(
+ pLocalHandle->Handle,
+ User,
+ (PLLS_USER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrUserProductEnumA(
+ pLocalHandle->Handle,
+ User,
+ (PLLS_USER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsUserProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserProductDeleteW(pLocalHandle->Handle, User, Product);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserProductDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsUserProductDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsUserProductDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrUserProductDeleteA(pLocalHandle->Handle, User, Product);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsUserProductDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingEnumW(
+ pLocalHandle->Handle,
+ (PLLS_MAPPING_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingEnumA(
+ pLocalHandle->Handle,
+ (PLLS_MAPPING_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrMappingInfoGetW(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrMappingInfoGetA(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingInfoSetW(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingInfoSetA(pLocalHandle->Handle, Group, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Group,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingUserEnumW(
+ pLocalHandle->Handle,
+ Group,
+ (PLLS_USER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Group,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrMappingUserEnumA(
+ pLocalHandle->Handle,
+ Group,
+ (PLLS_USER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsGroupUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserAddW(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserAddA(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserDeleteW(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupUserDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingUserDeleteA(pLocalHandle->Handle, Group, User);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingAddW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingAddA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsGroupDeleteW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingDeleteW(pLocalHandle->Handle, Group);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsGroupDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: GroupDeleteA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrMappingDeleteA(pLocalHandle->Handle, Group);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsGroupDeleteA
+
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerEnumW(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerEnumA(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerProductEnumW(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServerProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServerProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrServerProductEnumA(
+ pLocalHandle->Handle,
+ Server,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsServerProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalProductEnumW(
+ pLocalHandle->Handle,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLocalProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalProductEnumA(
+ pLocalHandle->Handle,
+ (PLLS_SERVER_PRODUCT_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+
+
+ return Status;
+} // LlsLocalProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrLocalProductInfoGetW(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrLocalProductInfoGetA(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLocalProductInfoSetW(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalProductInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrLocalProductInfoSetA(pLocalHandle->Handle, Product, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsLocalProductInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SERVICE_INFO_GETW ) )
+ {
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrServiceInfoGetW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ // the target server will blow up if we make the RPC call
+ // in 3.51, the IDL file for the returned structure was incorrect, causing
+ // the ReplicateTo and EnterpriseServer buffers at the server to be freed
+
+ // instead, get this info from the target machine's registry
+
+ PLLS_SERVICE_INFO_0W pServiceInfo;
+
+ pServiceInfo = MIDL_user_allocate( sizeof( *pServiceInfo ) );
+
+ if ( NULL == pServiceInfo )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ DWORD cbServerName = sizeof( WCHAR ) * ( 3 + MAX_COMPUTERNAME_LENGTH );
+
+ ZeroMemory( pServiceInfo, sizeof( *pServiceInfo ) );
+ pServiceInfo->Version = 5; // we know it's a 3.51 box
+ pServiceInfo->TimeStarted = 0; // don't know, but 3.51 fills in 0 anyway
+ pServiceInfo->Mode = LLS_MODE_ENTERPRISE_SERVER; // we know it's a 3.51 box
+ pServiceInfo->ReplicateTo = MIDL_user_allocate( cbServerName );
+ pServiceInfo->EnterpriseServer = MIDL_user_allocate( cbServerName );
+
+ if ( ( NULL == pServiceInfo->ReplicateTo ) || ( NULL == pServiceInfo->EnterpriseServer ) )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ HKEY hKeyLocalMachine;
+ LONG lError;
+
+ // get parameters from registry
+ lError = RegConnectRegistry( pLocalHandle->szServerName + 2, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyParameters;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseService\\Parameters" ), 0, KEY_READ, &hKeyParameters );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbData;
+
+ // these parameters all default to 0
+ // (they were initialized to 0 via ZeroMemory(), above)
+
+ cbData = sizeof( pServiceInfo->ReplicationTime );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "ReplicationTime" ), NULL, NULL, (LPBYTE) &pServiceInfo->ReplicationTime, &cbData );
+
+ cbData = sizeof( pServiceInfo->ReplicationType );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "ReplicationType" ), NULL, NULL, (LPBYTE) &pServiceInfo->ReplicationType, &cbData );
+
+ cbData = sizeof( pServiceInfo->UseEnterprise );
+ lError = RegQueryValueEx( hKeyParameters, TEXT( "UseEnterprise" ), NULL, NULL, (LPBYTE) &pServiceInfo->UseEnterprise, &cbData );
+
+ RegCloseKey( hKeyParameters );
+
+ lError = ERROR_SUCCESS;
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // parameters retrieved from registry; only remaining parameters
+ // to be filled in are EnterpriseServer and ReplicateTo
+ TCHAR szDomain[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ // retrieve the enterprise server
+ EnterpriseServerGet( pLocalHandle->szServerName, pServiceInfo->EnterpriseServer );
+
+ // derive ReplicateTo
+ Status = NTDomainGet( pLocalHandle->szServerName, szDomain );
+
+ if ( STATUS_ACCESS_DENIED != Status )
+ {
+ if ( STATUS_SUCCESS == Status )
+ {
+ NET_API_STATUS netStatus;
+ LPWSTR pszDCName;
+
+ netStatus = NetGetDCName( NULL, szDomain, (LPBYTE *) &pszDCName );
+
+ if ( NERR_Success == netStatus )
+ {
+ if ( !lstrcmpi( pszDCName, pLocalHandle->szServerName ) )
+ {
+ // server is primary domain controller;
+ // it replicates to its enterprise server (if any)
+ lstrcpy( pServiceInfo->ReplicateTo, pServiceInfo->EnterpriseServer );
+ }
+ else
+ {
+ // server is domain member; it replicates to the DC
+ lstrcpy( pServiceInfo->ReplicateTo, pszDCName );
+ }
+
+ NetApiBufferFree( pszDCName );
+ }
+ else
+ {
+ // server had domain but domain has no DC?
+ Status = STATUS_NO_SUCH_DOMAIN;
+ }
+ }
+ else
+ {
+ // server is not in a domain;
+ // it replicates to its enterprise server (if any)
+ lstrcpy( pServiceInfo->ReplicateTo, pServiceInfo->EnterpriseServer );
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ if ( NULL != pServiceInfo )
+ {
+ if ( NULL != pServiceInfo->ReplicateTo )
+ {
+ MIDL_user_free( pServiceInfo->ReplicateTo );
+ }
+ if ( NULL != pServiceInfo->EnterpriseServer )
+ {
+ MIDL_user_free( pServiceInfo->EnterpriseServer );
+ }
+
+ MIDL_user_free( pServiceInfo );
+ }
+ }
+ else
+ {
+ if ( !lstrcmpi( pLocalHandle->szServerName, pServiceInfo->ReplicateTo ) )
+ {
+ *pServiceInfo->ReplicateTo = TEXT( '\0' );
+ }
+ if ( !lstrcmpi( pLocalHandle->szServerName, pServiceInfo->EnterpriseServer ) )
+ {
+ *pServiceInfo->EnterpriseServer = TEXT( '\0' );
+ }
+
+ *bufptr = (LPBYTE) pServiceInfo;
+ }
+ }
+
+ return Status;
+} // LlsServiceInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if ((pLocalHandle == NULL) || (bufptr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ *bufptr = NULL;
+
+ try {
+ Status = LlsrServiceInfoGetA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsServiceInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrServiceInfoSetW(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ( ( STATUS_NOT_SUPPORTED == Status ) && ( 0 == Level ) )
+ {
+ // RPC API is not supported; use the registry instead
+ HKEY hKeyLocalMachine;
+ HKEY hKeyParameters;
+ LONG lError;
+ PLLS_SERVICE_INFO_0W pServiceInfo = (PLLS_SERVICE_INFO_0W) bufptr;
+ LPWSTR pszEnterpriseServer;
+
+ pszEnterpriseServer = pServiceInfo->EnterpriseServer;
+
+ // strip leading backslashes from EnterpriseServer
+ if ( !wcsncmp( pszEnterpriseServer, L"\\\\", 2 ) )
+ {
+ pszEnterpriseServer += 2;
+ }
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName + 2, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseService\\Parameters" ), 0, KEY_WRITE, &hKeyParameters );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueExW( hKeyParameters, L"EnterpriseServer", 0, REG_SZ, (LPBYTE) pszEnterpriseServer, sizeof( *pszEnterpriseServer ) * ( 1 + lstrlenW( pszEnterpriseServer ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "ReplicationTime" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->ReplicationTime, sizeof( pServiceInfo->ReplicationTime ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "ReplicationType" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->ReplicationType, sizeof( pServiceInfo->ReplicationType ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegSetValueEx( hKeyParameters, TEXT( "UseEnterprise" ), 0, REG_DWORD, (LPBYTE) &pServiceInfo->UseEnterprise, sizeof( pServiceInfo->UseEnterprise ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyParameters );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+ }
+
+ return Status;
+} // LlsServiceInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsServiceInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsServiceInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrServiceInfoSetA(pLocalHandle->Handle, Level, (PVOID) bufptr);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsServiceInfoSetA
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplConnectW(
+ LPTSTR Server,
+ LLS_REPL_HANDLE* Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+
+#ifdef API_TRACE
+ if (Server == NULL)
+ dprintf(TEXT("LLSRPC.DLL: LlsReplConnectW: <NULL>\n"));
+ else
+ dprintf(TEXT("LLSRPC.DLL: LlsReplConnectW: %s\n"), Server);
+#endif
+
+ if ((Server == NULL) || (*Server != TEXT('\\')) || (lstrlen(Server) < 3))
+ return STATUS_INVALID_PARAMETER;
+
+
+ Size = sizeof(pComputer);
+ GetComputerName(pComputer, &Size);
+
+ pszProtocolSequence = TEXT("ncacn_np");
+ pszEndpoint = TEXT(LLS_NP_ENDPOINT);
+ pszNetworkAddress = Server;
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ RtlEnterCriticalSection( &g_RpcHandleLock );
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &llsrpc_handle);
+ if(Status) {
+#if DBG
+ dprintf(TEXT("LLSRPC RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+ return I_RpcMapWin32Status(Status);
+ }
+
+ try {
+ LlsrReplConnect(Handle, pComputer);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ RtlLeaveCriticalSection( &g_RpcHandleLock );
+
+ return I_RpcMapWin32Status(Status);
+
+} // LlsReplConnectW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplClose(
+ PLLS_REPL_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+
+ try {
+ NtStatus = LlsrReplClose(Handle);
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ try {
+ Status = RpcStringFree(&pszStringBinding);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcStringFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ Status = RpcBindingFree(&llsrpc_handle);
+ if (Status ) {
+ NtStatus = I_RpcMapWin32Status(Status);
+#if DBG
+ dprintf(TEXT("LLSRPC.DLL: LlsClose - RpcBindingFree returned: 0x%lX\n"), NtStatus);
+#endif
+ }
+ }
+ except (TRUE) {
+ NtStatus = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), NtStatus);
+#endif
+ }
+
+ return NtStatus;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationRequestW(
+ IN LLS_REPL_HANDLE Handle,
+ IN DWORD Version,
+ IN OUT PREPL_REQUEST Request
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationRequestW(Handle, Version, Request);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationRequestW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServerAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVER_RECORD Servers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServerAddW(Handle, NumRecords, Servers);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServerAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServerServiceAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServerServiceAddW(Handle, NumRecords, ServerServices);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServerServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationServiceAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_SERVICE_RECORD Services
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationServiceAddW(Handle, NumRecords, Services);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsReplicationUserAddW(
+ IN LLS_REPL_HANDLE Handle,
+ IN ULONG NumRecords,
+ IN PREPL_USER_RECORD_0 Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrReplicationUserAddW(Handle, NumRecords, Users);
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationUserAddW
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecurityGetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductSecurityGetW( pLocalHandle->Handle, Product, pIsSecure );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductSecurityGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecurityGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ NOTE: Not yet implemented. Use LlsProductSecurityGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecurityGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductSecurityGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecuritySetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecuritySetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductSecuritySetW( pLocalHandle->Handle, Product );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductSecuritySetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductSecuritySetA(
+ LLS_HANDLE Handle,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+ NOTE: Not yet implemented. Use LlsProductSecuritySetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductSecuritySetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductSecuritySetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicensesGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed on the target machine for
+ use in the given mode.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPWSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Licensing mode for which to tally the licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicensesGetW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrProductLicensesGetW( pLocalHandle->Handle, Product, Mode, pQuantity );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsProductLicensesGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsProductLicensesGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed on the target machine for
+ use in the given mode.
+
+ NOTE: Not yet implemented. Use LlsProductLicensesGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Product (LPSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Licensing mode for which to tally the licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsProductLicensesGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductLicensesGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ DWORD TargetLevel,
+ LPBYTE * ppTargets,
+ LPDWORD pNumTargets )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes a license for which the certificate
+ targets are requested.
+ TargetLevel (DWORD)
+ The level of the target structure desired.
+ ppTargets (LPBYTE *)
+ On return (and if successful), holds a PLLS_EX_CERTIFICATE_CLAIM_X,
+ where X is TargetLevel. This array of structures describes the
+ location of all installations of licenses from the given certificate.
+ pNumTargets (LPDWORD)
+ On return (and if successful), holds the number of structures pointed
+ to by ppTargets.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimEnumW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = TargetLevel;
+
+ try
+ {
+ Status = LlsrCertificateClaimEnumW(
+ pLocalHandle->Handle,
+ LicenseLevel,
+ (LPVOID) pLicenseInfo,
+ (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW) &InfoStruct );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ *ppTargets = (LPBYTE) GenericInfoContainer.Buffer;
+ *pNumTargets = GenericInfoContainer.EntriesRead;
+ }
+
+ return Status;
+} // LlsCertificateClaimEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ DWORD TargetLevel,
+ LPBYTE * ppTargets,
+ LPDWORD pNumTargets )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimEnumW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes a license for which the certificate
+ targets are requested.
+ TargetLevel (DWORD)
+ The level of the target structure desired.
+ ppTargets (LPBYTE *)
+ On return (and if successful), holds a PLLS_EX_CERTIFICATE_CLAIM_X,
+ where X is TargetLevel. This array of structures describes the
+ location of all installations of licenses from the given certificate.
+ pNumTargets (LPDWORD)
+ On return (and if successful), holds the number of structures pointed
+ to by ppTargets.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license wished to add.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddCheckW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrCertificateClaimAddCheckW( pLocalHandle->Handle, LicenseLevel, (LPVOID) pLicenseInfo, pbMayInstall );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsCertificateClaimAddCheckW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddCheckW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license wished to add.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddCheckA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimAddCheckA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ServerName,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license added.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddW\n"));
+#endif
+
+ if ( !LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+ if (pLocalHandle == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ try {
+ Status = LlsrCertificateClaimAddW( pLocalHandle->Handle, ServerName, LicenseLevel, (LPVOID) pLicenseInfo );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsCertificateClaimAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddA(
+ LLS_HANDLE Handle,
+ LPSTR ServerName,
+ DWORD LicenseLevel,
+ LPBYTE pLicenseInfo )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ pLicenseInfo (LPBYTE)
+ Points to a LLS_LICENSE_INFO_X structure, where X is LicenseLevel.
+ This license structure describes the license added.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCertificateClaimAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsCertificateClaimAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationCertDbAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID Certificates )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function replicates
+ the contents of the remote certificate database.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of replication information sent.
+ Certificates (LPVOID)
+ Replicated certificate information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationCertDbAddW\n"));
+#endif
+
+ try {
+ Status = LlsrReplicationCertDbAddW( Handle, Level, Certificates );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationCertDbAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID SecureProducts )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function replicates
+ the list of products which require secure certificates.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of the product security information.
+ SecureProducts (LPVOID)
+ Replicated secure product information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationProductSecurityAddW\n"));
+#endif
+
+ try {
+ Status = LlsrReplicationProductSecurityAddW( Handle, Level, SecureProducts );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationProductSecurityAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsReplicationUserAddExW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ LPVOID Users )
+
+/*++
+
+Routine Description:
+
+ Replacement for LlsReplicationUserAddW(). (This function, unlike its
+ counterpart, supports structure levels.) This function replicates
+ the user list.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle to the target server.
+ Level (DWORD)
+ Level of the user information.
+ Users (LPVOID)
+ Replicated user information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsReplicationUserAddExW\n"));
+#endif
+
+ if ( (0 != Level) && (1 != Level) )
+ return STATUS_INVALID_LEVEL;
+
+ try {
+ Status = LlsrReplicationUserAddExW( Handle, Level, Users );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsReplicationUserAddExW
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTAPI
+LlsCapabilityIsSupported(
+ LLS_HANDLE Handle,
+ DWORD Capability )
+
+/*++
+
+Routine Description:
+
+ Determine whether the target license server supports an arbitrary
+ function.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ Capability (DWORD)
+ The capability number to check for, 0 <= Capability < LLS_CAPABILITY_MAX.
+
+Return Value:
+
+ TRUE (supports the capability) or FALSE (does not).
+
+--*/
+
+{
+ BOOL bIsSupported = FALSE;
+ PLOCAL_HANDLE pLocalHandle;
+ DWORD dwCapByte;
+ DWORD dwCapBit;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsCapabilityIsSupported\n"));
+#endif
+
+ if ( ( NULL != Handle ) && ( Capability < LLS_CAPABILITY_MAX ) )
+ {
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ dwCapByte = Capability / 8;
+ dwCapBit = Capability - 8 * dwCapByte;
+
+ if ( 1 & ( pLocalHandle->Capabilities[ dwCapByte ] >> dwCapBit ) )
+ {
+ bIsSupported = TRUE;
+ }
+ }
+
+ return bIsSupported;
+} // LlsCapabilityIsSupported
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceEnumW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try
+ {
+ Status = LlsrLocalServiceEnumW(
+ pLocalHandle->Handle,
+ (PLLS_LOCAL_SERVICE_ENUM_STRUCTW) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES))
+ {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0 pLocalServices = NULL;
+ DWORD cEntriesRead = 0;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_READ, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ const DWORD cbBufferSize = 0x4000;
+
+ // fudge; we ignore MaxPrefLen and allocate a 16k buffer
+ // this is because when we restart an enumeration, we don't know how
+ // many items we have left (we could do it, but it'd be slow)
+ // this is only for 3.51 boxes, anyway, and this buffer will hold
+ // about 500 local service entries (plenty!)
+ // this also keeps us from having to keep the registry key open
+ // across function calls
+
+ pLocalServices = MIDL_user_allocate( cbBufferSize );
+
+ if ( NULL == pLocalServices )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ DWORD iSubKey;
+ TCHAR szKeyName[ 128 ];
+
+ // read all the services installed on this machine
+ for ( iSubKey=0, cEntriesRead=0;
+ ( ERROR_SUCCESS == lError ) && ( ( cEntriesRead + 1 ) * sizeof( *pLocalServices ) < cbBufferSize );
+ iSubKey++ )
+ {
+ lError = RegEnumKey( hKeyLicenseInfo, iSubKey, szKeyName, sizeof( szKeyName ) / sizeof( *szKeyName ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, szKeyName, 0, KEY_READ, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbData;
+
+ cbData = sizeof( pLocalServices[ cEntriesRead ].Mode );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_MODE, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].Mode, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalServices[ cEntriesRead ].FlipAllow );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FLIP, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].FlipAllow, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalServices[ cEntriesRead ].ConcurrentLimit );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_LIMIT, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].ConcurrentLimit, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbKeyName;
+ DWORD cbDisplayName;
+ DWORD cbFamilyDisplayName;
+
+ cbData = sizeof( pLocalServices[ cEntriesRead ].HighMark );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_HIGHMARK, NULL, NULL, (LPBYTE) &pLocalServices[ cEntriesRead ].HighMark, &cbData );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ pLocalServices[ cEntriesRead ].HighMark = 0;
+ lError = ERROR_SUCCESS;
+ }
+
+ if ( ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, NULL, &cbDisplayName ) )
+ && ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, NULL, &cbFamilyDisplayName ) ) )
+ {
+ cbKeyName = sizeof( *szKeyName ) * ( 1 + lstrlen( szKeyName ) );
+
+ pLocalServices[ cEntriesRead ].KeyName = MIDL_user_allocate( cbKeyName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].KeyName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lstrcpy( pLocalServices[ cEntriesRead ].KeyName, szKeyName );
+
+ pLocalServices[ cEntriesRead ].DisplayName = MIDL_user_allocate( cbDisplayName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].DisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, (LPBYTE) pLocalServices[ cEntriesRead ].DisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalServices[ cEntriesRead ].FamilyDisplayName = MIDL_user_allocate( cbFamilyDisplayName );
+
+ if ( NULL == pLocalServices[ cEntriesRead ].FamilyDisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, (LPBYTE) pLocalServices[ cEntriesRead ].FamilyDisplayName, &cbFamilyDisplayName );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].FamilyDisplayName );
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].DisplayName );
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalServices[ cEntriesRead ].KeyName );
+ }
+ else
+ {
+ // all data for this service was retrieved!
+ cEntriesRead++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ if ( ERROR_OUTOFMEMORY != lError )
+ {
+ // continue enumeration...
+ lError = ERROR_SUCCESS;
+ }
+ }
+ }
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ case ERROR_NO_MORE_ITEMS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ case ERROR_OUTOFMEMORY:
+ Status = STATUS_NO_MEMORY;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ // free all of our allocations
+ DWORD i;
+
+ for ( i=0; i < cEntriesRead; i++ )
+ {
+ MIDL_user_free( pLocalServices[ i ].KeyName );
+ MIDL_user_free( pLocalServices[ i ].DisplayName );
+ MIDL_user_free( pLocalServices[ i ].FamilyDisplayName );
+ }
+
+ MIDL_user_free( pLocalServices );
+ }
+ else
+ {
+ // success! return the array of services.
+ *bufptr = (LPBYTE) pLocalServices;
+ *EntriesRead = cEntriesRead;
+ *TotalEntries = cEntriesRead;
+ *ResumeHandle = 0;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceEnumA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ GENERIC_INFO_CONTAINER GenericInfoContainer;
+ GENERIC_ENUM_STRUCT InfoStruct;
+
+ GenericInfoContainer.Buffer = NULL;
+ GenericInfoContainer.EntriesRead = 0;
+
+ InfoStruct.Container = &GenericInfoContainer;
+ InfoStruct.Level = Level;
+
+ try {
+ Status = LlsrLocalServiceEnumA(
+ pLocalHandle->Handle,
+ (PLLS_LOCAL_SERVICE_ENUM_STRUCTA) &InfoStruct,
+ PrefMaxLen,
+ TotalEntries,
+ ResumeHandle
+ );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ if ((Status == STATUS_SUCCESS) || (Status == STATUS_MORE_ENTRIES)) {
+ *bufptr = (LPBYTE) GenericInfoContainer.Buffer;
+ *EntriesRead = GenericInfoContainer.EntriesRead;
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceAddW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceAddW( pLocalHandle->Handle, Level, (PLLS_LOCAL_SERVICE_INFOW) bufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->KeyName )
+ || ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->DisplayName )
+ || ( NULL == ((PLLS_LOCAL_SERVICE_INFO_0W) bufptr)->FamilyDisplayName ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ LocalServiceInfo = (PLLS_LOCAL_SERVICE_INFO_0W) bufptr;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, TEXT( "System\\CurrentControlSet\\Services\\LicenseInfo" ), 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+ DWORD dwDisposition;
+
+ // create key
+ lError = RegCreateKeyEx( hKeyLicenseInfo, LocalServiceInfo->KeyName, 0, NULL, 0, KEY_WRITE, NULL, &hKeyService, &dwDisposition );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set DisplayName
+ lError = RegSetValueEx( hKeyService, TEXT( "DisplayName" ), 0, REG_SZ, (LPBYTE) LocalServiceInfo->DisplayName, sizeof( *LocalServiceInfo->DisplayName ) * ( 1 + lstrlen( LocalServiceInfo->DisplayName ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FamilyDisplayName
+ lError = RegSetValueEx( hKeyService, TEXT( "FamilyDisplayName" ), 0, REG_SZ, (LPBYTE) LocalServiceInfo->FamilyDisplayName, sizeof( *LocalServiceInfo->FamilyDisplayName ) * ( 1 + lstrlen( LocalServiceInfo->FamilyDisplayName ) ) );
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // set remaining items
+ Status = LlsLocalServiceInfoSetW( Handle, LocalServiceInfo->KeyName, Level, bufptr );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceAddA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceAddA( pLocalHandle->Handle, Level, (PLLS_LOCAL_SERVICE_INFOA) bufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoSetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceInfoSetW( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOW) bufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+ PLLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo;
+
+ LocalServiceInfo = (PLLS_LOCAL_SERVICE_INFO_0W) bufptr;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_WRITE, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set Mode
+ lError = RegSetValueEx( hKeyService, REG_VALUE_MODE, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->Mode, sizeof( LocalServiceInfo->Mode ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FlipAllow
+ lError = RegSetValueEx( hKeyService, REG_VALUE_FLIP, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->FlipAllow, sizeof( LocalServiceInfo->FlipAllow ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set ConcurrentLimit
+ lError = RegSetValueEx( hKeyService, REG_VALUE_LIMIT, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->ConcurrentLimit, sizeof( LocalServiceInfo->ConcurrentLimit ) );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoSetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceInfoSetA( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOA) bufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE * pbufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoGetW\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try
+ {
+ Status = LlsrLocalServiceInfoGetW( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOW *) pbufptr );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLLS_LOCAL_SERVICE_INFO_0W pLocalService = NULL;
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pLocalHandle->szServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyLicenseInfo;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine, REG_KEY_LICENSE, 0, KEY_READ, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_READ, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalService = MIDL_user_allocate( sizeof( *pLocalService ) );
+
+ if ( NULL == pLocalService )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ DWORD cbData;
+
+ cbData = sizeof( pLocalService->Mode );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_MODE, NULL, NULL, (LPBYTE) &pLocalService->Mode, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalService->FlipAllow );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FLIP, NULL, NULL, (LPBYTE) &pLocalService->FlipAllow, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ cbData = sizeof( pLocalService->ConcurrentLimit );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_LIMIT, NULL, NULL, (LPBYTE) &pLocalService->ConcurrentLimit, &cbData );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ DWORD cbKeyName;
+ DWORD cbDisplayName;
+ DWORD cbFamilyDisplayName;
+
+ cbData = sizeof( pLocalService->HighMark );
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_HIGHMARK, NULL, NULL, (LPBYTE) &pLocalService->HighMark, &cbData );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ pLocalService->HighMark = 0;
+ lError = ERROR_SUCCESS;
+ }
+
+ if ( ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, NULL, &cbDisplayName ) )
+ && ( ERROR_SUCCESS == RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, NULL, &cbFamilyDisplayName ) ) )
+ {
+ cbKeyName = sizeof( *KeyName ) * ( 1 + lstrlen( KeyName ) );
+
+ pLocalService->KeyName = MIDL_user_allocate( cbKeyName );
+
+ if ( NULL == pLocalService->KeyName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lstrcpy( pLocalService->KeyName, KeyName );
+
+ pLocalService->DisplayName = MIDL_user_allocate( cbDisplayName );
+
+ if ( NULL == pLocalService->DisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_NAME, NULL, NULL, (LPBYTE) pLocalService->DisplayName, &cbDisplayName );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ pLocalService->FamilyDisplayName = MIDL_user_allocate( cbFamilyDisplayName );
+
+ if ( NULL == pLocalService->FamilyDisplayName )
+ {
+ lError = ERROR_OUTOFMEMORY;
+ }
+ else
+ {
+ lError = RegQueryValueEx( hKeyService, REG_VALUE_FAMILY, NULL, NULL, (LPBYTE) pLocalService->FamilyDisplayName, &cbFamilyDisplayName );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->FamilyDisplayName );
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->DisplayName );
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService->KeyName );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( ERROR_SUCCESS != lError )
+ {
+ MIDL_user_free( pLocalService );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ case ERROR_ACCESS_DENIED:
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ *pbufptr = (LPBYTE) pLocalService;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPSTR KeyName,
+ LPBYTE * pbufptr )
+{
+ NTSTATUS Status;
+ PLOCAL_HANDLE pLocalHandle;
+
+#ifdef API_TRACE
+ dprintf(TEXT("LLSRPC.DLL: LlsLocalServiceInfoGetA\n"));
+#endif
+
+ pLocalHandle = (PLOCAL_HANDLE) Handle;
+
+ if (pLocalHandle == NULL)
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else if ( LlsCapabilityIsSupported( Handle, LLS_CAPABILITY_LOCAL_SERVICE_API ) )
+ {
+ try {
+ Status = LlsrLocalServiceInfoGetA( pLocalHandle->Handle, KeyName, Level, (PLLS_LOCAL_SERVICE_INFOA *) pbufptr );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSRPC.DLL: RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+ }
+ else
+ {
+ Status = STATUS_NOT_SUPPORTED;
+ }
+
+ return Status;
+}
+
+
diff --git a/private/net/svcdlls/lls/client/llsrpc.def b/private/net/svcdlls/lls/client/llsrpc.def
new file mode 100644
index 000000000..2e4228478
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.def
@@ -0,0 +1,106 @@
+LIBRARY LLSRPC
+
+DESCRIPTION 'Licence Logging Service RPC API'
+
+CODE LOADONCALL MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+
+HEAPSIZE 1024
+
+EXPORTS
+ LlsConnectW
+ LlsConnectA
+ LlsConnectEnterpriseW
+ LlsConnectEnterpriseA
+ LlsEnterpriseServerFindW
+ LlsEnterpriseServerFindA
+ LlsClose
+ LlsFreeMemory
+ LlsLicenseEnumW
+ LlsLicenseEnumA
+ LlsLicenseAddW
+ LlsLicenseAddA
+ LlsProductEnumW
+ LlsProductEnumA
+ LlsProductAddW
+ LlsProductAddA
+ LlsProductUserEnumW
+ LlsProductUserEnumA
+ LlsProductServerEnumW
+ LlsProductServerEnumA
+ LlsProductLicenseEnumW
+ LlsProductLicenseEnumA
+ LlsUserEnumW
+ LlsUserEnumA
+ LlsUserInfoGetW
+ LlsUserInfoGetA
+ LlsUserInfoSetW
+ LlsUserInfoSetA
+ LlsUserDeleteW
+ LlsUserDeleteA
+ LlsUserProductEnumW
+ LlsUserProductEnumA
+ LlsUserProductDeleteW
+ LlsUserProductDeleteA
+ LlsGroupEnumW
+ LlsGroupEnumA
+ LlsGroupInfoGetW
+ LlsGroupInfoGetA
+ LlsGroupInfoSetW
+ LlsGroupInfoSetA
+ LlsGroupUserEnumW
+ LlsGroupUserEnumA
+ LlsGroupUserAddW
+ LlsGroupUserAddA
+ LlsGroupUserDeleteW
+ LlsGroupUserDeleteA
+ LlsGroupAddW
+ LlsGroupAddA
+ LlsGroupDeleteW
+ LlsGroupDeleteA
+ LlsServerEnumW
+ LlsServerEnumA
+ LlsServerProductEnumW
+ LlsServerProductEnumA
+ LlsLocalProductEnumW
+ LlsLocalProductEnumA
+ LlsLocalProductInfoGetW
+ LlsLocalProductInfoGetA
+ LlsLocalProductInfoSetW
+ LlsLocalProductInfoSetA
+ LlsServiceInfoGetW
+ LlsServiceInfoGetA
+ LlsServiceInfoSetW
+ LlsServiceInfoSetA
+ LlsReplConnectW
+ LlsReplClose
+ LlsReplicationRequestW
+ LlsReplicationServerAddW
+ LlsReplicationServerServiceAddW
+ LlsReplicationServiceAddW
+ LlsReplicationUserAddW
+
+ LlsProductSecurityGetW
+ LlsProductSecurityGetA
+ LlsProductSecuritySetW
+ LlsProductSecuritySetA
+ LlsProductLicensesGetW
+ LlsProductLicensesGetA
+ LlsCertificateClaimEnumA
+ LlsCertificateClaimEnumW
+ LlsCertificateClaimAddCheckA
+ LlsCertificateClaimAddCheckW
+ LlsCertificateClaimAddA
+ LlsCertificateClaimAddW
+ LlsReplicationCertDbAddW
+ LlsReplicationProductSecurityAddW
+ LlsReplicationUserAddExW
+ LlsCapabilityIsSupported
+ LlsLocalServiceEnumW
+ LlsLocalServiceEnumA
+ LlsLocalServiceAddW
+ LlsLocalServiceAddA
+ LlsLocalServiceInfoSetW
+ LlsLocalServiceInfoSetA
+ LlsLocalServiceInfoGetW
+ LlsLocalServiceInfoGetA
diff --git a/private/net/svcdlls/lls/client/llsrpc.rc b/private/net/svcdlls/lls/client/llsrpc.rc
new file mode 100644
index 000000000..d33c24461
--- /dev/null
+++ b/private/net/svcdlls/lls/client/llsrpc.rc
@@ -0,0 +1,30 @@
+//=============================================================================
+// Microsoft (R) License Logging Service (tm). Copyright (C) 1991-1995.
+//
+// MODULE: llsrpc.rc
+//
+// Modification History
+//
+// arth Mar, 10 1995 Created
+//=============================================================================
+
+#include "windows.h"
+
+
+// include NT version resources
+#define VER_LEGALCOPYRIGHT_YEARS "1981-1995"
+#include "ntver.rc"
+
+#define IDS_BACKOFFICE 1500
+
+
+STRINGTABLE DISCARDABLE
+{
+ IDS_BACKOFFICE "Microsoft BackOffice"
+}
+
+
+// Event-logging support
+
+LANGUAGE 0x9,0x1
+1 11 MSG00001.bin
diff --git a/private/net/svcdlls/lls/client/makefile b/private/net/svcdlls/lls/client/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/client/makefile.inc b/private/net/svcdlls/lls/client/makefile.inc
new file mode 100644
index 000000000..0f9608a94
--- /dev/null
+++ b/private/net/svcdlls/lls/client/makefile.inc
@@ -0,0 +1,8 @@
+
+!IFNDEF MC
+MC=mc
+!ENDIF
+
+llsevent.h msg00001.bin llsevent.rc : llsevent.mc
+ $(MC) llsevent.mc
+ IF EXIST llsevent.h copy llsevent.h ..\inc
diff --git a/private/net/svcdlls/lls/client/ntver.rc b/private/net/svcdlls/lls/client/ntver.rc
new file mode 100644
index 000000000..913d1e4d3
--- /dev/null
+++ b/private/net/svcdlls/lls/client/ntver.rc
@@ -0,0 +1,53 @@
+/*
+** Template for version resources. Place this in your .rc file,
+** editing the values for VER_FILETYPE, VER_FILESUBTYPE,
+** VER_FILEDESCRIPTION_STR and VER_INTERNALNAME_STR as needed.
+** See winver.h for possible values.
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+/* #include <windows.h> needed if this will be the .rc file */
+
+#define VER_LEGALCOPYRIGHT_YEARS "1981-1995"
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_DLL
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "License Logging Service RPC Interface"
+#define VER_INTERNALNAME_STR "LLSRPC.DLL"
+#define VER_ORIGINALFILENAME_STR "LLSRPC.DLL"
+
+#include "common.ver"
diff --git a/private/net/svcdlls/lls/client/sources b/private/net/svcdlls/lls/client/sources
new file mode 100644
index 000000000..775fd58df
--- /dev/null
+++ b/private/net/svcdlls/lls/client/sources
@@ -0,0 +1,68 @@
+!IF 0
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=client
+
+TARGETNAME=llsrpc
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+#DLLENTRY=DllEntryPoint
+DLLENTRY=DllMain
+
+USE_CRTDLL=1
+
+#DLLBASE=0x7F000000
+SDKINC=$(BASEDIR)\public\sdk\inc
+PRIVINC=$(BASEDIR)\private\inc
+
+TARGETLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+INCLUDES=$(PRIVINC);$(SDKINC);..\inc
+
+SOURCES= \
+ llsrpc_c.c \
+ llsrpc.c \
+ llsrpc.rc
+
+C_DEFINES=-DINCL_32 -DNT -DWIN32 -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+UMTYPE=windows
+UMLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+NTTARGETFILE0=llsevent.h
diff --git a/private/net/svcdlls/lls/common/debug.c b/private/net/svcdlls/lls/common/debug.c
new file mode 100644
index 000000000..c524bffde
--- /dev/null
+++ b/private/net/svcdlls/lls/common/debug.c
@@ -0,0 +1,19 @@
+
+#include <windows.h>
+#include <windowsx.h>
+#include <io.h>
+#include <malloc.h>
+#include <string.h>
+
+#if DBG
+void __cdecl dprintf(LPTSTR szFormat, ...) {
+ static TCHAR tmpStr[1024];
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(tmpStr, szFormat, marker);
+ OutputDebugString(tmpStr);
+ va_end(marker);
+
+} // dprintf
+#endif
diff --git a/private/net/svcdlls/lls/common/makefile b/private/net/svcdlls/lls/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/common/sources b/private/net/svcdlls/lls/common/sources
new file mode 100644
index 000000000..edb3e0412
--- /dev/null
+++ b/private/net/svcdlls/lls/common/sources
@@ -0,0 +1,44 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=common
+
+TARGETNAME=llscomm
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+USE_CRTDLL=1
+
+INCLUDES=..\inc;$(_NTROOT)\public\sdk\inc;$(_NTROOT)\private\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+
+
+
+SOURCES= \
+ debug.c
+
+UMTYPE=windows
diff --git a/private/net/svcdlls/lls/dirs b/private/net/svcdlls/lls/dirs
new file mode 100644
index 000000000..f4ee31a11
--- /dev/null
+++ b/private/net/svcdlls/lls/dirs
@@ -0,0 +1 @@
+DIRS=common client server ccfapi32 ntlsapi test
diff --git a/private/net/svcdlls/lls/inc/debug.h b/private/net/svcdlls/lls/inc/debug.h
new file mode 100644
index 000000000..fa67d7307
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/debug.h
@@ -0,0 +1,40 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TRACE_FUNCTION_TRACE 0x0001
+#define TRACE_WARNINGS 0x0002
+#define TRACE_PACK 0x0004
+#define TRACE_LICENSE_REQUEST 0x0008
+#define TRACE_LICENSE_FREE 0x0010
+#define TRACE_REGISTRY 0x0020
+#define TRACE_REPLICATION 0x0040
+#define TRACE_LPC 0x0080
+#define TRACE_RPC 0x0100
+#define TRACE_INIT 0x0200
+#define TRACE_DATABASE 0x0400
+
+#define SERVICE_TABLE_NUM 1
+#define USER_TABLE_NUM 2
+#define SID_TABLE_NUM 3
+#define LICENSE_TABLE_NUM 4
+#define ADD_CACHE_TABLE_NUM 5
+#define MASTER_SERVICE_TABLE_NUM 6
+#define SERVICE_FAMILY_TABLE_NUM 7
+#define MAPPING_TABLE_NUM 8
+#define SERVER_TABLE_NUM 9
+#define SECURE_PRODUCT_TABLE_NUM 10
+#define CERTIFICATE_TABLE_NUM 11
+
+#if DBG
+void __cdecl dprintf(LPTSTR szFormat, ...);
+
+extern DWORD TraceFlags;
+
+#else
+#define dprintf
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/private/net/svcdlls/lls/inc/llsapi.h b/private/net/svcdlls/lls/inc/llsapi.h
new file mode 100644
index 000000000..b5ed01125
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsapi.h
@@ -0,0 +1,1441 @@
+/*++
+
+Copyright (c) 1994-95 Microsoft Corporation
+
+Module Name:
+
+ llsapi.h
+
+Abstract:
+
+ License logging server's RPC API's.
+
+Author:
+
+ Arthur Hanson (arth) 21-Mar-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ Jeff Parham (jeffparh) 04-Dec-1995
+ o Added type definitions, macros, and prototypes for extended RPC APIs
+ and license certificate APIs (available only post-3.51).
+ o Corrected prototypes for LlsServerEnumW(), LlsServerEnumA(),
+ LlsLocalProductInfoGetW(), and LlsLocalProductInfoGetA().
+
+--*/
+
+#ifndef _LLSAPI_H
+#define _LLSAPI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define LLS_FLAG_LICENSED 0x0001
+#define LLS_FLAG_UPDATE 0x0002
+#define LLS_FLAG_SUITE_USE 0x0004
+#define LLS_FLAG_SUITE_AUTO 0x0008
+
+#define LLS_FLAG_PRODUCT_PERSEAT 0x0010
+#define LLS_FLAG_PRODUCT_SWITCH 0x0020
+
+#define LLS_FLAG_DELETED 0x1000
+
+
+typedef PVOID LLS_HANDLE, *PLLS_HANDLE;
+typedef PVOID LLS_REPL_HANDLE, *PLLS_REPL_HANDLE;
+
+#define LLS_NUM_SECRETS ( 4 )
+
+typedef struct _LLS_LICENSE_INFO_0 {
+ LPTSTR Product;
+ LONG Quantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} LLS_LICENSE_INFO_0, *PLLS_LICENSE_INFO_0;
+
+typedef struct _LLS_LICENSE_INFO_1 {
+ LPTSTR Product;
+ LPTSTR Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} LLS_LICENSE_INFO_1, *PLLS_LICENSE_INFO_1;
+
+typedef struct _LLS_PRODUCT_INFO_0 {
+ LPTSTR Product;
+} LLS_PRODUCT_INFO_0, *PLLS_PRODUCT_INFO_0;
+
+typedef struct _LLS_PRODUCT_INFO_1 {
+ LPTSTR Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG ConcurrentTotal;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1, *PLLS_PRODUCT_INFO_1;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0 {
+ LPTSTR User;
+} LLS_PRODUCT_USER_INFO_0, *PLLS_PRODUCT_USER_INFO_0;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1 {
+ LPTSTR User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1, *PLLS_PRODUCT_USER_INFO_1;
+
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0 {
+ LONG Quantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} LLS_PRODUCT_LICENSE_INFO_0, *PLLS_PRODUCT_LICENSE_INFO_0;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1 {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} LLS_PRODUCT_LICENSE_INFO_1, *PLLS_PRODUCT_LICENSE_INFO_1;
+
+typedef struct _LLS_USER_INFO_0 {
+ LPTSTR Name;
+} LLS_USER_INFO_0, *PLLS_USER_INFO_0;
+
+typedef struct _LLS_USER_INFO_1 {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Group;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1, *PLLS_USER_INFO_1;
+
+typedef struct _LLS_USER_INFO_2 {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Group;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ LPTSTR Products;
+} LLS_USER_INFO_2, *PLLS_USER_INFO_2;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0 {
+ LPTSTR Product;
+} LLS_USER_PRODUCT_INFO_0, *PLLS_USER_PRODUCT_INFO_0;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1 {
+ LPTSTR Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1, *PLLS_USER_PRODUCT_INFO_1;
+
+typedef struct _LLS_GROUP_INFO_0 {
+ LPTSTR Name;
+} LLS_GROUP_INFO_0, *PLLS_GROUP_INFO_0;
+
+typedef struct _LLS_GROUP_INFO_1 {
+ LPTSTR Name;
+ LPTSTR Comment;
+ ULONG Licenses;
+} LLS_GROUP_INFO_1, *PLLS_GROUP_INFO_1;
+
+
+#define LLS_REPLICATION_TYPE_DELTA 0
+#define LLS_REPLICATION_TYPE_TIME 1
+
+#define LLS_MODE_LICENSE_SERVER 0
+#define LLS_MODE_PDC 1
+#define LLS_MODE_ENTERPRISE_SERVER 2
+
+typedef struct _LLS_SERVICE_INFO_0 {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ LPTSTR ReplicateTo;
+ LPTSTR EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0, *PLLS_SERVICE_INFO_0;
+
+typedef struct _LLS_CONNECT_INFO_0 {
+ LPTSTR Domain;
+ LPTSTR EnterpriseServer;
+} LLS_CONNECT_INFO_0, *PLLS_CONNECT_INFO_0;
+
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0 {
+ LPTSTR Name;
+} LLS_SERVER_PRODUCT_INFO_0, *PLLS_SERVER_PRODUCT_INFO_0;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1 {
+ LPTSTR Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1, *PLLS_SERVER_PRODUCT_INFO_1;
+
+
+typedef struct _LLS_SERVER_INFO_0 {
+ LPTSTR Name;
+} LLS_SERVER_INFO_0, *PLLS_SERVER_INFO_0;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0
+{
+ TCHAR ServerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0, *PLLS_CERTIFICATE_CLAIM_INFO_0;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0
+{
+ LPTSTR KeyName;
+ LPTSTR DisplayName;
+ LPTSTR FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0, *PLLS_LOCAL_SERVICE_INFO_0;
+
+#define LLS_LICENSE_MODE_PER_SEAT ( 0 )
+#define LLS_LICENSE_MODE_PER_SERVER ( 1 )
+
+#define LLS_LICENSE_MODE_ALLOW_PER_SEAT ( 1 )
+#define LLS_LICENSE_MODE_ALLOW_PER_SERVER ( 2 )
+
+#define LLS_LICENSE_FLIP_ALLOW_PER_SEAT ( 1 )
+#define LLS_LICENSE_FLIP_ALLOW_PER_SERVER ( 2 )
+
+
+// capability flags; query with LlsCapabilityIsSupported
+#define LLS_CAPABILITY_SECURE_CERTIFICATES ( 0 )
+#define LLS_CAPABILITY_REPLICATE_CERT_DB ( 1 )
+#define LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ( 2 )
+#define LLS_CAPABILITY_REPLICATE_USERS_EX ( 3 )
+#define LLS_CAPABILITY_SERVICE_INFO_GETW ( 4 )
+#define LLS_CAPABILITY_LOCAL_SERVICE_API ( 5 )
+#define LLS_CAPABILITY_MAX ( 32 )
+
+
+#ifndef NO_LLS_APIS
+//
+// Connection control API's
+//
+
+NTSTATUS
+NTAPI
+LlsConnectW(
+ IN LPWSTR Server,
+ OUT PLLS_HANDLE Handle
+ );
+
+NTSTATUS
+NTAPI
+LlsConnectA(
+ IN LPSTR Server,
+ OUT PLLS_HANDLE Handle
+ );
+#ifdef UNICODE
+# define LlsConnect LlsConnectW
+#else
+# define LlsConnect LlsConnectA
+#endif
+
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_W)( LPWSTR, PLLS_HANDLE );
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_A)( LPSTR, PLLS_HANDLE );
+
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseW(
+ IN LPWSTR Focus,
+ OUT PLLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsConnectEnterpriseA(
+ IN LPSTR Focus,
+ OUT PLLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+#ifdef UNICODE
+#define LlsConnectEnterprise LlsConnectEnterpriseW
+#else
+#define LlsConnectEnterprise LlsConnectEnterpriseA
+#endif
+
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_ENTERPRISE_W)( LPWSTR, PLLS_HANDLE, DWORD, LPBYTE * );
+typedef NTSTATUS (NTAPI *PLLS_CONNECT_ENTERPRISE_A)( LPSTR, PLLS_HANDLE, DWORD, LPBYTE * );
+
+NTSTATUS
+NTAPI
+LlsClose(
+ IN LLS_HANDLE Handle
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_CLOSE)( LLS_HANDLE );
+
+NTSTATUS
+NTAPI
+LlsFreeMemory(
+ IN PVOID bufptr
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_FREE_MEMORY)( PVOID );
+
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindW(
+ IN LPWSTR Focus,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsEnterpriseServerFindA(
+ IN LPSTR Focus,
+ IN DWORD Level,
+ OUT LPBYTE *bufptr
+ );
+#ifdef UNICODE
+#define LlsEnterpriseServerFind LlsEnterpriseServerFindW
+#else
+#define LlsEnterpriseServerFind LlsEnterpriseServerFindA
+#endif
+
+//
+// License control API's
+//
+
+// Enum purchase history of licenses for all products.
+NTSTATUS
+NTAPI
+LlsLicenseEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsLicenseEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsLicenseEnum LlsLicenseEnumW
+#else
+#define LlsLicenseEnum LlsLicenseEnumA
+#endif
+
+// Add purchase of license for a product.
+NTSTATUS
+NTAPI
+LlsLicenseAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLicenseAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 0 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsLicenseAdd LlsLicenseAddW
+#else
+#define LlsLicenseAdd LlsLicenseAddA
+#endif
+
+//
+// Product control API's
+//
+// Product is SQL, BackOffice, Exchange, Etc. (Even though BackOffice isn't
+// a product - we count it like one to keep things simplistic.
+//
+
+// Enum all products with purchase and InUse info.
+NTSTATUS
+NTAPI
+LlsProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductEnum LlsProductEnumW
+#else
+#define LlsProductEnum LlsProductEnumA
+#endif
+
+// Add purchase of license for a product.
+NTSTATUS
+NTAPI
+LlsProductAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR ProductFamily,
+ IN LPWSTR Product,
+ IN LPWSTR Version
+ );
+
+NTSTATUS
+NTAPI
+LlsProductAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ );
+#ifdef UNICODE
+#define LlsProductAdd LlsProductAddW
+#else
+#define LlsProductAdd LlsProductAddA
+#endif
+
+// For a particular product enum all users.
+NTSTATUS
+NTAPI
+LlsProductUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductUserEnum LlsProductUserEnumW
+#else
+#define LlsProductUserEnum LlsProductUserEnumA
+#endif
+
+// For a particular product enum all license purchases.
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductLicenseEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Level 0 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductLicenseEnum LlsProductLicenseEnumW
+#else
+#define LlsProductLicenseEnum LlsProductLicenseEnumA
+#endif
+
+
+// For given product enum all servers with concurrent limits
+NTSTATUS
+NTAPI
+LlsProductServerEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsProductServerEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsProductServerEnum LlsProductServerEnumW
+#else
+#define LlsProductServerEnum LlsProductServerEnumA
+#endif
+//
+// User control API's
+// A user can be a mapped user or a normal user
+//
+
+// Enums all users
+NTSTATUS
+NTAPI
+LlsUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsUserEnum LlsUserEnumW
+#else
+#define LlsUserEnum LlsUserEnumA
+#endif
+
+// Info is Group and whether to force back-office license
+NTSTATUS
+NTAPI
+LlsUserInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsUserInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsUserInfoGet LlsUserInfoGetW
+#else
+#define LlsUserInfoGet LlsUserInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsUserInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ );
+
+NTSTATUS
+NTAPI
+LlsUserInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level,
+ IN LPBYTE bufptr // Level 1 supported
+ );
+#ifdef UNICODE
+#define LlsUserInfoSet LlsUserInfoSetW
+#else
+#define LlsUserInfoSet LlsUserInfoSetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsUserDelete LlsUserDeleteW
+#else
+#define LlsUserDelete LlsUserDeleteA
+#endif
+
+// For a given user enums all license useages
+NTSTATUS
+NTAPI
+LlsUserProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsUserProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsUserProductEnum LlsUserProductEnumW
+#else
+#define LlsUserProductEnum LlsUserProductEnumA
+#endif
+
+// For a given user deletes a license useage
+NTSTATUS
+NTAPI
+LlsUserProductDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR User,
+ IN LPWSTR Product
+ );
+
+NTSTATUS
+NTAPI
+LlsUserProductDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR User,
+ IN LPSTR Product
+ );
+#ifdef UNICODE
+#define LlsUserProductDelete LlsUserProductDeleteW
+#else
+#define LlsUserProductDelete LlsUserProductDeleteA
+#endif
+
+//
+// Group control API's
+//
+
+// Enums all user Groups
+NTSTATUS
+NTAPI
+LlsGroupEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsGroupEnum LlsGroupEnumW
+#else
+#define LlsGroupEnum LlsGroupEnumA
+#endif
+
+// For given Group gets info, info is name, comment and # licenses used
+NTSTATUS
+NTAPI
+LlsGroupInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Level 1 supported
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupInfoGet LlsGroupInfoGetW
+#else
+#define LlsGroupInfoGet LlsGroupInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsGroupInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupInfoSet LlsGroupInfoSetW
+#else
+#define LlsGroupInfoSet LlsGroupInfoSetA
+#endif
+
+// For given Group enum all users
+NTSTATUS
+NTAPI
+LlsGroupUserEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsGroupUserEnum LlsGroupUserEnumW
+#else
+#define LlsGroupUserEnum LlsGroupUserEnumA
+#endif
+
+// Add user to given Group
+NTSTATUS
+NTAPI
+LlsGroupUserAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsGroupUserAdd LlsGroupUserAddW
+#else
+#define LlsGroupUserAdd LlsGroupUserAddA
+#endif
+
+// Delete user from given Group
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group,
+ IN LPWSTR User
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupUserDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group,
+ IN LPSTR User
+ );
+#ifdef UNICODE
+#define LlsGroupUserDelete LlsGroupUserDeleteW
+#else
+#define LlsGroupUserDelete LlsGroupUserDeleteA
+#endif
+
+// Add a given Group
+NTSTATUS
+NTAPI
+LlsGroupAddW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupAddA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Level 1 supported
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsGroupAdd LlsGroupAddW
+#else
+#define LlsGroupAdd LlsGroupAddA
+#endif
+
+NTSTATUS
+NTAPI
+LlsGroupDeleteW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Group
+ );
+
+NTSTATUS
+NTAPI
+LlsGroupDeleteA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Group
+ );
+#ifdef UNICODE
+#define LlsGroupDelete LlsGroupDeleteW
+#else
+#define LlsGroupDelete LlsGroupDeleteA
+#endif
+
+
+//
+// Service control API's
+//
+
+NTSTATUS
+NTAPI
+LlsServiceInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsServiceInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsServiceInfoGet LlsServiceInfoGetW
+#else
+#define LlsServiceInfoGet LlsServiceInfoGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsServiceInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsServiceInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsServiceInfoSet LlsServiceInfoSetW
+#else
+#define LlsServiceInfoSet LlsServiceInfoSetA
+#endif
+
+
+//
+// Server Table Stuff (Replicated Server / Product Tree)
+//
+NTSTATUS
+NTAPI
+LlsServerEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsServerEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsServerEnum LlsServerEnumW
+#else
+#define LlsServerEnum LlsServerEnumA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsServerProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsServerProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Server,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsServerUserEnum LlsServerUserEnumW
+#else
+#define LlsServerUserEnum LlsServerUserEnumA
+#endif
+
+
+//
+// Concurrent (Per-Server) mode API's (these will interact with the registry
+// on the remote system).
+//
+NTSTATUS
+NTAPI
+LlsLocalProductEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD Level, // Levels 0,1 supported
+ OUT LPBYTE* bufptr,
+ IN DWORD prefmaxlen,
+ OUT LPDWORD EntriesRead,
+ OUT LPDWORD TotalEntries,
+ IN OUT LPDWORD ResumeHandle
+ );
+#ifdef UNICODE
+#define LlsLocalProductEnum LlsLocalProductEnumW
+#else
+#define LlsLocalProductEnum LlsLocalProductEnumA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ OUT LPBYTE* bufptr
+ );
+#ifdef UNICODE
+#define LlsLocalProductInfoGet LlsLocalProductInfoGetW
+#else
+#define LlsLocalProductInfoGet LlsLocalProductInfoGetA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+
+NTSTATUS
+NTAPI
+LlsLocalProductInfoSetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ IN DWORD Level,
+ IN LPBYTE bufptr
+ );
+#ifdef UNICODE
+#define LlsLocalProductInfoSet LlsLocalProductInfoSetW
+#else
+#define LlsLocalProductInfoSet LlsLocalProductInfoSetA
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// LLS EXTENDED API //
+////////////////////////
+
+BOOL
+NTAPI
+LlsCapabilityIsSupported(
+ LLS_HANDLE Handle,
+ DWORD Capability );
+
+typedef BOOL (NTAPI *PLLS_CAPABILITY_IS_SUPPORTED)( LLS_HANDLE, DWORD );
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product,
+ OUT LPBOOL pSecurity
+ );
+
+NTSTATUS
+NTAPI
+LlsProductSecurityGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product,
+ OUT LPBOOL pSecurity
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_GET_W)( LLS_HANDLE, LPWSTR, LPBOOL );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_GET_A)( LLS_HANDLE, LPSTR, LPBOOL );
+
+#ifdef UNICODE
+# define LlsProductSecurityGet LlsProductSecurityGetW
+#else
+# define LlsProductSecurityGet LlsProductSecurityGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsProductSecuritySetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR Product
+ );
+
+NTSTATUS
+NTAPI
+LlsProductSecuritySetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR Product
+ );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_SET_W)( LLS_HANDLE, LPWSTR );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_SECURITY_SET_A)( LLS_HANDLE, LPSTR );
+
+#ifdef UNICODE
+# define LlsProductSecuritySet LlsProductSecuritySetW
+#else
+# define LlsProductSecuritySet LlsProductSecuritySetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsProductLicensesGetW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR DisplayName,
+ IN DWORD Mode,
+ OUT LPDWORD pQuantity );
+
+NTSTATUS
+NTAPI
+LlsProductLicensesGetA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR DisplayName,
+ IN DWORD Mode,
+ OUT LPDWORD pQuantity );
+
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_LICENSES_GET_W)( LLS_HANDLE, LPWSTR, DWORD, LPDWORD );
+typedef NTSTATUS (NTAPI *PLLS_PRODUCT_LICENSES_GET_A)( LLS_HANDLE, LPSTR, DWORD, LPDWORD );
+
+#ifdef UNICODE
+# define LlsProductLicensesGet LlsProductLicensesGetW
+#else
+# define LlsProductLicensesGet LlsProductLicensesGetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumW(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ IN DWORD TargetLevel,
+ OUT LPBYTE * ppTargets,
+ OUT LPDWORD pNumTargets );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimEnumA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ IN DWORD TargetLevel,
+ OUT LPBYTE * ppTargets,
+ OUT LPDWORD pNumTargets );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ENUM_W)( LLS_HANDLE, DWORD, LPBYTE, DWORD, LPBYTE *, LPDWORD );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ENUM_A)( LLS_HANDLE, DWORD, LPBYTE, DWORD, LPBYTE *, LPDWORD );
+
+#ifdef UNICODE
+# define LlsCertificateClaimEnum LlsCertificateClaimEnumW
+#else
+# define LlsCertificateClaimEnum LlsCertificateClaimEnumA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckW(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pMayInstall );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddCheckA(
+ IN LLS_HANDLE Handle,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo,
+ OUT LPBOOL pMayInstall );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W)( LLS_HANDLE, DWORD, LPBYTE, LPBOOL );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_CHECK_A)( LLS_HANDLE, DWORD, LPBYTE, LPBOOL );
+
+#ifdef UNICODE
+# define LlsCertificateClaimAddCheck LlsCertificateClaimAddCheckW
+#else
+# define LlsCertificateClaimAddCheck LlsCertificateClaimAddCheckA
+#endif
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddW(
+ IN LLS_HANDLE Handle,
+ IN LPWSTR ServerName,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo );
+
+NTSTATUS
+NTAPI
+LlsCertificateClaimAddA(
+ IN LLS_HANDLE Handle,
+ IN LPSTR ServerName,
+ IN DWORD LicenseLevel,
+ IN LPBYTE pLicenseInfo );
+
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_W)( LLS_HANDLE, LPWSTR, DWORD, LPBYTE );
+typedef NTSTATUS (NTAPI *PLLS_CERTIFICATE_CLAIM_ADD_A)( LLS_HANDLE, LPSTR, DWORD, LPBYTE );
+
+#ifdef UNICODE
+# define LlsCertificateClaimAdd LlsCertificateClaimAddW
+#else
+# define LlsCertificateClaimAdd LlsCertificateClaimAddA
+#endif
+
+
+NTSTATUS
+NTAPI
+LlsReplicationCertDbAddW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID Certificates );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_CERT_DB_ADD_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID SecureProducts );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsReplicationUserAddExW(
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD Level,
+ LPVOID Users );
+
+typedef NTSTATUS (NTAPI *PLLS_REPLICATION_USER_ADD_EX_W)( LLS_REPL_HANDLE, DWORD, LPVOID );
+
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE* bufptr,
+ DWORD PrefMaxLen,
+ LPDWORD EntriesRead,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle );
+
+#ifdef UNICODE
+# define LlsLocalServiceEnum LlsLocalServiceEnumW
+#else
+# define LlsLocalServiceEnum LlsLocalServiceEnumA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPBYTE bufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceAdd LlsLocalServiceAddW
+#else
+# define LlsLocalServiceAdd LlsLocalServiceAddA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ LPBYTE bufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceInfoSet LlsLocalServiceInfoSetW
+#else
+# define LlsLocalServiceInfoSet LlsLocalServiceInfoSetA
+#endif
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ LPBYTE * pbufptr );
+
+NTSTATUS
+NTAPI
+LlsLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ LPSTR KeyName,
+ LPBYTE * pbufptr );
+
+#ifdef UNICODE
+# define LlsLocalServiceInfoGet LlsLocalServiceInfoGetW
+#else
+# define LlsLocalServiceInfoGet LlsLocalServiceInfoGetA
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+// CCF API //
+///////////////
+
+#define CCF_ENTER_FLAG_PER_SEAT_ONLY ( 1 )
+#define CCF_ENTER_FLAG_PER_SERVER_ONLY ( 2 )
+#define CCF_ENTER_FLAG_SERVER_IS_ES ( 4 )
+
+// prototype for certificate source enter API
+typedef DWORD (APIENTRY *PCCF_ENTER_API)( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags );
+
+DWORD APIENTRY CCFCertificateEnterUI( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags,
+ LPCSTR pszSourceToUse );
+
+// prototype for certificate source remove API
+typedef DWORD (APIENTRY *PCCF_REMOVE_API)( HWND hWndParent,
+ LPCSTR pszServerName,
+ DWORD dwFlags,
+ DWORD dwLicenseLevel,
+ LPVOID lpvLicenseInfo );
+
+DWORD APIENTRY CCFCertificateRemoveUI( HWND hWndParent,
+ LPCSTR pszServerName,
+ LPCSTR pszProductName,
+ LPCSTR pszVendor,
+ DWORD dwFlags,
+ LPCSTR pszSourceToUse );
+
+#endif
+
+//
+// Registry values
+//
+
+#define REG_KEY_LICENSE TEXT("SYSTEM\\CurrentControlSet\\Services\\LicenseInfo")
+#define REG_KEY_CONFIG TEXT("SYSTEM\\CurrentControlSet\\Services\\LicenseService\\Parameters")
+
+#define REG_VALUE_NAME TEXT("DisplayName")
+#define REG_VALUE_FAMILY TEXT("FamilyDisplayName")
+#define REG_VALUE_MODE TEXT("Mode")
+#define REG_VALUE_FLIP TEXT("FlipAllow")
+#define REG_VALUE_LIMIT TEXT("ConcurrentLimit")
+#define REG_VALUE_HIGHMARK TEXT("LocalKey")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/inc/llsconst.h b/private/net/svcdlls/lls/inc/llsconst.h
new file mode 100644
index 000000000..eb65778f0
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsconst.h
@@ -0,0 +1,14 @@
+
+#ifndef NT_LS_USER_NAME
+#define NT_LS_USER_NAME 0
+#endif
+
+#ifndef NT_LS_USER_SID
+#define NT_LS_USER_SID 1
+#endif
+
+#define MAX_PRODUCT_NAME_LENGTH 35
+#define MAX_VERSION_LENGTH 15
+#define MAX_USER_NAME_LENGTH 37
+#define MAX_DATA_LENGTH 80
+#define MAX_DATA_LENGTHW 40
diff --git a/private/net/svcdlls/lls/inc/llsimp.h b/private/net/svcdlls/lls/inc/llsimp.h
new file mode 100644
index 000000000..13dfcb17f
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/llsimp.h
@@ -0,0 +1,24 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ llsimp.h
+
+Abstract:
+
+ Private Includes
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <windef.h>
diff --git a/private/net/svcdlls/lls/inc/lpcstub.h b/private/net/svcdlls/lls/inc/lpcstub.h
new file mode 100644
index 000000000..bcdf3e8ca
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/lpcstub.h
@@ -0,0 +1,38 @@
+//
+//
+//
+
+#ifndef _LLSLPCSTUB_H
+#define _LLSLPCSTUB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS LLSInitLPC();
+NTSTATUS LLSCloseLPC();
+NTSTATUS LLSLicenseRequest ( IN LPWSTR ProductName, IN LPWSTR Version, IN ULONG DataType,
+ IN BOOLEAN IsAdmin, IN PVOID Data, OUT PULONG LicenseHandle );
+NTSTATUS LLSLicenseFree ( IN ULONG LicenseHandle );
+
+
+#ifdef DEBUG
+
+NTSTATUS LLSDbg_TableDump ( ULONG Table );
+NTSTATUS LLSDbg_TableInfoDump ( IN ULONG Table, IN ULONG DataType, IN PVOID Data );
+NTSTATUS LLSDbg_TableFlush ( ULONG Table );
+NTSTATUS LLSDbg_TraceSet ( ULONG Level );
+NTSTATUS LLSDbg_ConfigDump ( );
+NTSTATUS LLSDbg_ReplicationForce ( );
+NTSTATUS LLSDbg_ReplicationDeny ( );
+NTSTATUS LLSDbg_RegistryUpdateForce ( );
+NTSTATUS LLSDbg_LicenseCheckForce ( );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/inc/rpcutil.h b/private/net/svcdlls/lls/inc/rpcutil.h
new file mode 100644
index 000000000..345cfe1a3
--- /dev/null
+++ b/private/net/svcdlls/lls/inc/rpcutil.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ rpcutil.h
+
+Abstract:
+
+ This file contains prototypes for the bind and unbind functions that
+ all lls functions will call. It also includes the allocate
+ and free routines used by the MIDL generated RPC stubs.
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1994
+
+[Environment:]
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _RPCUTIL_
+#define _RPCUTIL_
+
+#ifndef RPC_NO_WINDOWS_H // Don't let rpc.h include windows.h
+#define RPC_NO_WINDOWS_H
+#endif // RPC_NO_WINDOWS_H
+
+#include <rpc.h>
+
+//
+// The following typedefs are created for use in the Enum entry point
+// routines. These structures are meant to mirror the level specific
+// info containers that are specified in the .idl file for the Enum API
+// function. Using these structures to set up for the API call allows
+// the entry point routine to avoid using any bulky level-specific logic
+// to set-up or return from the RPC stub call.
+//
+
+typedef struct _GENERIC_INFO_CONTAINER {
+ DWORD EntriesRead;
+ LPBYTE Buffer;
+} GENERIC_INFO_CONTAINER, *PGENERIC_INFO_CONTAINER, *LPGENERIC_INFO_CONTAINER ;
+
+typedef struct _GENERIC_ENUM_STRUCT {
+ DWORD Level;
+ PGENERIC_INFO_CONTAINER Container;
+} GENERIC_ENUM_STRUCT, *PGENERIC_ENUM_STRUCT, *LPGENERIC_ENUM_STRUCT ;
+
+
+
+//
+// DEFINES
+//
+
+//
+// Function Prototypes
+//
+
+void *
+MIDL_user_allocate(
+ IN ULONG NumBytes
+ );
+
+void
+MIDL_user_free(
+ IN PVOID MemPointer
+ );
+
+
+#endif // _RPCUTIL_
diff --git a/private/net/svcdlls/lls/llscli.acf b/private/net/svcdlls/lls/llscli.acf
new file mode 100644
index 000000000..708b5f4ac
--- /dev/null
+++ b/private/net/svcdlls/lls/llscli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llscli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsrpc_handle)
+]
+
+interface llsrpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/llsdbg.idl b/private/net/svcdlls/lls/llsdbg.idl
new file mode 100644
index 000000000..3d71d07d3
--- /dev/null
+++ b/private/net/svcdlls/lls/llsdbg.idl
@@ -0,0 +1,113 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsdbg.idl
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ uuid(F40E17F0-520F-11CE-A897-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface llsdbgrpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+//
+// Debugging API's
+//
+NTSTATUS
+LlsrDbgTableDump(
+ [in] DWORD Table
+ );
+
+NTSTATUS
+LlsrDbgTableInfoDump(
+ [in] DWORD Table,
+ [in, string] LPWSTR Item
+ );
+
+NTSTATUS
+LlsrDbgTableFlush(
+ [in] DWORD Table
+ );
+
+NTSTATUS
+LlsrDbgTraceSet(
+ [in] DWORD Flags
+ );
+
+NTSTATUS
+LlsrDbgConfigDump(
+ );
+
+NTSTATUS
+LlsrDbgReplicationForce(
+ );
+
+NTSTATUS
+LlsrDbgReplicationDeny(
+ );
+
+NTSTATUS
+LlsrDbgRegistryUpdateForce(
+ );
+
+NTSTATUS
+LlsrDbgDatabaseFlush(
+ );
+
+
+}
diff --git a/private/net/svcdlls/lls/llsimp.idl b/private/net/svcdlls/lls/llsimp.idl
new file mode 100644
index 000000000..4a4af637d
--- /dev/null
+++ b/private/net/svcdlls/lls/llsimp.idl
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1991-1995 Microsoft Corporation
+
+Module Name:
+
+ llsimp.idl (taken from lsaimp.idl)
+
+Abstract:
+
+ Temporary dummy IDL interface for ntos2.h.
+
+ This file contains a dummy RPC Interface Definition Language file for
+ ntos2.h. This allows the file ntos2.h to be presented to the RPC compiler
+ as an included file within an imported interface. This temporary measure
+ is necessary so that definitions and function prototypes within ntos2.h
+ and its descendants are presentable to midl in such a way that:
+
+ (a) Types are not generated in the output .h file generated by midl
+ (b) Function prototypes therein are not treated as belonging to the
+ IDL interface being compiled.
+
+Author: Scott Birrell (ScottBi) April 23, 1991
+
+Environment: User Mode
+
+Revision History:
+
+--*/
+[
+ uuid(6D5F5960-41FB-11CE-A894-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ endpoint("mscn_np:[\pipe\llsrpc]")
+]
+
+interface lsaimp
+
+{
+
+#define MIDL_PASS "llsimp.idl"
+#include <llsimp.h>
+#include <ntlsapi.h>
+void LlsImpDummy();
+}
diff --git a/private/net/svcdlls/lls/llsrpc.idl b/private/net/svcdlls/lls/llsrpc.idl
new file mode 100644
index 000000000..d54c74ebd
--- /dev/null
+++ b/private/net/svcdlls/lls/llsrpc.idl
@@ -0,0 +1,2041 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsrpc.idl
+
+Abstract:
+
+ License Logging Service RPC Interface Definition File
+
+ This file contains the RPC Interface Definition Language file for
+ the LLS.
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+ Jeff Parham (jeffparh) 06-Dec-1995
+ o Added LLS_LICENSE_INFO_1 and LLS_PRODUCT_LICENSE_INFO_1 support.
+ o Added new API's for SUR.
+ o Plugged memory leak at the server caused by LlsConnect() and
+ LlsReplConnect() being defined as taking PNAMEW parameters (which
+ differ from LPWSTR's in the sense that they're not automatically
+ freed at the server), even though they neither stored nor freed
+ the passed pointers.
+
+--*/
+
+[
+ uuid(342CFD40-3C6C-11CE-A893-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface llsrpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+//
+// LLS RPC Context Handle
+//
+
+typedef [context_handle] PVOID LLS_HANDLE;
+typedef [context_handle] PVOID LLS_REPL_HANDLE;
+
+typedef [ref] LLS_HANDLE * PLLS_HANDLE;
+typedef [ref] LLS_REPL_HANDLE * PLLS_REPL_HANDLE;
+
+// these are not freed at the server
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+// License Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_LICENSE_INFO_0W {
+ PNAMEW Product;
+ LONG Quantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+} LLS_LICENSE_INFO_0W, *PLLS_LICENSE_INFO_0W;
+
+typedef struct _LLS_LICENSE_INFO_1W {
+ PNAMEW Product;
+ PNAMEW Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEW Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_LICENSE_INFO_1W, *PLLS_LICENSE_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_LICENSE_INFO_0W LicenseInfo0;
+ [case(1)] LLS_LICENSE_INFO_1W LicenseInfo1;
+} LLS_LICENSE_INFOW, *PLLS_LICENSE_INFOW;
+
+typedef struct _LLS_LICENSE_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_0W Buffer;
+} LLS_LICENSE_INFO_0_CONTAINERW, *PLLS_LICENSE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_LICENSE_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_1W Buffer;
+} LLS_LICENSE_INFO_1_CONTAINERW, *PLLS_LICENSE_INFO_1_CONTAINERW;
+
+typedef struct _LLS_LICENSE_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LICENSE_ENUM_UNIONW {
+ [case(0)]
+ PLLS_LICENSE_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_LICENSE_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsLicenseInfo;
+} LLS_LICENSE_ENUM_STRUCTW, *PLLS_LICENSE_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_LICENSE_INFO_0A {
+ PNAMEA Product;
+ LONG Quantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+} LLS_LICENSE_INFO_0A, *PLLS_LICENSE_INFO_0A;
+
+typedef struct _LLS_LICENSE_INFO_1A
+{
+ PNAMEA Product;
+ PNAMEA Vendor;
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEA Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_LICENSE_INFO_1A, *PLLS_LICENSE_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_LICENSE_INFO_0A LicenseInfo0;
+ [case(1)] LLS_LICENSE_INFO_1A LicenseInfo1;
+} LLS_LICENSE_INFOA, *PLLS_LICENSE_INFOA;
+
+typedef struct _LLS_LICENSE_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_0A Buffer;
+} LLS_LICENSE_INFO_0_CONTAINERA, *PLLS_LICENSE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_LICENSE_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LICENSE_INFO_1A Buffer;
+} LLS_LICENSE_INFO_1_CONTAINERA, *PLLS_LICENSE_INFO_1_CONTAINERA;
+
+typedef struct _LLS_LICENSE_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LICENSE_ENUM_UNIONA {
+ [case(0)]
+ PLLS_LICENSE_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_LICENSE_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsLicenseInfo;
+} LLS_LICENSE_ENUM_STRUCTA, *PLLS_LICENSE_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_INFO_0W {
+ PNAMEW Product;
+} LLS_PRODUCT_INFO_0W, *PLLS_PRODUCT_INFO_0W;
+
+typedef struct _LLS_PRODUCT_INFO_1W {
+ PNAMEW Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG TotalConcurrent;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1W, *PLLS_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_INFO_0W ProductInfo0;
+ [case(1)] LLS_PRODUCT_INFO_1W ProductInfo1;
+} LLS_PRODUCT_INFOW, *PLLS_PRODUCT_INFOW;
+
+typedef struct _LLS_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_0W Buffer;
+} LLS_PRODUCT_INFO_0_CONTAINERW, *PLLS_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_1W Buffer;
+} LLS_PRODUCT_INFO_1_CONTAINERW, *PLLS_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductInfo;
+} LLS_PRODUCT_ENUM_STRUCTW, *PLLS_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_INFO_0A {
+ PNAMEA Product;
+} LLS_PRODUCT_INFO_0A, *PLLS_PRODUCT_INFO_0A;
+
+typedef struct _LLS_PRODUCT_INFO_1A {
+ PNAMEA Product;
+ ULONG Purchased;
+ ULONG InUse;
+ ULONG TotalConcurrent;
+ ULONG HighMark;
+} LLS_PRODUCT_INFO_1A, *PLLS_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_INFO_0A ProductInfo0;
+ [case(1)] LLS_PRODUCT_INFO_1A ProductInfo1;
+} LLS_PRODUCT_INFOA, *PLLS_PRODUCT_INFOA;
+
+typedef struct _LLS_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_0A Buffer;
+} LLS_PRODUCT_INFO_0_CONTAINERA, *PLLS_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_INFO_1A Buffer;
+} LLS_PRODUCT_INFO_1_CONTAINERA, *PLLS_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductInfo;
+} LLS_PRODUCT_ENUM_STRUCTA, *PLLS_PRODUCT_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product User Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_USER_INFO_0W {
+ PNAMEW User;
+} LLS_PRODUCT_USER_INFO_0W, *PLLS_PRODUCT_USER_INFO_0W;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1W {
+ PNAMEW User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1W, *PLLS_PRODUCT_USER_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_USER_INFO_0W ProductUserInfo0;
+ [case(1)] LLS_PRODUCT_USER_INFO_1W ProductUserInfo1;
+} LLS_PRODUCT_USER_INFOW, *PLLS_PRODUCT_USER_INFOW;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_0W Buffer;
+} LLS_PRODUCT_USER_INFO_0_CONTAINERW, *PLLS_PRODUCT_USER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_1W Buffer;
+} LLS_PRODUCT_USER_INFO_1_CONTAINERW, *PLLS_PRODUCT_USER_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_USER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_USER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_USER_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_USER_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductUserInfo;
+} LLS_PRODUCT_USER_ENUM_STRUCTW, *PLLS_PRODUCT_USER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_USER_INFO_0A {
+ PNAMEA User;
+} LLS_PRODUCT_USER_INFO_0A, *PLLS_PRODUCT_USER_INFO_0A;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1A {
+ PNAMEA User;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_PRODUCT_USER_INFO_1A, *PLLS_PRODUCT_USER_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_USER_INFO_0A ProductUserInfo0;
+ [case(1)] LLS_PRODUCT_USER_INFO_1A ProductUserInfo1;
+} LLS_PRODUCT_USER_INFOA, *PLLS_PRODUCT_USER_INFOA;
+
+typedef struct _LLS_PRODUCT_USER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_0A Buffer;
+} LLS_PRODUCT_USER_INFO_0_CONTAINERA, *PLLS_PRODUCT_USER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_USER_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_USER_INFO_1A Buffer;
+} LLS_PRODUCT_USER_INFO_1_CONTAINERA, *PLLS_PRODUCT_USER_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_USER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_USER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_USER_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_PRODUCT_USER_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsProductUserInfo;
+} LLS_PRODUCT_USER_ENUM_STRUCTA, *PLLS_PRODUCT_USER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product License Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0W {
+ LONG Quantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+} LLS_PRODUCT_LICENSE_INFO_0W, *PLLS_PRODUCT_LICENSE_INFO_0W;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1W {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEW Admin;
+ PNAMEW Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEW Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_PRODUCT_LICENSE_INFO_1W, *PLLS_PRODUCT_LICENSE_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_LICENSE_INFO_0W ProductLicenseInfo0;
+ [case(1)] LLS_PRODUCT_LICENSE_INFO_1W ProductLicenseInfo1;
+} LLS_PRODUCT_LICENSE_INFOW, *PLLS_PRODUCT_LICNESE_INFOW;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_0W Buffer;
+} LLS_PRODUCT_LICENSE_INFO_0_CONTAINERW, *PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_1W Buffer;
+} LLS_PRODUCT_LICENSE_INFO_1_CONTAINERW, *PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERW;
+
+typedef struct _LLS_PRODUCT_LICENSE_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_LICENSE_ENUM_UNIONW {
+ [case(0)]
+ PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsProductLicenseInfo;
+} LLS_PRODUCT_LICENSE_ENUM_STRUCTW, *PLLS_PRODUCT_LICENSE_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0A {
+ LONG Quantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+} LLS_PRODUCT_LICENSE_INFO_0A, *PLLS_PRODUCT_LICENSE_INFO_0A;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1A {
+ LONG Quantity;
+ DWORD MaxQuantity;
+ DWORD Date;
+ PNAMEA Admin;
+ PNAMEA Comment;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ PNAMEA Source;
+ DWORD ExpirationDate;
+ DWORD Secrets[ 4 ];
+} LLS_PRODUCT_LICENSE_INFO_1A, *PLLS_PRODUCT_LICENSE_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_PRODUCT_LICENSE_INFO_0A ProductLicenseInfo0;
+ [case(1)] LLS_PRODUCT_LICENSE_INFO_1A ProductLicenseInfo1;
+} LLS_PRODUCT_LICENSE_INFOA, *PLLS_PRODUCT_LICENSE_INFOA;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_0A Buffer;
+} LLS_PRODUCT_LICENSE_INFO_0_CONTAINERA, *PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_LICENSE_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_PRODUCT_LICENSE_INFO_1A Buffer;
+} LLS_PRODUCT_LICENSE_INFO_1_CONTAINERA, *PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERA;
+
+typedef struct _LLS_PRODUCT_LICENSE_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_PRODUCT_LICENSE_ENUM_UNIONA {
+ [case(0)]
+ PLLS_PRODUCT_LICENSE_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_PRODUCT_LICENSE_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsProductLicenseInfo;
+} LLS_PRODUCT_LICENSE_ENUM_STRUCTA, *PLLS_PRODUCT_LICENSE_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Server Product Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVER_PRODUCT_INFO_0W {
+ PNAMEW Name;
+} LLS_SERVER_PRODUCT_INFO_0W, *PLLS_SERVER_PRODUCT_INFO_0W;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1W {
+ PNAMEW Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1W, *PLLS_SERVER_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_PRODUCT_INFO_0W ServerProductInfo0;
+ [case(1)] LLS_SERVER_PRODUCT_INFO_1W ServerProductInfo1;
+} LLS_SERVER_PRODUCT_INFOW, *PLLS_SERVER_PRODUCT_INFOW;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_0W Buffer;
+} LLS_SERVER_PRODUCT_INFO_0_CONTAINERW, *PLLS_SERVER_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_1W Buffer;
+} LLS_SERVER_PRODUCT_INFO_1_CONTAINERW, *PLLS_SERVER_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_SERVER_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_SERVER_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_SERVER_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsServerProductInfo;
+} LLS_SERVER_PRODUCT_ENUM_STRUCTW, *PLLS_SERVER_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVER_PRODUCT_INFO_0A {
+ PNAMEA Name;
+} LLS_SERVER_PRODUCT_INFO_0A, *PLLS_SERVER_PRODUCT_INFO_0A;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1A {
+ PNAMEA Name;
+ DWORD Flags;
+ ULONG MaxUses;
+ ULONG MaxSetUses;
+ ULONG HighMark;
+} LLS_SERVER_PRODUCT_INFO_1A, *PLLS_SERVER_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_PRODUCT_INFO_0A ServerProductInfo0;
+ [case(1)] LLS_SERVER_PRODUCT_INFO_1A ServerProductInfo1;
+} LLS_SERVER_PRODUCT_INFOA, *PLLS_SERVER_PRODUCT_INFOA;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_0A Buffer;
+} LLS_SERVER_PRODUCT_INFO_0_CONTAINERA, *PLLS_SERVER_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_SERVER_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_PRODUCT_INFO_1A Buffer;
+} LLS_SERVER_PRODUCT_INFO_1_CONTAINERA, *PLLS_SERVER_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_SERVER_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_SERVER_PRODUCT_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_SERVER_PRODUCT_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsServerProductInfo;
+} LLS_SERVER_PRODUCT_ENUM_STRUCTA, *PLLS_SERVER_PRODUCT_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_USER_INFO_0W {
+ PNAMEW Name;
+} LLS_USER_INFO_0W, *PLLS_USER_INFO_0W;
+
+typedef struct _LLS_USER_INFO_1W {
+ PNAMEW Name;
+ DWORD Flags;
+ PNAMEW Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1W, *PLLS_USER_INFO_1W;
+
+typedef struct _LLS_USER_INFO_2W {
+ PNAMEW Name;
+ DWORD Flags;
+ PNAMEW Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ [string, unique] LPWSTR Products;
+} LLS_USER_INFO_2W, *PLLS_USER_INFO_2W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_INFO_0W UserInfo0;
+ [case(1)] LLS_USER_INFO_1W UserInfo1;
+ [case(2)] LLS_USER_INFO_2W UserInfo2;
+} LLS_USER_INFOW, *PLLS_USER_INFOW;
+
+typedef struct _LLS_USER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_0W Buffer;
+} LLS_USER_INFO_0_CONTAINERW, *PLLS_USER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_USER_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_1W Buffer;
+} LLS_USER_INFO_1_CONTAINERW, *PLLS_USER_INFO_1_CONTAINERW;
+
+typedef struct _LLS_USER_INFO_2_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_2W Buffer;
+} LLS_USER_INFO_2_CONTAINERW, *PLLS_USER_INFO_2_CONTAINERW;
+
+typedef struct _LLS_USER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_USER_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_USER_INFO_1_CONTAINERW Level1;
+ [case(2)]
+ PLLS_USER_INFO_2_CONTAINERW Level2;
+ [default]
+ ;
+ } LlsUserInfo;
+} LLS_USER_ENUM_STRUCTW, *PLLS_USER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_USER_INFO_0A {
+ PNAMEA Name;
+} LLS_USER_INFO_0A, *PLLS_USER_INFO_0A;
+
+typedef struct _LLS_USER_INFO_1A {
+ PNAMEA Name;
+ DWORD Flags;
+ PNAMEA Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+} LLS_USER_INFO_1A, *PLLS_USER_INFO_1A;
+
+typedef struct _LLS_USER_INFO_2A {
+ PNAMEA Name;
+ DWORD Flags;
+ PNAMEA Mapping;
+ ULONG Licensed;
+ ULONG UnLicensed;
+ [string, unique] LPSTR Products;
+} LLS_USER_INFO_2A, *PLLS_USER_INFO_2A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_INFO_0A UserInfo0;
+ [case(1)] LLS_USER_INFO_1A UserInfo1;
+ [case(2)] LLS_USER_INFO_2A UserInfo2;
+} LLS_USER_INFOA, *PLLS_USER_INFOA;
+
+typedef struct _LLS_USER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_0A Buffer;
+} LLS_USER_INFO_0_CONTAINERA, *PLLS_USER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_USER_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_1A Buffer;
+} LLS_USER_INFO_1_CONTAINERA, *PLLS_USER_INFO_1_CONTAINERA;
+
+typedef struct _LLS_USER_INFO_2_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_INFO_2A Buffer;
+} LLS_USER_INFO_2_CONTAINERA, *PLLS_USER_INFO_2_CONTAINERA;
+
+typedef struct _LLS_USER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_USER_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_USER_INFO_1_CONTAINERA Level1;
+ [case(2)]
+ PLLS_USER_INFO_2_CONTAINERA Level2;
+ [default]
+ ;
+ } LlsUserInfo;
+} LLS_USER_ENUM_STRUCTA, *PLLS_USER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Product info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_USER_PRODUCT_INFO_0W {
+ PNAMEW Product;
+} LLS_USER_PRODUCT_INFO_0W, *PLLS_USER_PRODUCT_INFO_0W;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1W {
+ PNAMEW Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1W, *PLLS_USER_PRODUCT_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_PRODUCT_INFO_0W UserProduct0;
+ [case(1)] LLS_USER_PRODUCT_INFO_1W UserProduct1;
+} LLS_USER_PRODUCT_INFOW, *PLLS_USER_PRODUCT_INFOW;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_0W Buffer;
+} LLS_USER_PRODUCT_INFO_0_CONTAINERW, *PLLS_USER_PRODUCT_INFO_0_CONTAINERW;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_1W Buffer;
+} LLS_USER_PRODUCT_INFO_1_CONTAINERW, *PLLS_USER_PRODUCT_INFO_1_CONTAINERW;
+
+typedef struct _LLS_USER_PRODUCT_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_PRODUCT_ENUM_UNIONW {
+ [case(0)]
+ PLLS_USER_PRODUCT_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_USER_PRODUCT_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsUserProductInfo;
+} LLS_USER_PRODUCT_ENUM_STRUCTW, *PLLS_USER_PRODUCT_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_USER_PRODUCT_INFO_0A {
+ PNAMEA Product;
+} LLS_USER_PRODUCT_INFO_0A, *PLLS_USER_PRODUCT_INFO_0A;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1A {
+ PNAMEA Product;
+ DWORD Flags;
+ DWORD LastUsed;
+ ULONG UsageCount;
+} LLS_USER_PRODUCT_INFO_1A, *PLLS_USER_PRODUCT_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_USER_PRODUCT_INFO_0A UserProduct0;
+ [case(1)] LLS_USER_PRODUCT_INFO_1A UserProduct1;
+} LLS_USER_PRODUCT_INFOA, *PLLS_USER_PRODUCT_INFOA;
+
+typedef struct _LLS_USER_PRODUCT_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_0A Buffer;
+} LLS_USER_PRODUCT_INFO_0_CONTAINERA, *PLLS_USER_PRODUCT_INFO_0_CONTAINERA;
+
+typedef struct _LLS_USER_PRODUCT_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_USER_PRODUCT_INFO_1A Buffer;
+} LLS_USER_PRODUCT_INFO_1_CONTAINERA, *PLLS_USER_PRODUCT_INFO_1_CONTAINERA;
+
+typedef struct _LLS_USER_PRODUCT_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_USER_PRODUCT_ENUM_UNIONA {
+ [case(0)]
+ PLLS_USER_PRODUCT_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_USER_PRODUCT_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsUserProductInfo;
+} LLS_USER_PRODUCT_ENUM_STRUCTA, *PLLS_USER_PRODUCT_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Mapping Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_MAPPING_INFO_0W {
+ PNAMEW Name;
+} LLS_MAPPING_INFO_0W, *PLLS_MAPPING_INFO_0W;
+
+typedef struct _LLS_MAPPING_INFO_1W {
+ PNAMEW Name;
+ PNAMEW Comment;
+ ULONG Licenses;
+} LLS_MAPPING_INFO_1W, *PLLS_MAPPING_INFO_1W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_MAPPING_INFO_0W MappingInfo0;
+ [case(1)] LLS_MAPPING_INFO_1W MappingInfo1;
+} LLS_MAPPING_INFOW, *PLLS_MAPPING_INFOW;
+
+typedef struct _LLS_MAPPING_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_0W Buffer;
+} LLS_MAPPING_INFO_0_CONTAINERW, *PLLS_MAPPING_INFO_0_CONTAINERW;
+
+typedef struct _LLS_MAPPING_INFO_1_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_1W Buffer;
+} LLS_MAPPING_INFO_1_CONTAINERW, *PLLS_MAPPING_INFO_1_CONTAINERW;
+
+typedef struct _LLS_MAPPING_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_MAPPING_ENUM_UNIONW {
+ [case(0)]
+ PLLS_MAPPING_INFO_0_CONTAINERW Level0;
+ [case(1)]
+ PLLS_MAPPING_INFO_1_CONTAINERW Level1;
+ [default]
+ ;
+ } LlsMappingInfo;
+} LLS_MAPPING_ENUM_STRUCTW, *PLLS_MAPPING_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_MAPPING_INFO_0A {
+ PNAMEA Name;
+} LLS_MAPPING_INFO_0A, *PLLS_MAPPING_INFO_0A;
+
+typedef struct _LLS_MAPPING_INFO_1A {
+ PNAMEA Name;
+ PNAMEA Comment;
+ ULONG Licenses;
+} LLS_MAPPING_INFO_1A, *PLLS_MAPPING_INFO_1A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_MAPPING_INFO_0A MappingInfo0;
+ [case(1)] LLS_MAPPING_INFO_1A MappingInfo1;
+} LLS_MAPPING_INFOA, *PLLS_MAPPING_INFOA;
+
+typedef struct _LLS_MAPPING_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_0A Buffer;
+} LLS_MAPPING_INFO_0_CONTAINERA, *PLLS_MAPPING_INFO_0_CONTAINERA;
+
+typedef struct _LLS_MAPPING_INFO_1_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_MAPPING_INFO_1A Buffer;
+} LLS_MAPPING_INFO_1_CONTAINERA, *PLLS_MAPPING_INFO_1_CONTAINERA;
+
+typedef struct _LLS_MAPPING_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_MAPPING_ENUM_UNIONA {
+ [case(0)]
+ PLLS_MAPPING_INFO_0_CONTAINERA Level0;
+ [case(1)]
+ PLLS_MAPPING_INFO_1_CONTAINERA Level1;
+ [default]
+ ;
+ } LlsMappingInfo;
+} LLS_MAPPING_ENUM_STRUCTA, *PLLS_MAPPING_ENUM_STRUCTA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Service Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVICE_INFO_0W {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ PNAMEW ReplicateTo;
+ PNAMEW EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0W, *PLLS_SERVICE_INFO_0W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVICE_INFO_0W ServiceInfo0;
+} LLS_SERVICE_INFOW, *PLLS_SERVICE_INFOW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVICE_INFO_0A {
+ DWORD Version;
+ DWORD TimeStarted;
+ DWORD Mode;
+ PNAMEA ReplicateTo;
+ PNAMEA EnterpriseServer;
+ DWORD ReplicationType;
+ DWORD ReplicationTime;
+ DWORD UseEnterprise;
+ DWORD LastReplicated;
+} LLS_SERVICE_INFO_0A, *PLLS_SERVICE_INFO_0A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVICE_INFO_0A ServiceInfo0;
+} LLS_SERVICE_INFOA, *PLLS_SERVICE_INFOA;
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Server Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_SERVER_INFO_0W {
+ PNAMEW Name;
+} LLS_SERVER_INFO_0W, *PLLS_SERVER_INFO_0W;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_INFO_0W ServerInfo0;
+} LLS_SERVER_INFOW, *PLLS_SERVER_INFOW;
+
+typedef struct _LLS_SERVER_INFO_0_CONTAINERW {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_INFO_0W Buffer;
+} LLS_SERVER_INFO_0_CONTAINERW, *PLLS_SERVER_INFO_0_CONTAINERW;
+
+typedef struct _LLS_SERVER_ENUM_STRUCTW {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_ENUM_UNIONW {
+ [case(0)]
+ PLLS_SERVER_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsServerInfo;
+} LLS_SERVER_ENUM_STRUCTW, *PLLS_SERVER_ENUM_STRUCTW;
+
+
+//
+// ANSI
+//
+typedef struct _LLS_SERVER_INFO_0A {
+ PNAMEA Name;
+} LLS_SERVER_INFO_0A, *PLLS_SERVER_INFO_0A;
+
+typedef [switch_type(DWORD)] union {
+ [case(0)] LLS_SERVER_INFO_0A ServerInfo0;
+} LLS_SERVER_INFOA, *PLLS_SERVER_INFOA;
+
+typedef struct _LLS_SERVER_INFO_0_CONTAINERA {
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_SERVER_INFO_0A Buffer;
+} LLS_SERVER_INFO_0_CONTAINERA, *PLLS_SERVER_INFO_0_CONTAINERA;
+
+typedef struct _LLS_SERVER_ENUM_STRUCTA {
+ DWORD Level;
+ [switch_is(Level)] union _LLS_SERVER_ENUM_UNIONA {
+ [case(0)]
+ PLLS_SERVER_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsServerInfo;
+} LLS_SERVER_ENUM_STRUCTA, *PLLS_SERVER_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_REQUEST {
+ DWORD Version;
+ WCHAR EnterpriseServer[18];
+ DWORD EnterpriseServerDate;
+ DWORD LastReplicated;
+ DWORD CurrentTime;
+ ULONG NumberServices;
+ ULONG NumberUsers;
+ ULONG ReplSize;
+ ULONG Backoff;
+} REPL_REQUEST, *PREPL_REQUEST;
+
+typedef struct _REPL_SERVER_SERVICE_RECORD {
+ ULONG Server;
+ DWORD Flags;
+ ULONG Service;
+ ULONG MaxSessionCount;
+ ULONG MaxSetSessionCount;
+ ULONG HighMark;
+} REPL_SERVER_SERVICE_RECORD, *PREPL_SERVER_SERVICE_RECORD;
+
+typedef struct _REPL_SERVER_RECORD {
+ ULONG Index;
+ PNAMEW Name;
+ ULONG MasterServer;
+} REPL_SERVER_RECORD, *PREPL_SERVER_RECORD;
+
+typedef struct _REPL_SERVICE_RECORD {
+ ULONG Index;
+ PNAMEW Name;
+ DWORD Version;
+ PNAMEW FamilyName;
+} REPL_SERVICE_RECORD, *PREPL_SERVICE_RECORD;
+
+typedef struct _REPL_USER_NAME_RECORD {
+ PNAMEW Name;
+} REPL_USER_NAME_RECORD, *PREPL_USER_NAME_RECORD;
+
+typedef [unique] PREPL_SERVER_RECORD REPL_SERVERS;
+typedef [unique] PREPL_SERVER_SERVICE_RECORD REPL_SERVER_SERVICES;
+typedef [unique] PREPL_SERVICE_RECORD REPL_SERVICES;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Target Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0W
+{
+ WCHAR ServerName[ 16 ]; // 1 + MAX_COMPUERNAME_LENGTH
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0W, *PLLS_CERTIFICATE_CLAIM_INFO_0W;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_CERTIFICATE_CLAIM_INFO_0W ClaimInfo0;
+} LLS_CERTIFICATE_CLAIM_INFO_W, *PLLS_CERTIFICATE_CLAIM_INFO_W;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_CERTIFICATE_CLAIM_INFO_0W Buffer;
+} LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW, *PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_ENUM_STRUCTW
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_CERTIFICATE_CLAIM_ENUM_UNIONW
+ {
+ [case(0)]
+ PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsCertificateClaimInfo;
+} LLS_CERTIFICATE_CLAIM_ENUM_STRUCTW, *PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW;
+
+//
+// ANSI
+//
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0A
+{
+ CHAR ServerName[ 16 ]; // 1 + MAX_COMPUERNAME_LENGTH
+ LONG Quantity;
+} LLS_CERTIFICATE_CLAIM_INFO_0A, *PLLS_CERTIFICATE_CLAIM_INFO_0A;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_CERTIFICATE_CLAIM_INFO_0A ClaimInfo0;
+} LLS_CERTIFICATE_CLAIM_INFO_A, *PLLS_CERTIFICATE_CLAIM_INFO_A;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_CERTIFICATE_CLAIM_INFO_0A Buffer;
+} LLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA, *PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA;
+
+typedef struct _LLS_CERTIFICATE_CLAIM_ENUM_STRUCTA
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_CERTIFICATE_CLAIM_ENUM_UNIONA
+ {
+ [case(0)]
+ PLLS_CERTIFICATE_CLAIM_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsCertificateClaimInfo;
+} LLS_CERTIFICATE_CLAIM_ENUM_STRUCTA, *PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Certificate Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_CERT_DB_CERTIFICATE_CLAIM_0
+{
+ WCHAR ServerName[ 16 ]; // 1 + MAX_COMPUTERNAME_LENGTH
+ DWORD ReplicationDate;
+ LONG Quantity;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_0, *PREPL_CERT_DB_CERTIFICATE_CLAIM_0;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0
+{
+ DWORD NumClaims;
+ [size_is(NumClaims)] PREPL_CERT_DB_CERTIFICATE_CLAIM_0 Claims;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0, *PREPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER_0 Level0;
+} REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER, *PREPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_HEADER_0
+{
+ DWORD CertificateID;
+ DWORD AllowedModes;
+ DWORD MaxQuantity;
+ DWORD ExpirationDate;
+
+ DWORD NumClaims;
+} REPL_CERT_DB_CERTIFICATE_HEADER_0, *PREPL_CERT_DB_CERTIFICATE_HEADER_0;
+
+typedef struct _REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0
+{
+ DWORD NumHeaders;
+ [size_is(NumHeaders)] PREPL_CERT_DB_CERTIFICATE_HEADER_0 Headers;
+} REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0, *PREPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER_0 Level0;
+} REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER, *PREPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER;
+
+typedef struct _REPL_CERTIFICATE_DB_0
+{
+ DWORD HeaderLevel;
+ [switch_is(HeaderLevel)]
+ REPL_CERT_DB_CERTIFICATE_HEADER_CONTAINER HeaderContainer;
+
+ DWORD ClaimLevel;
+ [switch_is(ClaimLevel)]
+ REPL_CERT_DB_CERTIFICATE_CLAIM_CONTAINER ClaimContainer;
+
+ DWORD StringSize;
+ [size_is(StringSize)] WCHAR * Strings;
+} REPL_CERTIFICATE_DB_0, *PREPL_CERTIFICATE_DB_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_CERTIFICATE_DB_0 Level0;
+} REPL_CERTIFICATE_DB, *PREPL_CERTIFICATE_DB;
+
+typedef [unique] PREPL_CERTIFICATE_DB REPL_CERTIFICATES;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Product Security Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_PRODUCT_SECURITY_0
+{
+ DWORD StringSize;
+ [size_is(StringSize)] WCHAR * Strings;
+} REPL_PRODUCT_SECURITY_0, *PREPL_PRODUCT_SECURITY_0;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_PRODUCT_SECURITY_0 Level0;
+} REPL_PRODUCT_SECURITY, *PREPL_PRODUCT_SECURITY;
+
+typedef [unique] PREPL_PRODUCT_SECURITY REPL_SECURE_PRODUCTS;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// User Replication Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _REPL_USER_RECORD_0
+{
+ PNAMEW Name;
+ ULONG Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+} REPL_USER_RECORD_0, *PREPL_USER_RECORD_0;
+
+typedef struct _REPL_USER_RECORD_CONTAINER_0
+{
+ DWORD NumUsers;
+ [size_is(NumUsers)] PREPL_USER_RECORD_0 Users;
+} REPL_USER_RECORD_CONTAINER_0, *PREPL_USER_RECORD_CONTAINER_0;
+
+typedef struct _REPL_USER_RECORD_1
+{
+ PNAMEW Name;
+ ULONG Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+ DWORD Flags;
+} REPL_USER_RECORD_1, *PREPL_USER_RECORD_1;
+
+typedef struct _REPL_USER_RECORD_CONTAINER_1
+{
+ DWORD NumUsers;
+ [size_is(NumUsers)] PREPL_USER_RECORD_1 Users;
+} REPL_USER_RECORD_CONTAINER_1, *PREPL_USER_RECORD_CONTAINER_1;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] REPL_USER_RECORD_CONTAINER_0 Level0;
+ [case(1)] REPL_USER_RECORD_CONTAINER_1 Level1;
+} REPL_USER_RECORD_CONTAINER, *PREPL_USER_RECORD_CONTAINER;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] PREPL_USER_RECORD_0 Level0;
+ [case(1)] PREPL_USER_RECORD_1 Level1;
+} PREPL_USER_RECORD;
+
+typedef [unique] PREPL_USER_RECORD_CONTAINER REPL_USERS;
+typedef [unique] PREPL_USER_RECORD_0 REPL_USERS_0;
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Local Service Info
+///////////////////////////////////////////////////////////////////////////////
+//
+// Unicode
+//
+typedef struct _LLS_LOCAL_SERVICE_INFO_0W
+{
+ PNAMEW KeyName;
+ PNAMEW DisplayName;
+ PNAMEW FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0W, *PLLS_LOCAL_SERVICE_INFO_0W;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_LOCAL_SERVICE_INFO_0W LocalServiceInfo0;
+} LLS_LOCAL_SERVICE_INFOW, *PLLS_LOCAL_SERVICE_INFOW;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0_CONTAINERW
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LOCAL_SERVICE_INFO_0W Buffer;
+} LLS_LOCAL_SERVICE_INFO_0_CONTAINERW, *PLLS_LOCAL_SERVICE_INFO_0_CONTAINERW;
+
+typedef struct _LLS_LOCAL_SERVICE_ENUM_STRUCTW
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LOCAL_SERVICE_ENUM_UNIONW
+ {
+ [case(0)]
+ PLLS_LOCAL_SERVICE_INFO_0_CONTAINERW Level0;
+ [default]
+ ;
+ } LlsLocalServiceInfo;
+} LLS_LOCAL_SERVICE_ENUM_STRUCTW, *PLLS_LOCAL_SERVICE_ENUM_STRUCTW;
+
+//
+// ANSI
+//
+typedef struct _LLS_LOCAL_SERVICE_INFO_0A
+{
+ PNAMEA KeyName;
+ PNAMEA DisplayName;
+ PNAMEA FamilyDisplayName;
+ DWORD Mode;
+ DWORD FlipAllow;
+ DWORD ConcurrentLimit;
+ DWORD HighMark;
+} LLS_LOCAL_SERVICE_INFO_0A, *PLLS_LOCAL_SERVICE_INFO_0A;
+
+typedef [switch_type(DWORD)] union
+{
+ [case(0)] LLS_LOCAL_SERVICE_INFO_0A LocalServiceInfo0;
+} LLS_LOCAL_SERVICE_INFOA, *PLLS_LOCAL_SERVICE_INFOA;
+
+typedef struct _LLS_LOCAL_SERVICE_INFO_0_CONTAINERA
+{
+ DWORD EntriesRead;
+ [size_is(EntriesRead)] PLLS_LOCAL_SERVICE_INFO_0A Buffer;
+} LLS_LOCAL_SERVICE_INFO_0_CONTAINERA, *PLLS_LOCAL_SERVICE_INFO_0_CONTAINERA;
+
+typedef struct _LLS_LOCAL_SERVICE_ENUM_STRUCTA
+{
+ DWORD Level;
+ [switch_is(Level)] union _LLS_LOCAL_SERVICE_ENUM_UNIONA
+ {
+ [case(0)]
+ PLLS_LOCAL_SERVICE_INFO_0_CONTAINERA Level0;
+ [default]
+ ;
+ } LlsLocalServiceInfo;
+} LLS_LOCAL_SERVICE_ENUM_STRUCTA, *PLLS_LOCAL_SERVICE_ENUM_STRUCTA;
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Connect / Disconnect API's
+//
+NTSTATUS
+LlsrConnect(
+ [out] PLLS_HANDLE Handle,
+ [in, string] LPWSTR Name
+ );
+
+NTSTATUS
+LlsrClose(
+ [in] LLS_HANDLE Handle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// License control API's
+//
+NTSTATUS
+LlsrLicenseEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LICENSE_ENUM_STRUCTW LicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLicenseEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LICENSE_ENUM_STRUCTA LicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+// Add purchase of license for a product.
+NTSTATUS
+LlsrLicenseAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrLicenseAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA BufPtr
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Product control API's
+//
+NTSTATUS
+LlsrProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_PRODUCT_ENUM_STRUCTW ProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_PRODUCT_ENUM_STRUCTA ProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR ProductFamily,
+ [in, string] LPWSTR Product,
+ [in, string] LPWSTR Version
+ );
+
+NTSTATUS
+LlsrProductAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR ProductFamily,
+ [in, string] LPSTR Product,
+ [in, string] LPSTR Version
+ );
+
+NTSTATUS
+LlsrProductUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_PRODUCT_USER_ENUM_STRUCTW ProductUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_PRODUCT_USER_ENUM_STRUCTA ProductUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductServerEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ProductServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductServerEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ProductServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+
+NTSTATUS
+LlsrProductLicenseEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in, out] PLLS_PRODUCT_LICENSE_ENUM_STRUCTW ProductLicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrProductLicenseEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in, out] PLLS_PRODUCT_LICENSE_ENUM_STRUCTA ProductLicenseInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// User control API's
+//
+NTSTATUS
+LlsrUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_USER_ENUM_STRUCTW UserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_USER_ENUM_STRUCTA UserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_USER_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_USER_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_USER_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrUserInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_USER_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrUserDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrUserDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrUserProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in, out] PLLS_USER_PRODUCT_ENUM_STRUCTW UserProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in, out] PLLS_USER_PRODUCT_ENUM_STRUCTA UserProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrUserProductDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR User,
+ [in, string] LPWSTR Product
+ );
+
+NTSTATUS
+LlsrUserProductDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR User,
+ [in] LPSTR Product
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Mapping control API's
+//
+NTSTATUS
+LlsrMappingEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_MAPPING_ENUM_STRUCTW MappingInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_MAPPING_ENUM_STRUCTA MappingInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_MAPPING_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_MAPPING_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrMappingInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrMappingUserEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, out] PLLS_USER_ENUM_STRUCTW MappingUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingUserEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, out] PLLS_USER_ENUM_STRUCTA MappingUserInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrMappingUserAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping,
+ [in, string] LPWSTR User
+ );
+
+NTSTATUS
+LlsrMappingUserDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping,
+ [in, string] LPSTR User
+ );
+
+NTSTATUS
+LlsrMappingAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrMappingAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_MAPPING_INFOA BufPtr
+ );
+
+NTSTATUS
+LlsrMappingDeleteW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Mapping
+ );
+
+NTSTATUS
+LlsrMappingDeleteA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Mapping
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Server Product API's
+//
+NTSTATUS
+LlsrServerEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Server,
+ [in, out] PLLS_SERVER_ENUM_STRUCTW ServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Server,
+ [in, out] PLLS_SERVER_ENUM_STRUCTA ServerInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Server,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrServerProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Server,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Registry Wrapper API's
+//
+NTSTATUS
+LlsrLocalProductEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTW ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalProductEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_SERVER_PRODUCT_ENUM_STRUCTA ServerProductInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalProductInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrLocalProductInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVER_PRODUCT_INFOA BufPtr
+ );
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Service control API's
+//
+NTSTATUS
+LlsrServiceInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVICE_INFOW *BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_SERVICE_INFOA *BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVICE_INFOW BufPtr
+ );
+
+NTSTATUS
+LlsrServiceInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_SERVICE_INFOA BufPtr
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Replication API's
+//
+NTSTATUS
+LlsrReplConnect(
+ [out] PLLS_REPL_HANDLE Handle,
+ [in, string] LPWSTR Name
+ );
+
+NTSTATUS
+LlsrReplClose(
+ [in, out] LLS_REPL_HANDLE *Handle
+ );
+
+NTSTATUS
+LlsrReplicationRequestW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Version,
+ [in, out] PREPL_REQUEST Request
+ );
+
+NTSTATUS
+LlsrReplicationServerAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVERS Servers
+ );
+
+NTSTATUS
+LlsrReplicationServerServiceAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVER_SERVICES ServerServices
+ );
+
+NTSTATUS
+LlsrReplicationServiceAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_SERVICES Services
+ );
+
+NTSTATUS
+LlsrReplicationUserAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] ULONG NumRecords,
+ [in, size_is(NumRecords)] REPL_USERS_0 Users
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// LLS Extended API's (added for SUR)
+//
+NTSTATUS
+LlsrProductSecurityGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product,
+ [out] LPBOOL pIsSecure
+ );
+
+NTSTATUS
+LlsrProductSecurityGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product,
+ [out] LPBOOL pIsSecure
+ );
+
+NTSTATUS
+LlsrProductSecuritySetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR Product
+ );
+
+NTSTATUS
+LlsrProductSecuritySetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR Product
+ );
+
+NTSTATUS
+LlsrProductLicensesGetA(
+ [in] LLS_HANDLE Handle,
+ [in,string] LPSTR DisplayName,
+ [in] DWORD Mode,
+ [out] LPDWORD pQuantity );
+
+NTSTATUS
+LlsrProductLicensesGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR DisplayName,
+ [in] DWORD Mode,
+ [out] LPDWORD pQuantity );
+
+NTSTATUS
+LlsrCertificateClaimEnumA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD LicenseLevel,
+ [in, switch_is(LicenseLevel)] PLLS_LICENSE_INFOA LicensePtr,
+ [in,out] PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA ClaimInfo );
+
+NTSTATUS
+LlsrCertificateClaimEnumW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD LicenseLevel,
+ [in, switch_is(LicenseLevel)] PLLS_LICENSE_INFOW LicensePtr,
+ [in,out] PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW ClaimInfo );
+
+NTSTATUS
+LlsrCertificateClaimAddCheckA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA LicensePtr,
+ [out] LPBOOL pbMayInstall );
+
+NTSTATUS
+LlsrCertificateClaimAddCheckW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW LicensePtr,
+ [out] LPBOOL pbMayInstall );
+
+NTSTATUS
+LlsrCertificateClaimAddA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOA LicensePtr );
+
+NTSTATUS
+LlsrCertificateClaimAddW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR ServerName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LICENSE_INFOW LicensePtr );
+
+NTSTATUS
+LlsrReplicationCertDbAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_CERTIFICATES Certificates );
+
+NTSTATUS
+LlsrReplicationProductSecurityAddW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_SECURE_PRODUCTS SecureProducts );
+
+NTSTATUS
+LlsrReplicationUserAddExW(
+ [in] LLS_REPL_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] REPL_USERS Users );
+
+NTSTATUS
+LlsrCapabilityGet(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD cbCapabilities,
+ [out, size_is(cbCapabilities)] LPBYTE pbCapabilities );
+
+NTSTATUS
+LlsrLocalServiceEnumW(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LOCAL_SERVICE_ENUM_STRUCTW LocalServiceInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalServiceEnumA(
+ [in] LLS_HANDLE Handle,
+ [in, out] PLLS_LOCAL_SERVICE_ENUM_STRUCTA LocalServiceInfo,
+ [in] DWORD PrefMaxLen,
+ [out] LPDWORD TotalEntries,
+ [in, out, unique] LPDWORD ResumeHandle
+ );
+
+NTSTATUS
+LlsrLocalServiceAddW(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceAddA(
+ [in] LLS_HANDLE Handle,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoSetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR KeyName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoSetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR KeyName,
+ [in] DWORD Level,
+ [in, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoGetW(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPWSTR KeyName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOW * LocalServiceInfo
+ );
+
+NTSTATUS
+LlsrLocalServiceInfoGetA(
+ [in] LLS_HANDLE Handle,
+ [in, string] LPSTR KeyName,
+ [in] DWORD Level,
+ [out, switch_is(Level)] PLLS_LOCAL_SERVICE_INFOA * LocalServiceInfo
+ );
+}
diff --git a/private/net/svcdlls/lls/llssrv.acf b/private/net/svcdlls/lls/llssrv.acf
new file mode 100644
index 000000000..b812f5d91
--- /dev/null
+++ b/private/net/svcdlls/lls/llssrv.acf
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Llssrv.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsrpc_handle)
+]
+
+interface llsrpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+typedef [allocate(dont_free)] REPL_SERVERS;
+typedef [allocate(dont_free)] REPL_SERVER_SERVICES;
+typedef [allocate(dont_free)] REPL_SERVICES;
+typedef [allocate(dont_free)] REPL_USERS_0;
+typedef [allocate(dont_free)] REPL_CERTIFICATES;
+typedef [allocate(dont_free)] REPL_SECURE_PRODUCTS;
+typedef [allocate(dont_free)] REPL_USERS;
+
+}
diff --git a/private/net/svcdlls/lls/lsapi.idl b/private/net/svcdlls/lls/lsapi.idl
new file mode 100644
index 000000000..4853e1173
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapi.idl
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsapi.idl
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 20-1994
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ uuid(57674CD0-5200-11CE-A897-08002B2E9C6D),
+ version(0.0),
+#ifdef __midl
+ ms_union,
+#endif // __midl
+ pointer_default(unique)
+]
+
+interface lsapirpc
+
+{
+
+//
+// Import a dummy interface containing #includes for public .h files. This
+// trick is necessary so that midl will only generate marshalling routines
+// for subtypes that are relevant to the parameters specified on the RPC
+// interface. midl also ingores function prototypes contained therein.
+//
+
+import "llsimp.idl" ;
+
+//
+// Emit these constants into the generated file.
+//
+cpp_quote("#define LLS_LPC_ENDPOINT \"llslpc\"")
+//
+// Note: Must use quad backslash to emit two backslashes into #define
+// which when compiled will boil down to single backslash
+//
+cpp_quote("#define LLS_NP_ENDPOINT \"\\\\pipe\\\\llsrpc\"")
+
+
+typedef [string] LPWSTR PNAMEW;
+typedef [string] LPSTR PNAMEA;
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+//
+// Licensing API's
+//
+NTSTATUS
+LlsrLicenseRequestW(
+ [out] LPDWORD LicenseHandle,
+ [in, string] LPWSTR Product,
+ [in] ULONG VersionIndex,
+ [in] BOOLEAN IsAdmin,
+ [in] ULONG DataType,
+ [in] ULONG DataSize,
+ [in, size_is(DataSize)] PBYTE Data
+ );
+
+NTSTATUS
+LlsrLicenseFree(
+ [in] DWORD LicenseHandle
+ );
+
+
+}
diff --git a/private/net/svcdlls/lls/lsapicli.acf b/private/net/svcdlls/lls/lsapicli.acf
new file mode 100644
index 000000000..ae57f41c0
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapicli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsapicli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t lsapirpc_handle)
+]
+
+interface lsapirpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/lsapisrv.acf b/private/net/svcdlls/lls/lsapisrv.acf
new file mode 100644
index 000000000..06d6a1ae2
--- /dev/null
+++ b/private/net/svcdlls/lls/lsapisrv.acf
@@ -0,0 +1,49 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Lsapicli.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t lsapirpc_handle)
+]
+
+interface lsapirpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+}
diff --git a/private/net/svcdlls/lls/lsdbgcli.acf b/private/net/svcdlls/lls/lsdbgcli.acf
new file mode 100644
index 000000000..95d505a7f
--- /dev/null
+++ b/private/net/svcdlls/lls/lsdbgcli.acf
@@ -0,0 +1,46 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ lsdbgcli.acf
+
+Abstract:
+
+ License Logging Service CLIENT rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the client stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS CLIENT STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use llssrv.acf when generating server stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsdbgrpc_handle)
+]
+
+interface llsdbgrpc
+
+{
+
+}
diff --git a/private/net/svcdlls/lls/lsdbgsrv.acf b/private/net/svcdlls/lls/lsdbgsrv.acf
new file mode 100644
index 000000000..4c3d4120d
--- /dev/null
+++ b/private/net/svcdlls/lls/lsdbgsrv.acf
@@ -0,0 +1,49 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Llsdbgsrv.acf
+
+Abstract:
+
+ License Logging Service SERVER rpc stub attribute configuration file.
+
+ This file contains the attribute configuration information necessary
+ for generating the server stubs for remotable LLS functions.
+
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! !!
+ !! This .acf file is USED ONLY WHEN GENERATING LLS SERVER STUBS. !!
+ !! !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ Use Llscli.acf when generating client stubs.
+
+
+Author:
+
+ Arthur Hanson (arth) Jan 30, 1995
+
+Environment:
+
+ User Mode
+
+Revision History:
+
+--*/
+
+[
+ implicit_handle(handle_t llsdbgrpc_handle)
+]
+
+interface llsdbgrpc
+
+{
+
+typedef [allocate(dont_free)] PNAMEW;
+typedef [allocate(dont_free)] PNAMEA;
+
+}
diff --git a/private/net/svcdlls/lls/makefil0 b/private/net/svcdlls/lls/makefil0
new file mode 100644
index 000000000..589468402
--- /dev/null
+++ b/private/net/svcdlls/lls/makefil0
@@ -0,0 +1,151 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+
+!INCLUDE $(NTMAKEENV)\makefile.plt
+
+#####################################################################
+#
+# For the UI RPC functions
+#
+IDL_NAME = llsrpc
+CLIENT_ACF = llscli.acf
+SERVER_ACF = llssrv.acf
+
+CLIENT_INC_FILE = $(IDL_NAME)_c.h
+SERVER_INC_FILE = $(IDL_NAME)_s.h
+
+#####################################################################
+#
+# For the NtLSApi functions
+#
+LSAPI_IDL_NAME = lsapi
+LSAPI_CLIENT_ACF = lsapicli.acf
+LSAPI_SERVER_ACF = lsapisrv.acf
+
+LSAPI_CLIENT_INC_FILE = $(LSAPI_IDL_NAME)_c.h
+LSAPI_SERVER_INC_FILE = $(LSAPI_IDL_NAME)_s.h
+
+#####################################################################
+#
+# For the Debugging RPC functions
+#
+DEBUG_IDL_NAME = llsdbg
+DEBUG_CLIENT_ACF = lsdbgcli.acf
+DEBUG_SERVER_ACF = lsdbgsrv.acf
+
+DEBUG_CLIENT_INC_FILE = $(DEBUG_IDL_NAME)_c.h
+DEBUG_SERVER_INC_FILE = $(DEBUG_IDL_NAME)_s.h
+
+#####################################################################
+#
+# Common INC files
+#
+SDKINC = $(BASEDIR)\public\sdk\inc
+SDKCRTINC = $(BASEDIR)\public\sdk\inc\crt
+LLSINC = .\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(LLSINC)
+
+EXTRN_DEPENDS =
+
+CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(CLIENT_ACF) -header $(CLIENT_INC_FILE)
+SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(SERVER_ACF) -header $(SERVER_INC_FILE)
+
+LSAPI_CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(LSAPI_CLIENT_ACF) -header $(LSAPI_CLIENT_INC_FILE)
+LSAPI_SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(LSAPI_SERVER_ACF) -header $(LSAPI_SERVER_INC_FILE)
+
+DEBUG_CLIENT_FLAGS = -Oi -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(DEBUG_CLIENT_ACF) -header $(DEBUG_CLIENT_INC_FILE)
+DEBUG_SERVER_FLAGS = -oldnames -error allocation -error ref -c_ext -ms_ext $(MIDL_FLAGS) -acf $(DEBUG_SERVER_ACF) -header $(DEBUG_SERVER_INC_FILE)
+
+CPP = -cpp_cmd "$(MIDL_CPP)"
+
+#
+# Separate client and server targets. Note that the .h file produced
+# when MIDL is run with the client .acf file attached differs from the
+# .h file produced when MIDL is run with the server .acf file attached.
+#
+
+CLIENT_TARGETS = client\$(IDL_NAME)_c.c \
+ client\$(CLIENT_INC_FILE)
+
+SERVER_TARGETS = server\$(IDL_NAME)_s.c \
+ server\$(SERVER_INC_FILE)
+
+LSAPI_CLIENT_TARGETS = \
+ ntlsapi\$(LSAPI_IDL_NAME)_c.c \
+ ntlsapi\$(LSAPI_CLIENT_INC_FILE)
+
+LSAPI_SERVER_TARGETS = \
+ server\$(LSAPI_IDL_NAME)_s.c \
+ server\$(LSAPI_SERVER_INC_FILE)
+
+DEBUG_CLIENT_TARGETS = \
+ test\common\$(DEBUG_IDL_NAME)_c.c \
+ test\common\$(DEBUG_CLIENT_INC_FILE)
+
+DEBUG_SERVER_TARGETS = \
+ server\$(DEBUG_IDL_NAME)_s.c \
+ server\$(DEBUG_SERVER_INC_FILE)
+
+TARGETS = $(CLIENT_TARGETS) \
+ $(SERVER_TARGETS) \
+ $(LSAPI_CLIENT_TARGETS) \
+ $(LSAPI_SERVER_TARGETS) \
+ $(DEBUG_CLIENT_TARGETS) \
+ $(DEBUG_SERVER_TARGETS)
+
+
+CLIENT_EXTRN_DEPENDS = $(CLIENT_ACF) $(LSAPI_CLIENT_ACF) $(DEBUG_CLIENT_ACF)
+SERVER_EXTRN_DEPENDS = $(SERVER_ACF) $(LSAPI_SERVER_ACF) $(DEBUG_SERVER_ACF)
+EXTRN_DEPENDS = $(CLIENT_EXTRN_DEPENDS)
+
+#
+# Define Products and Dependencies
+#
+
+all: $(CLIENT_TARGETS) $(SERVER_TARGETS) $(LSAPI_CLIENT_TARGETS) $(LSAPI_SERVER_TARGETS) $(DEBUG_CLIENT_TARGETS) $(DEBUG_SERVER_TARGETS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delete_source all
+
+delete_source:
+ -erase $(TARGETS)
+
+#
+# MIDL COMPILE
+#
+
+$(CLIENT_TARGETS) : $(IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(CLIENT_FLAGS) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(CLIENT_INC_FILE) copy $(CLIENT_INC_FILE) .\client & del $(CLIENT_INC_FILE)
+
+$(SERVER_TARGETS) : $(IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(SERVER_FLAGS) $(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(SERVER_INC_FILE) copy $(SERVER_INC_FILE) .\server & del $(SERVER_INC_FILE)
+
+$(LSAPI_CLIENT_TARGETS) : $(LSAPI_IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(LSAPI_CLIENT_FLAGS) $(LSAPI_IDL_NAME).idl $(INCS)
+ IF EXIST $(LSAPI_IDL_NAME)_c.c copy $(LSAPI_IDL_NAME)_c.c .\ntlsapi & del $(LSAPI_IDL_NAME)_c.c
+ IF EXIST $(LSAPI_CLIENT_INC_FILE) copy $(LSAPI_CLIENT_INC_FILE) .\ntlsapi & del $(LSAPI_CLIENT_INC_FILE)
+
+$(LSAPI_SERVER_TARGETS) : $(LSAPI_IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(LSAPI_SERVER_FLAGS) $(LSAPI_IDL_NAME).idl $(INCS)
+ IF EXIST $(LSAPI_IDL_NAME)_s.c copy $(LSAPI_IDL_NAME)_s.c .\server & del $(LSAPI_IDL_NAME)_s.c
+ IF EXIST $(LSAPI_SERVER_INC_FILE) copy $(LSAPI_SERVER_INC_FILE) .\server & del $(LSAPI_SERVER_INC_FILE)
+
+$(DEBUG_CLIENT_TARGETS) : $(DEBUG_IDL_NAME).idl $(CLIENT_EXTRN_DEPENDS)
+ midl $(CPP) $(DEBUG_CLIENT_FLAGS) $(DEBUG_IDL_NAME).idl $(INCS)
+ IF EXIST $(DEBUG_IDL_NAME)_c.c copy $(DEBUG_IDL_NAME)_c.c .\test\common & del $(DEBUG_IDL_NAME)_c.c
+ IF EXIST $(DEBUG_CLIENT_INC_FILE) copy $(DEBUG_CLIENT_INC_FILE) .\test\common & del $(DEBUG_CLIENT_INC_FILE)
+
+$(DEBUG_SERVER_TARGETS) : $(DEBUG_IDL_NAME).idl $(SERVER_EXTRN_DEPENDS)
+ midl $(CPP) $(DEBUG_SERVER_FLAGS) $(DEBUG_IDL_NAME).idl $(INCS)
+ IF EXIST $(DEBUG_IDL_NAME)_s.c copy $(DEBUG_IDL_NAME)_s.c .\server & del $(DEBUG_IDL_NAME)_s.c
+ IF EXIST $(DEBUG_SERVER_INC_FILE) copy $(DEBUG_SERVER_INC_FILE) .\server & del $(DEBUG_SERVER_INC_FILE)
diff --git a/private/net/svcdlls/lls/ntlsapi/main.c b/private/net/svcdlls/lls/ntlsapi/main.c
new file mode 100644
index 000000000..87699f7e4
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/main.c
@@ -0,0 +1,57 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ main.c
+
+Created:
+
+ 20-Apr-1994
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <malloc.h>
+
+#include <lpcstub.h>
+
+//
+// DLL Startup code
+//
+BOOL WINAPI DllMain(
+ HANDLE hDll,
+ DWORD dwReason,
+ LPVOID lpReserved)
+{
+ switch(dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ LLSInitLPC();
+ break;
+
+ case DLL_PROCESS_DETACH:
+ LLSCloseLPC();
+ break;
+
+ case DLL_THREAD_ATTACH:
+ break;
+
+ case DLL_THREAD_DETACH:
+ break;
+
+ default:
+ break;
+
+ } // end switch()
+
+ return TRUE;
+
+} // DllMain
diff --git a/private/net/svcdlls/lls/ntlsapi/makefile b/private/net/svcdlls/lls/ntlsapi/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.c b/private/net/svcdlls/lls/ntlsapi/ntlsapi.c
new file mode 100644
index 000000000..2f0ae38f1
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.c
@@ -0,0 +1,1150 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ lsapi.c
+
+Created:
+
+ 20-Apr-1994
+
+Revision History:
+
+ 01-Nov-1994 arth Changed from LS API set to simpler request only
+ API.
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <ntlsapi.h>
+#include <llsconst.h>
+#include <debug.h>
+#include <stdlib.h>
+
+#include <lpcstub.h>
+
+// #define API_TRACE 1
+// #define TIME_TRACE 1
+
+
+#ifdef TIME_TRACE
+ DWORD TimeDelta;
+#endif
+
+
+//
+// SID is linear in memory (check if this is safe assumption). Can use
+// RtlCopySID then pass linear buffer - use RtlLengthSid for size.
+//
+
+/*++
+ NtLicenseRequest()
+ Request licensing resources needed to allow software to be
+ used.
+
+ Format
+
+ Status = NtLicenseRequest(
+ [in] LS_STR *ProductName,
+ [in] LS_STR *Version,
+ [in] NT_LS_DATA *NtData)
+ );
+
+ Arguments
+
+ ProductName The name of the product requesting licensing
+ resources. This string may not be null and must
+ be unique (in the first 32 characters) within the
+ PublisherName domain.
+
+ Version The version number of this product. This string
+ must be unique (in the first 12 characters) within
+ the ProductName domain, and cannot be NULL
+
+ NtData The username and/or SID identifying the person using the
+ license.
+
+ NOTE: The arguments ProductName, and Version may not be
+ NULL.
+
+
+ Description
+
+ This function is used by the application to request licensing
+ resources to allow the identified product to execute. If a
+ valid license is found, the challenge response is computed and
+ LS_SUCCESS is returned.
+
+--*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequestA(
+ LPSTR ProductName,
+ LPSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData)
+{
+ WCHAR uProductName[MAX_PRODUCT_NAME_LENGTH + 1];
+ WCHAR uVersion[MAX_VERSION_LENGTH + 1];
+ WCHAR uUserName[MAX_USER_NAME_LENGTH + 1];
+ void *tmpData = NULL;
+ LS_STATUS_CODE ret = LS_SUCCESS;
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLicenseRequestA!!!\r\n"));
+
+#endif
+
+ // Convert parms to Unicode and call Unicode function
+
+ // First make sure we have correct data
+ if ( (ProductName != NULL) && (Version != NULL) && (NtData != NULL) && (NtData->Data != NULL)) {
+ if (lstrlenA(ProductName) > MAX_PRODUCT_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: ProductName too long\r\n"));
+#endif
+ lstrcpy(uProductName, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, ProductName, -1, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+
+ if (lstrlenA(Version) > MAX_VERSION_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: Version too long\r\n"));
+#endif
+ lstrcpy(uVersion, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, Version, -1, uVersion, MAX_VERSION_LENGTH + 1);
+
+ if (NtData->DataType == NT_LS_USER_NAME) {
+ if (lstrlenA((LPSTR) NtData->Data) > MAX_USER_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: UserName too long\r\n"));
+#endif
+ lstrcpy(uUserName, TEXT(""));
+ } else
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, -1, uUserName, MAX_USER_NAME_LENGTH + 1);
+
+ // Have UserName convert to wide char format, but need to point
+ // Data structure to it...
+ tmpData = (void *) NtData->Data;
+ NtData->Data = (void *) uUserName;
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, NtData);
+
+ // Nothing needs to be converted back to ANSI on return, just return
+ // data structure to the way it was
+ NtData->Data = tmpData;
+ return ret;
+ } else {
+ // Gave SID so no Unicode conversion needed on name
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, NtData);
+ return ret;
+ }
+
+ }
+#ifdef API_TRACE
+ else
+ dprintf(TEXT(" LLS Error: <NULL> Parms passed in!\r\n"));
+#endif
+
+ // If NULL parms or such then just return a dummy handle for right now
+ if ( LicenseHandle != NULL )
+ *LicenseHandle = 0xffffffff;
+
+ return(LS_SUCCESS);
+} // NtLicenseRequestA
+
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequestW(
+ LPWSTR ProductName,
+ LPWSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData)
+{
+ LPWSTR dVersion = Version;
+ LS_STATUS_CODE Status;
+#ifdef API_TRACE
+ UNICODE_STRING UString;
+ NTSTATUS NtStatus;
+#endif
+
+ //
+ // Check parms before calling down
+ //
+ if ((ProductName == NULL) || (NtData == NULL) || (NtData->DataType > NT_LS_USER_SID)) {
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLicenseRequestW: <Bad Parms>\r\n"));
+#endif
+
+ if (LicenseHandle != NULL)
+ *LicenseHandle = 0xffffffffL;
+
+ return(LS_SUCCESS);
+ }
+
+ //
+ // LsaLogonUser passes in NULL version because it doesn't know what version
+ // the calling app is. So just use a blank field for this. There must
+ // be something in the version field or it messes up the lower level
+ // algorithms, so just enter a blank.
+ //
+ if ((Version == NULL) || (*Version == TEXT('\0')))
+ dVersion = TEXT("");
+
+#ifdef API_TRACE
+ if (NtData->DataType == NT_LS_USER_SID) {
+ NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) NtData->Data, TRUE);
+
+ if (NtStatus != STATUS_SUCCESS)
+ dprintf(TEXT("NtLicenseRequestW RtlConvertSidToUnicodeString: 0x%lx\n"), NtStatus);
+ else {
+ if (NtData->IsAdmin)
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, <ADMIN>, SID: %s\n"), ProductName, dVersion, UString.Buffer);
+ else
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, SID: %s\n"), ProductName, dVersion, UString.Buffer);
+
+ RtlFreeUnicodeString(&UString);
+ }
+ } else {
+
+ if (NtData->IsAdmin)
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, <ADMIN>, %s\n"), ProductName, dVersion, NtData->Data);
+ else
+ dprintf(TEXT("NtLicenseRequestW: %s, %s, %s\n"), ProductName, dVersion, NtData->Data);
+
+ }
+
+#endif
+
+#ifdef TIME_TRACE
+ TimeDelta = GetTickCount();
+#endif
+
+ // make the LPC call and marshal the parms.
+ Status = (LS_STATUS_CODE) LLSLicenseRequest( ProductName,
+ dVersion,
+ NtData->DataType,
+ (BOOLEAN) NtData->IsAdmin,
+ NtData->Data,
+ LicenseHandle
+ );
+
+#ifdef TIME_TRACE
+ TimeDelta = GetTickCount() - TimeDelta;
+ dprintf(TEXT("NtLicenseRequest LPC Call Time: %ldms\n"), TimeDelta);
+#endif
+
+ return(Status);
+} // NtLicenseRequestW
+
+
+/*
+ NtLSFreeHandle ( )
+
+ Frees all licensing handle context.
+
+ Format
+
+ void NtLSFreeHandle ( [in] LS_HANDLE LicenseHandle);
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ NtLSRequest() or NtLicenseRequest().
+
+ Description
+
+ (NOTE: The handle is no longer valid.)
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSFreeHandle(
+ LS_HANDLE LicenseHandle )
+{
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLSFreeHandle: %ld\r\n"), LicenseHandle);
+#endif
+
+ //
+ // If we get an invalid License Handle (or a dummy 0xFFFFFFFF one)
+ // then don't even bother calling through LPC as it is a waste
+ // of time.
+ //
+ if (LicenseHandle == (ULONG) 0xFFFFFFFFL)
+ return( LS_SUCCESS );
+
+ //
+ // Make the LPC call
+ //
+ LLSLicenseFree( LicenseHandle );
+
+ return( LS_SUCCESS );
+} // NtLSFreeHandle
+
+
+// **************************************************************************
+// OLD API's Don't Use
+// **************************************************************************
+
+/*
+ LSRequest()
+ Request licensing resources needed to allow software to be
+ used.
+
+ Format
+
+ Status = LSRequest( [in] LicenseSystem, [in] PublisherName,
+ [in] ProductName,
+ [in] Version, [in] TotUnitsReserved, [in]
+ LogComment,
+ [in/out] Challenge, [out] TotUnitsGranted, [out]
+ hLicenseHandle );
+
+ LS_STR * LicenseSystem;
+
+ LS_STR * PublisherName;
+
+ LS_STR * ProductName;
+
+ LS_STR * Version;
+
+ LS_ULONG TotUnitsReserved;
+
+ LS_STR * LogComment;
+
+ LS_CHALLENGE *Challenge;
+
+ LS_ULONG * TotUnitsGranted;
+
+ LS_HANDLE * hLicenseHandle;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseSystem Pointer to a string which uniquely identifies
+ the particular license system. This may be
+ obtained through the LSEnumProviders() API.
+ Normally, the constant LS_ANY is specified to
+ indicate a match against all installed license
+ systems (indicates that all license providers
+ should be searched for a license match).
+
+ PublisherName The name of the publisher (manufacturer) of
+ this product. This string may not be null and
+ must be unique in the first 32 characters. It is
+ recommended that a company name and trademark be
+ used.
+
+ ProductName The name of the product requesting licensing
+ resources. This string may not be null and must
+ be unique (in the first 32 characters) within the
+ PublisherName domain.
+
+ Version The version number of this product. This string
+ must be unique (in the first 12 characters) within
+ the ProductName domain, and cannot be NULL
+
+ NOTE: The arguments PublisherName, ProductName,
+ and Version may not be NULL, or may not be
+ LS_ANY.
+
+ TotUnitsReserved Specifies the number of units required to run
+ the application. The software publisher may
+ choose to specify this policy attribute within the
+ application. The recommended value of
+ LS_DEFAULT_UNITS allows the licensing system to
+ determine the proper value using information
+ provided by the license system or license itself.
+ The license system verifies that the requested
+ number of units exist and may reserve those units,
+ but no units are actually consumed at this time.
+ The number of units available is returned in
+ TotUnitsGranted.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Challenge Pointer to a challenge structure. The challenge
+ response will also be returned in this structure.
+ Refer to Challenge Mechanism on page 25 for more
+ information.
+
+ TotUnitsGrantedA pointer to an LS_ULONG in which the total
+ number of units granted is returned. The following
+ table describes the TotUnitsGranted return value,
+ given the TotUnitsReserved input value, and the
+ Status returned:
+
+ LS_INSUFFICIENT_U
+ TotUnitsReserv LS_SUCCES NITS Other
+ ed S errors
+ LS_DEFAULT_UNI (A) (B) (E)
+ TS
+ Other (C) (D) (E)
+ (specific
+ count)
+ (A) The default umber of units commensurate
+ with the license granted.(B) The maximum
+ number of units available to the requesting
+ software. This may be less than the normal
+ default.
+ (C) The number of units used to grant the
+ request. Note that this value may be greater
+ than or equal to the actual units requested
+ (i.e., the policy may allow only in increments
+ of 5 units, thus a request of 7 units would
+ result in 10 units being granted).
+ (D) The maximum number of units available to
+ the requesting software. This may be more or
+ less than the units requested.
+ (E) Zero is returned.
+
+ LicenseHandle Pointer to a LS_HANDLE in which a handle to the
+ license context is to be returned.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ This function is used by the application to request licensing
+ resources to allow the identified product to execute. If a
+ valid license is found, the challenge response is computed and
+ LS_SUCCESS is returned. At minimum, the PublisherName,
+ ProductName, and Version strings are used to identify matching
+ license(s). Note that an underlying license system service
+ provider may ascertain additional information for the license
+ request (e.g., the current username, machine name, etc.).
+
+ A valid license handle is always returned by this function
+ whether valid license resources are granted or not. This
+ handle must always be released with LSFreeHandle() when the
+ application has completed execution.
+
+ If license resources were granted, it must call LSRelease() to
+ free the license resource, prior to calling LSFreeHandle().
+
+ A challenge response is NOT returned unless the license
+ request completed successfully (i.e., a status code of
+ LS_SUCCESS is returned).
+
+ If the number of units requested is greater than the number of
+ units available, then the license request is not granted. Upon
+ successful completion, the value returned in TotUnitsReserved
+ indicates the number of units granted. This is greater than or
+ equal to the number of units requested unless LS_DEFAULT_UNITS
+ was specified. In the case of failure, the value returned in
+ TotUnitsGranted is zero.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSRequest(
+ LS_STR FAR *LicenseSystem,
+ LS_STR FAR *PublisherName,
+ LS_STR FAR *ProductName,
+ LS_STR FAR *Version,
+ LS_ULONG TotUnitsReserved,
+ LS_STR FAR *LogComment,
+ LS_CHALLENGE FAR *Challenge,
+ LS_ULONG FAR *TotUnitsGranted,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA FAR *NtData)
+{
+ NT_LS_DATA tmpNtData;
+ WCHAR uProductName[MAX_PRODUCT_NAME_LENGTH + 1];
+ WCHAR uVersion[MAX_VERSION_LENGTH + 1];
+ WCHAR uUserName[MAX_USER_NAME_LENGTH + 1];
+ LS_STATUS_CODE ret = LS_SUCCESS;
+
+#ifdef API_TRACE
+ dprintf(TEXT("NtLSRequest:\r\n"));
+
+ if (ProductName == NULL)
+ dprintf(TEXT(" Product Name: <NULL>\r\n"));
+
+ if (Version == NULL)
+ dprintf(TEXT(" Version: <NULL>\r\n"));
+
+ if (LicenseHandle == NULL)
+ dprintf(TEXT(" LicenseHandle: <NULL>\r\n"));
+
+ if (NtData != NULL) {
+ if (NtData->Data == NULL)
+ dprintf(TEXT(" NtData->Data: <NULL>\r\n"));
+ } else
+ dprintf(TEXT("NtData: <NULL>\r\n"));
+
+ dprintf(TEXT("\r\n"));
+
+#endif
+
+ // Do some fudging to follow old API spec...
+ if ( TotUnitsGranted != NULL )
+ *TotUnitsGranted = TotUnitsReserved;
+
+ // Need to do a couple things:
+ // 1. Convert used parms to Unicode
+ // 2. Set up new NtData structure (extra IsAdmin field)
+ // 3. Make call to new NtLicenseRequest API and use it's return code.
+ //
+ // Note: No conversion back to ANSI needed upon return from API
+ //
+
+ // First make sure we have correct data
+ if ( (ProductName != NULL) && (Version != NULL) && (NtData != NULL) && (NtData->Data != NULL)) {
+
+ // 1. Convert parms to Unicode
+ if (lstrlenA(ProductName) > MAX_PRODUCT_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: ProductName too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, ProductName, MAX_PRODUCT_NAME_LENGTH, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+ uProductName[MAX_PRODUCT_NAME_LENGTH] = TEXT('\0');
+ } else
+ MultiByteToWideChar(CP_ACP, 0, ProductName, -1, uProductName, MAX_PRODUCT_NAME_LENGTH + 1);
+
+ if (lstrlenA(Version) > MAX_VERSION_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: Version too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, Version, MAX_VERSION_LENGTH, uVersion, MAX_VERSION_LENGTH + 1);
+ uVersion[MAX_VERSION_LENGTH] = TEXT('\0');
+ } else
+ MultiByteToWideChar(CP_ACP, 0, Version, -1, uVersion, MAX_VERSION_LENGTH + 1);
+
+ // 2. Set up new NtData structure
+ tmpNtData.DataType = NtData->DataType;
+
+ // just use FALSE for IsAdmin as none of the old Apps need it.
+ tmpNtData.IsAdmin = FALSE;
+
+ if (NtData->DataType == NT_LS_USER_NAME) {
+ if (lstrlenA((LPSTR) NtData->Data) > MAX_USER_NAME_LENGTH) {
+#ifdef API_TRACE
+ dprintf(TEXT(" Error: UserName too long\r\n"));
+#endif
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, MAX_USER_NAME_LENGTH, uUserName, MAX_USER_NAME_LENGTH + 1);
+ uUserName[MAX_USER_NAME_LENGTH] = TEXT('\0');
+ } else {
+ MultiByteToWideChar(CP_ACP, 0, NtData->Data, -1, uUserName, MAX_USER_NAME_LENGTH + 1);
+ }
+
+ tmpNtData.Data = (void *) uUserName;
+
+ // Have UserName convert to wide char format, but need to point
+ // Data structure to it...
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, &tmpNtData);
+
+ // Nothing needs to be converted back to ANSI on return, just return
+ return ret;
+ } else {
+ // Gave SID so no Unicode convesion needed on name
+ tmpNtData.Data = NtData->Data;
+ ret = NtLicenseRequestW(uProductName, uVersion, LicenseHandle, &tmpNtData);
+ return ret;
+ }
+
+ }
+
+ // If NULL parms or such then just return a dummy handle for right now
+ if ( LicenseHandle != NULL )
+ *LicenseHandle = 0xffffffffL;
+
+ return(LS_SUCCESS);
+} // NtLSRequest
+
+/*
+ LSRelease()
+ Release licensing resources associated with the specified
+ context.
+
+ Format
+
+ Status = LSRelease( [in] LicenseHandle, [in] TotUnitsConsumed,
+ [in] LogComment );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG TotUnitsConsumed;
+
+ LS_STR * LogComment;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ TotUnitsConsumedThe TOTAL number of units consumed in this
+ handle context since the initial LSRequest() call.
+ The software publisher may choose to specify this
+ policy attribute within the application. A value
+ of LS_DEFAULT_UNITS indicates that the licensing
+ system should determine the appropriate value
+ using its own licensing policy mechanisms.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ This function is used to release licensing resources
+ associated with the license context identified by
+ LicenseHandle. If a consumptive style licensing policy is in
+ effect, and if the software publisher chooses to implement
+ such license policy in the application, then the license units
+ to be consumed may be passed as part of this call.
+
+ NOTE: The license handle context is NOT freed. See
+ LSFreeHandle().
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSRelease(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG TotUnitsConsumed,
+ LS_STR FAR *LogComment)
+{
+ return(LS_SUCCESS);
+}
+
+/*
+ LSUpdate()
+ Update the synchronization between licensed software and the
+ licensing system.
+
+ Format
+
+ Status = LSUpdate( [in] LicenseHandle, [in] TotUnitsConsumed,
+ [in] TotUnitsReserved,
+ [in] LogComment, [in/out] Challenge, [out]
+ TotUnitsGranted );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG TotUnitsConsumed;
+
+ LS_ULONG TotUnitsReserved;
+
+ LS_STR * LogComment;
+
+ LS_CHALLENGE *Challenge;
+
+ LS_ULONG * TotUnitsGranted;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ TotUnitsConsumedThe TOTAL number of units consumed so far in
+ this handle context. The software publisher may
+ choose to specify this policy attribute within the
+ application. A value of LS_DEFAULT_UNITS
+ indicates that the licensing system should
+ determine the appropriate value using its own
+ licensing policy mechanisms. If an error is
+ returned, then no units are consumed.
+
+ TotUnitsReserved Specifies the total number of units to be
+ reserved. If no additional units are required
+ since the initial LSRequest() or last LSUpdate(),
+ then this parameter should be the current total
+ (as returned in TotUnitsGranted). The total
+ reserved is inclusive of units consumed. That is,
+ if an application requests 100 units be reserved,
+ then consumes 20 units, there are still 100 units
+ reserved (but only 80 available for consumption).
+
+ If additional units are required, the application
+ must calculate a new total for TotUnitsReserved.
+ LS_DEFAULT_UNITS may be specified, but this will
+ not allocate any additional units.
+
+ The license system verifies that the requested
+ number of units exist and may reserve those units,
+ but these units are not consumed at this time.
+ This value may be smaller than the original
+ request to indicate that fewer units are needed
+ than originally anticipated.
+
+ LogComment An optional string indicating a comment to be
+ associated with the request and logged (if logging
+ is enabled and supported) by the underlying
+ licensing system. The underlying license system
+ may choose to log the comment even if an error is
+ returned (i.e., logged with the error), but this
+ is not guaranteed. If a string is not specified,
+ the value must be LS_NULL.
+
+ Challenge Pointer to a challenge structure. The challenge
+ response will also be returned in this structure.
+ Refer to Challenge Mechanism on page 25 for more
+ information.
+
+ TotUnitsGrantedA pointer to an LS_ULONG in which the total
+ number of units granted since the initial license
+ request is returned. The following table describes
+ the TotUnitsGranted return value, given the
+ TotUnitsReserved input value, and the Status
+ returned:
+
+ LS_INSUFFICIENT_U
+ TotUnitsReserv LS_SUCCES NITS Other
+ ed S errors
+ LS_DEFAULT_UNI (A) (B) (E)
+ TS
+ Other (C) (D) (E)
+ (specific
+ count)
+ (A) The default umber of units commensurate
+ with the license granted. (B) The maximum
+ number of units available to the requesting
+ software. This may be less than the normal
+ default.
+ (C) The number of units used to grant the
+ request. Note that this value may differ from
+ the actual units requested (i.e., the policy
+ may allow only in increments of 5 units, thus a
+ request of 7 units would result in 10 units
+ being granted).
+ (D) The maximum number of units available to
+ the requesting software. This may be more or
+ less than the units requested.
+ (E) Zero is returned.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or that can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ The client application periodically issues this call to re-
+ verify that the current license is still valid. The LSQuery()
+ API may be used to determine the proper interval for the
+ current licensing context. A guideline of once an hour may be
+ appropriate, with a minimum interval of 15 minutes. Consult
+ your licensing system vendor for more information.
+
+ If the number of new units requested (in TotUnitsReserved) is
+ greater than the number available, then the update request
+ fails with an LS_INSUFFICIENT_UNITS error. Upon successful
+ completion, the value returned in TotUnitsGranted indicates
+ the current total of units granted.
+
+ If the TotUnitsConsumed exceeds the number of units reserved,
+ then the error LS_INSUFFICIENT_UNITS is returned. The
+ remaining units are consumed.
+
+ A challenge response is NOT returned if an error is returned.
+
+ The LSUpdate() call verifies that the licensing system context
+ has not changed from that expected by the licensed software.
+ In this way the LSUpdate() call can:
+
+ 1.Determine if the licensing system can verify that the
+ licensing resources granted to the specified handle are
+ still reserved for this application by the licensing system.
+ Note that in distributed license system, an error here might
+ indicate a temporary network interruption, among other
+ things.
+
+ 2.Determine when the licensing system has released the
+ licensing resources that had been granted to the specified
+ handle, indicating the software requiring that grant no
+ longer has authorization to execute normally.
+
+ Application Software should be prepared to handle vendor
+ specific error conditions, should they arise. However, a best
+ effort will be used by license systems to map error conditions
+ to the common error set.
+
+ The LSUpdate() call may indicate if that the current licensing
+ context has expired (for example, in the case of a time-
+ restricted license policy). In such a case, the warning status
+ LS_LICENSE_EXPIRED is returned. If any error is returned, a
+ call to LSRelease() is still required.
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSUpdate(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG TotUnitsConsumed,
+ LS_ULONG TotUnitsReserved,
+ LS_STR FAR *LogComment,
+ LS_CHALLENGE FAR *Challenge,
+ LS_ULONG FAR *TotUnitsGranted)
+{
+ // set the return buffer to NULL
+ if ( TotUnitsGranted != NULL )
+ *TotUnitsGranted = TotUnitsReserved;
+
+ return(LS_SUCCESS);
+}
+
+/*
+ LSGetMessage()
+ Return the message associated with a License Service API
+ status code.
+
+ Format
+
+ Status = LSGetMessage( [in] LicenseHandle, [in] Value, [out]
+ Buffer, [in] BufferSize );
+
+ LS_HANDLE LicenseHandle;
+
+ LS_STATUS_CODEValue;
+
+ LS_STR * Buffer;
+
+ LS_ULONG BufferSize;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ Value Any status code returned by a License Service API
+ function.
+
+ Buffer Pointer to a buffer in which a localized error
+ message string is to be placed.
+
+ BufferSize Maximum size of the string that may be returned in
+ Buffer.
+
+ Status Resulting status of LSGetMessage() call.
+
+ Description
+
+ For a given error, this function returns an error code and a string
+ describing the error, and a suggested action to be taken in
+ response to the specific error. If the value of Value is
+ LS_USE_LAST, then the last error associated with the supplied
+ licensing handle, and its associated data, is returned. Otherwise,
+ the supplied error code is used.
+
+ Possible status codes returned by LSGetMessage() include:
+ LS_SUCCESS, LS_NO_MSG_TEXT, LS_UNKNOWN_STATUS, and
+ LS_BUFFER_TOO_SMALL.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSGetMessage(
+ LS_HANDLE LicenseHandle,
+ LS_STATUS_CODE Value,
+ LS_STR FAR *Buffer,
+ LS_ULONG BufferSize)
+{
+ return(LS_TEXT_UNAVAILABLE);
+}
+
+/*
+ LSQuery()
+ Return information about the license system context associated
+ with the specified handle.
+
+ Format
+
+ Status = LSQuery( [in] LicenseHandle, [in] Information, [out]
+ InfoBuffer, [in] BufferSize,
+ [out] ActualBufferSize);
+
+ LS_HANDLE LicenseHandle;
+
+ LS_ULONG Information;
+
+ LS_VOID * InfoBuffer;
+
+ LS_ULONG BufferSize;
+
+ LS_ULONG * ActualBufferSize;
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ LicenseHandle Handle identifying the license context. This
+ argument must be a handle that was created with
+ LSRequest().
+
+ Information Index which identifies the information to be
+ returned.
+
+ InfoBuffer Points to a buffer in which the resulting
+ information is to be placed.
+
+ BufferSize Maximum size of the buffer pointed to by
+ InfoBuffer.
+
+ ActualBufferSize On entry, points to a LS_ULONG whose value on
+ exit indicates the actual count of characters
+ returned in the buffer (not including the trailing
+ NULL byte).
+
+ Status Detailed error code that can be directly processed
+ by the caller, or which can be converted into a
+ localized message string by the LSGetMessage
+ function.
+
+ Description
+
+ This function is used to obtain information about the license
+ obtained from the LSRequest() call. For example, an application may
+ determine the license type (demo, concurrent, personal, etc.); time
+ restrictions; etc.
+
+ The buffer should be large enough to accommodate the expected data.
+ If the buffer is too small, then the status code
+ LS_BUFFER_TOO_SMALL is returned and only BufferSize bytes of data
+ are returned.
+
+ The following Information constants are defined:
+
+ Information Valu Meaning
+ Constant e
+ LS_INFO_NONE 0 Reserved.
+ LS_INFO_SYSTEM 1 Return the unique identification
+ of the license system supplying
+ the current license context.
+ This is returned as a null-
+ terminated string.
+
+ This value is the same as an
+ appropriate call to
+ LSEnumProviders() provides.
+
+ LS_INFO_DATA 2 Return the block of
+ miscellaneous application data
+ contained on the license. This
+ data is completely vendor-
+ defined. The amount of space
+ allocated for such data will
+ vary from license system to
+ license system, or may not be
+ available at all.
+
+ The first ULONG in the data
+ buffer indicates the size (in
+ bytes) of the actual data which
+ follows:
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | (count of bytes that follow)
+ |
+ +------------------------------
+ --+
+ | Vendor data bytes from license
+ |
+ |
+ |
+ +------------------------------
+ --+
+
+ LS_UPDATE_PERIO 3 Return the recommended interval
+ D (in minutes) at which LSUpdate()
+ should be called.
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Recommended Interval
+ |
+ | (in minutes)
+ |
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Recommended Minutes until
+ |
+ | next LSUpdate()call
+ |
+ +------------------------------
+ --+
+
+ If a value of 0xFFFFFFFF is
+ returned for the recommended
+ interval, then no recommendation
+ is being made.
+
+ LS_LICENSE_CONT 4 Return a value which uniquely
+ EXT identifies the licensing context
+ within the specific license
+ service provider identified by
+ the LicenseHandle.
+
+ +------------------------------
+ --+
+ | ULONG
+ |
+ | Count of Bytes that follow
+ |
+ +------------------------------
+ --+
+ | BYTES
+ |
+ ...
+ |
+ |
+ +------------------------------
+ --+
+
+ The contents of the bytes
+ returned is license system
+ specific. In circumstances where
+ license system specific
+ functionality is being used,
+ this sequence of bytes may be
+ used to identify the current
+ license context.
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSQuery(
+ LS_HANDLE LicenseHandle,
+ LS_ULONG Information,
+ LS_VOID FAR *InfoBuffer,
+ LS_ULONG BufferSize,
+ LS_ULONG FAR *ActualBufferSize)
+{
+ switch ( Information )
+ {
+ case LS_INFO_DATA:
+ case LS_LICENSE_CONTEXT:
+ // set the return buffer to NULL
+ if ( InfoBuffer != NULL )
+ *((LS_ULONG *)InfoBuffer) = 0;
+
+ if ( ActualBufferSize != NULL )
+ *ActualBufferSize = sizeof( LS_ULONG );
+
+ break;
+ case LS_UPDATE_PERIOD:
+ if (( InfoBuffer != NULL ) && ( BufferSize >= sizeof(LS_ULONG)*2 ))
+ {
+ // set the return balue to no recommendation
+ LS_ULONG * uLong = (LS_ULONG*)InfoBuffer;
+ *uLong = 0xffffffff;
+ uLong++;
+ *uLong = 0xffffffff;
+ *ActualBufferSize = sizeof(LS_ULONG) * 2;
+ }
+ break;
+ case LS_INFO_NONE:
+ case LS_INFO_SYSTEM:
+ default:
+ // set return buffer to NULL
+ if ( InfoBuffer != NULL )
+ strcpy( InfoBuffer, (LS_STR*)"");
+
+ if ( ActualBufferSize != NULL )
+ *ActualBufferSize = 0;
+
+ break;
+ }
+ return(LS_SUCCESS);
+}
+
+/*
+ LSEnumProviders()
+ This call is used to enumerate the installed license system
+ service providers.
+
+ Format
+
+ Status = LSEnumProviders( [in] Index, [out] Buffer);
+
+ LS_ULONG Index
+
+ LS_STR * Buffer
+
+ LS_STATUS_CODEStatus;
+
+ Arguments
+
+ Index Index of the service provider. The first provider
+ has an index of zero, the second has an index of
+ one, etc. This index should be incremented by the
+ caller for each successive call to
+ LSEnumProviders() until the status LS_BAD_INDEX is
+ returned.
+
+ Buffer Points to a buffer in which the unique null-
+ terminated string identifying the license system
+ service provider is to be placed. The buffer
+ pointed to by Buffer must be at least 255 bytes
+ long. The value of LS_ANY indicates that the
+ current index is not in use, but is not the last
+ index to obtain.
+
+ Status Detailed error code that can be directly processed
+ by the caller, or which can be converted into a
+ localized message string by the LSGetMessage()
+ function.
+
+ Description
+
+ For each installed provider, a unique string is returned. The
+ unique null-terminated string typically identifies the vendor,
+ product, and version of the license system. This value is the same
+ as an appropriate call to LSQuery(). An Error of LS_BAD_INDEX is
+ returned when the value of Index is higher than the number of
+ providers currently installed. In a networked environment, the
+ version returned is that of the client, not the server.
+
+ An application may enumerate the installed license system service
+ providers by calling LSEnumProviders() successively. The Index is
+ passed in and should be incremented by the caller for each call
+ until the status LS_BAD_INDEX is returned.
+
+*/
+
+LS_STATUS_CODE LS_API_ENTRY NtLSEnumProviders(
+ LS_ULONG Index,
+ LS_STR FAR *Buffer)
+{
+ // set return buffer to NULL
+ if ( Buffer != NULL )
+ strcpy( Buffer, (LS_STR*)"" );
+
+ return(LS_SUCCESS);
+}
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.def b/private/net/svcdlls/lls/ntlsapi/ntlsapi.def
new file mode 100644
index 000000000..ffc93c667
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.def
@@ -0,0 +1,19 @@
+LIBRARY ntlsapi
+
+DESCRIPTION 'Licence System 32 bits API'
+
+CODE LOADONCALL MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+
+HEAPSIZE 1024
+
+EXPORTS
+ NtLSRequest
+ NtLSRelease
+ NtLSFreeHandle
+ NtLSUpdate
+ NtLSGetMessage
+ NtLSQuery
+ NtLSEnumProviders
+ NtLicenseRequestA
+ NtLicenseRequestW
diff --git a/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc b/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc
new file mode 100644
index 000000000..e9a3d07bf
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/ntlsapi.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server Interface DLL"
+
+#define VER_INTERNALNAME_STR "ntlsapi.dll"
+#define VER_ORIGINALFILENAME_STR "ntlsapi.dll"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/ntlsapi/rpcstub.c b/private/net/svcdlls/lls/ntlsapi/rpcstub.c
new file mode 100644
index 000000000..11614c448
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/rpcstub.c
@@ -0,0 +1,399 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ rpcstub.c
+
+Abstract:
+
+ License Logging Service client stubs.
+
+Author:
+
+ Arthur Hanson (arth) 06-Dec-1994
+
+Environment: User mode only.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <string.h>
+#include <zwapi.h>
+#include <llsconst.h>
+
+#include <debug.h>
+#include "lsapi_c.h"
+
+
+// #define API_TRACE 1
+
+BOOLEAN LLSUp = FALSE;
+
+#define MAX_EXPECTED_SID_LENGTH 72
+
+LPTSTR pszStringBinding = NULL;
+RTL_CRITICAL_SECTION LPCInitLock;
+
+static HANDLE LpcPortHandle = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSReInitLPC( )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LLS server and initializes the LPC port.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ RPC_STATUS Status = STATUS_SUCCESS;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+
+ if (LLSUp) {
+ LLSUp = FALSE;
+
+ if (pszStringBinding != NULL) {
+ Status = RpcStringFree(&pszStringBinding);
+ pszStringBinding = NULL;
+ }
+
+ if (Status == STATUS_SUCCESS) {
+
+ if (lsapirpc_handle != NULL) {
+ Status = RpcBindingFree(&lsapirpc_handle);
+ }
+
+ lsapirpc_handle = NULL;
+ }
+
+ }
+
+ try {
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ }
+ except (TRUE) {
+ Status = RpcExceptionCode();
+ }
+
+ if(Status) {
+#if DBG
+ dprintf(TEXT("NTLSAPI RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ // Bind using the created string binding...
+ try {
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &lsapirpc_handle);
+ }
+ except (TRUE) {
+ Status = RpcExceptionCode();
+ }
+
+ if(Status) {
+#if DBG
+ dprintf(TEXT("NTLSAPI RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ lsapirpc_handle = NULL;
+
+ return I_RpcMapWin32Status(Status);
+ }
+
+ LLSUp = TRUE;
+
+ return I_RpcMapWin32Status(Status);
+
+} // LLSReInitLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSInitLPC( )
+
+/*++
+
+Routine Description:
+
+ This service connects to the LLS server and initializes the LPC port.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ RtlInitializeCriticalSection(&LPCInitLock);
+
+ lsapirpc_handle = NULL;
+ Status = LLSReInitLPC();
+ return Status;
+
+} // LLSInitLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSCloseLPC( )
+
+/*++
+
+Routine Description:
+
+ This closes the LPC port connection to the service.
+
+Arguments:
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+--*/
+
+{
+ RPC_STATUS Status = STATUS_SUCCESS;
+
+ RtlEnterCriticalSection(&LPCInitLock);
+ LLSUp = FALSE;
+
+ if (pszStringBinding != NULL) {
+ Status = RpcStringFree(&pszStringBinding);
+ pszStringBinding = NULL;
+ }
+
+ if (Status == STATUS_SUCCESS) {
+
+ if (lsapirpc_handle != NULL) {
+ Status = RpcBindingFree(&lsapirpc_handle);
+ }
+
+ lsapirpc_handle = NULL;
+ }
+
+ RtlLeaveCriticalSection(&LPCInitLock);
+ return Status;
+
+} // LLSCloseLPC
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSLicenseRequest (
+ IN LPWSTR ProductName,
+ IN LPWSTR Version,
+ IN ULONG DataType,
+ IN BOOLEAN IsAdmin,
+ IN PVOID Data,
+ OUT PULONG LicenseHandle
+ )
+
+/*++
+
+Arguments:
+
+ ProductName -
+
+ Version -
+
+ DataType -
+
+ IsAdmin -
+
+ Data -
+
+ LicenseHandle -
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+Routine Description:
+
+
+--*/
+
+{
+ WCHAR ProductID[MAX_PRODUCT_NAME_LENGTH + MAX_VERSION_LENGTH + 2];
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOL Close = FALSE;
+ ULONG VersionIndex;
+ ULONG Size = 0;
+ ULONG i;
+
+ //
+ // Get this out of the way in-case anything goes wrong
+ //
+ *LicenseHandle = (ULONG) 0xFFFFFFFFL;
+
+ //
+ // If LicenseService isn't running (no LPC port) then just return
+ // dummy info - and let the user on.
+ //
+ RtlEnterCriticalSection(&LPCInitLock);
+ if (!LLSUp)
+ Status = LLSReInitLPC();
+ RtlLeaveCriticalSection(&LPCInitLock);
+
+ if (!NT_SUCCESS(Status))
+ return STATUS_SUCCESS;
+
+ if (((i = lstrlen(ProductName)) > MAX_PRODUCT_NAME_LENGTH) || (lstrlen(Version) > MAX_VERSION_LENGTH))
+ return STATUS_SUCCESS;
+
+ //
+ // Create productID - product name + version string.
+ //
+ lstrcpy(ProductID, ProductName);
+ lstrcat(ProductID, TEXT(" "));
+ lstrcat(ProductID, Version);
+
+ VersionIndex = i;
+
+ //
+ // Based on DataType figure out if we are doing a name or a SID
+ // and copy the data appropriatly
+ //
+ if (DataType == NT_LS_USER_NAME) {
+ Size = lstrlen((LPWSTR) Data);
+ if (Size > MAX_USER_NAME_LENGTH)
+ return STATUS_SUCCESS;
+
+ Size = (Size + 1) * sizeof(TCHAR);
+ }
+
+ if (DataType == NT_LS_USER_SID) {
+ //
+ // Friggin SID, so need to copy it manually.
+ // WARNING: This makes it dependent on the structure of the
+ // SID!!!
+ //
+ Size = RtlLengthSid( (PSID) Data);
+
+ if (Size > MAX_EXPECTED_SID_LENGTH)
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Call the Server.
+ //
+ try {
+ Status = LlsrLicenseRequestW(
+ LicenseHandle,
+ ProductID,
+ VersionIndex,
+ IsAdmin,
+ DataType,
+ Size,
+ (PBYTE) Data );
+ }
+ except (TRUE) {
+#if DBG
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+ if (Status != RPC_NT_SERVER_UNAVAILABLE) {
+ dprintf(TEXT("ERROR NTLSAPI.DLL: RPC Exception: 0x%lX\n"), Status);
+// ASSERT(FALSE);
+ }
+#endif
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if (Close)
+ LLSCloseLPC();
+
+ return Status;
+
+} // LLSLicenseRequest
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSLicenseFree (
+ IN ULONG LicenseHandle
+ )
+
+/*++
+
+Arguments:
+
+ LicenseHandle -
+
+Return Status:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+ BOOL Close = FALSE;
+ NTSTATUS Status;
+
+ //
+ // If LicenseService isn't running (no LPC port) then just return
+ // dummy info - and let the user on.
+ //
+ if (!LLSUp)
+ return STATUS_SUCCESS;
+
+
+ //
+ // Call the Server.
+ //
+ try {
+ Status = LlsrLicenseFree( (DWORD) LicenseHandle );
+ }
+ except (TRUE) {
+#if DBG
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+ if (Status != RPC_NT_SERVER_UNAVAILABLE) {
+ dprintf(TEXT("ERROR NTLSAPI.DLL: RPC Exception: 0x%lX\n"), Status);
+// ASSERT(FALSE);
+ }
+#endif
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if (Close)
+ LLSCloseLPC();
+
+ return Status;
+} // LLSLicenseFree
diff --git a/private/net/svcdlls/lls/ntlsapi/sources b/private/net/svcdlls/lls/ntlsapi/sources
new file mode 100644
index 000000000..21ce312f3
--- /dev/null
+++ b/private/net/svcdlls/lls/ntlsapi/sources
@@ -0,0 +1,66 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntlsapi
+MINORCOMP=
+
+TARGETNAME=ntlsapi
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+
+DLLENTRY=DllMain
+
+SDKINC=$(BASEDIR)\public\sdk\inc
+PRIVINC=$(BASEDIR)\private\inc
+WINSINC=$(BASEDIR)\private\net\sockets\wins\server\server\inc
+WINSMSGINC=$(BASEDIR)\private\net\sockets\wins\server\server\msg
+TARGETLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+INCLUDES=$(PRIVINC);$(WINSINC);$(WINSMSGINC);$(SDKINC);..\inc
+
+USE_CRTDLL=1
+
+SOURCES= \
+ ntlsapi.c \
+ rpcstub.c \
+ lsapi_c.c \
+ main.c \
+ ntlsapi.rc
+
+C_DEFINES=-DINCL_32 -DNT -DWIN32 -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+UMTYPE=console
+UMLIBS= \
+ ..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
diff --git a/private/net/svcdlls/lls/server/certdb.c b/private/net/svcdlls/lls/server/certdb.c
new file mode 100644
index 000000000..e543ad203
--- /dev/null
+++ b/private/net/svcdlls/lls/server/certdb.c
@@ -0,0 +1,1511 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ certdb.c
+
+Abstract:
+
+ License Logging Service certificate database implementation. This database
+ tracks license certificates to help ensure that no more licenses from a
+ single certificate are installe don the license enterprise than are allowed
+ by the certificate's license agreement.
+
+ The certificate database at the top level is an unsorted array of
+ certificate headers. There is exactly one header per unique certificate.
+ A unique certificate is identified by a combination of product name,
+ certificate ID, certificate capacity (max. licenses legally installable),
+ and expiration date.
+
+ Each header has an attached array of certificate claims. There is exactly
+ one claim per machine that (a) replicates to this machine, directly or
+ indirectly, and (b) has licenses from this certificate installed. Each
+ claim contains the server name to which it corresponds, the number of
+ licenses installed on it, and the date this information was replicated.
+ If a claim is not updated after LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
+ seconds (3 days as of this writing), the claim is considered forfeit and
+ is erased.
+
+Author:
+
+ Jeff Parham (jeffparh) 08-Dec-1995
+
+Revision History:
+
+--*/
+
+
+#include <stdlib.h>
+#include <limits.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <rpc.h>
+#include <rpcndr.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "llsapi.h"
+#include "llsevent.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+#include "purchase.h"
+#include "registry.h"
+
+
+RTL_RESOURCE CertDbHeaderListLock;
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList = NULL;
+DWORD CertDbHeaderListSize = 0;
+HANDLE CertDbFile = NULL;
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbClaimEnter( LPTSTR pszServerName,
+ PLLS_LICENSE_INFO_1 pLicense,
+ BOOL bIsTotal,
+ DWORD ReplicationDate )
+
+/*++
+
+Routine Description:
+
+ Enter a claim into the database.
+
+Arguments:
+
+ pszServerName (LPTSTR)
+ The server for which to enter this claim. A value of NULL indicates the
+ local server.
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information to enter into the database.
+ bIsTotal (BOOL)
+ If TRUE, indicates that this license information represents the total
+ licenses installed on the machine and should therefore replace the
+ current claim (if any). Otherwise, indicates this license information
+ should be added to the current claim (if any).
+ ReplicationDate (DWORD)
+ Indicates the date which this information was last replicated. A value
+ of 0 will be replaced with the current system time.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_INVALID_PARAMETER
+ STATUS_INVALID_COMPUTER_NAME
+ STATUS_NO_MEMORY
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ if ( ( NULL == pLicense ) || ( 0 == pLicense->CertificateID ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ if ( NULL == pszServerName )
+ {
+ // use local server name
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ pszServerName = szComputerName;
+ }
+ }
+ else
+ {
+ // remove leading backslashes (if any) from server name
+ while ( TEXT('\\') == *pszServerName )
+ {
+ pszServerName++;
+ }
+ }
+
+ if ( ( NULL == pszServerName ) || !*pszServerName || ( lstrlen( pszServerName ) > MAX_COMPUTERNAME_LENGTH ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_INVALID_COMPUTER_NAME;
+ }
+ else
+ {
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ // is the certificate in the db?
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // certificate not yet in db; add it
+ pHeader = CertDbHeaderAdd( pLicense );
+ }
+
+ if ( NULL == pHeader )
+ {
+ // could not find or add header
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // now have header; is this claim already filed?
+ int iClaim;
+
+ iClaim = CertDbClaimFind( pHeader, pszServerName );
+
+ if ( iClaim < 0 )
+ {
+ // claim does not yet exist; add it
+ if ( NULL == pHeader->Claims )
+ {
+ pHeader->Claims = LocalAlloc( LPTR, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
+ }
+ else
+ {
+ pHeader->Claims = LocalReAlloc( pHeader->Claims, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ), LHND );
+ }
+
+ if ( NULL == pHeader->Claims )
+ {
+ // memory allocation failed
+ pHeader->NumClaims = 0;
+ }
+ else
+ {
+ // claim list expanded; save server name
+ iClaim = pHeader->NumClaims;
+ lstrcpy( pHeader->Claims[ iClaim ].ServerName, pszServerName );
+ pHeader->Claims[ iClaim ].Quantity = 0;
+
+ pHeader->NumClaims++;
+ }
+ }
+
+ if ( iClaim < 0 )
+ {
+ // could not find or add claim to header
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // claim found or added; update info
+ ASSERT( !lstrcmpi( pszServerName, pHeader->Claims[ iClaim ].ServerName ) );
+ pHeader->Claims[ iClaim ].ReplicationDate = ReplicationDate ? ReplicationDate : DateSystemGet();
+
+ if ( bIsTotal )
+ {
+ // the given value is the new total
+ pHeader->Claims[ iClaim ].Quantity = pLicense->Quantity;
+ nt = STATUS_SUCCESS;
+ }
+ else if ( pHeader->Claims[ iClaim ].Quantity + pLicense->Quantity >= 0 )
+ {
+ // the given value is added to the current sum to make the total
+ pHeader->Claims[ iClaim ].Quantity += pLicense->Quantity;
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ // overflow
+ nt = STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ // any product that has licenses with non-0 certificate IDs
+ // must be secure; this code is here such that when certificates
+ // are replicated, the "product is secure" info is replicated, too
+ // this will also help recover from the case where someone deletes
+ // the registry key that lists all secure products
+ ServiceSecuritySet( pLicense->Product );
+ }
+ }
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+BOOL CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Check to see if adding the given licenses is legal. This call is typically
+ made before adding a license into the system to verify that doing so does
+ not violate the certificate's license agreement.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which approval is sought.
+
+Return Value:
+
+ TRUE (approved) or FALSE (rejected).
+
+--*/
+
+{
+ BOOL bOkToAdd = TRUE;
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ if ( ( pLicense->Quantity > 0 ) && ( (DWORD)pLicense->Quantity > pLicense->MaxQuantity ) )
+ {
+ // certificate add request exceeds its capacity all by itself!
+ bOkToAdd = FALSE;
+ }
+ else
+ {
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( !ok )
+ {
+ // deletions will fail...
+ *szComputerName = TEXT( '\0' );
+ }
+
+ // do we have a record of this certificate?
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // don't have any record of this certificate; ok to add if Quantity > 0
+ bOkToAdd = ( pLicense->Quantity > 0 );
+ }
+ else
+ {
+ LONG lTotalQuantity = 0;
+ int iClaim;
+
+ // we have seen this certificate; are there enough licenses available?
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ // for license remove requests, tally only local licenses
+ // for license add requests, tally all licenses
+ if ( ( ( pLicense->Quantity > 0 )
+ || ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, szComputerName ) ) )
+ && ( lTotalQuantity + pHeader->Claims[ iClaim ].Quantity >= 0 ) )
+ {
+ // add to tally
+ lTotalQuantity += pHeader->Claims[ iClaim ].Quantity;
+ }
+ }
+
+ if ( lTotalQuantity + pLicense->Quantity < 0 )
+ {
+ // overflow or underflow
+ bOkToAdd = FALSE;
+ }
+ else if ( (DWORD)(lTotalQuantity + pLicense->Quantity) > pHeader->MaxQuantity )
+ {
+ // exceeds certificate capacity
+ bOkToAdd = FALSE;
+ }
+ else
+ {
+ // okay by me
+ bOkToAdd = TRUE;
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+
+ return bOkToAdd;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Find a certificate header in the database.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which to find the appropriate header.
+
+Return Value:
+
+ A pointer to the found header, or NULL if not found.
+
+--*/
+
+{
+ // assumes db is already locked for shared or exclusive access
+
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader = NULL;
+ int iHeader;
+
+ for ( iHeader=0; ( NULL == pHeader ) && ( (DWORD)iHeader < CertDbHeaderListSize ); iHeader++ )
+ {
+ if ( ( CertDbHeaderList[ iHeader ].CertificateID == pLicense->CertificateID )
+ && ( CertDbHeaderList[ iHeader ].MaxQuantity == pLicense->MaxQuantity )
+ && ( CertDbHeaderList[ iHeader ].ExpirationDate == pLicense->ExpirationDate )
+ && ( !lstrcmpi( CertDbHeaderList[ iHeader ].Product, pLicense->Product ) ) )
+ {
+ // header found!
+ pHeader = &CertDbHeaderList[ iHeader ];
+ }
+ }
+
+ return pHeader;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense )
+
+/*++
+
+Routine Description:
+
+ Add a certificate header to the database.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License information for which to add the header.
+
+Return Value:
+
+ A pointer to the added header, or NULL if memory could not be allocated.
+
+--*/
+
+{
+ // assumes caller has made sure the header does not already exist
+ // assumes db is locked for exclusive access
+
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+
+ if ( CertDbHeaderListSize )
+ {
+ CertDbHeaderList = LocalReAlloc( CertDbHeaderList, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ), LHND );
+ }
+ else
+ {
+ CertDbHeaderList = LocalAlloc( LPTR, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ) );
+ }
+
+ if ( NULL == CertDbHeaderList )
+ {
+ // memory allocation failed; bye-bye database!
+ ASSERT( FALSE );
+ CertDbHeaderListSize = 0;
+ pHeader = NULL;
+ }
+ else
+ {
+ // allocate space for product name
+ CertDbHeaderList[ CertDbHeaderListSize ].Product = LocalAlloc( LPTR, sizeof( TCHAR ) * ( 1 + lstrlen( pLicense->Product ) ) );
+
+ if ( NULL == CertDbHeaderList[ CertDbHeaderListSize ].Product )
+ {
+ // memory allocation failed
+ ASSERT( FALSE );
+ pHeader = NULL;
+ }
+ else
+ {
+ // success!
+ pHeader = &CertDbHeaderList[ CertDbHeaderListSize ];
+ CertDbHeaderListSize++;
+
+ lstrcpy( pHeader->Product, pLicense->Product );
+ pHeader->CertificateID = pLicense->CertificateID;
+ pHeader->MaxQuantity = pLicense->MaxQuantity;
+ pHeader->ExpirationDate = pLicense->ExpirationDate;
+ }
+ }
+
+ return pHeader;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+int CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader, LPTSTR pszServerName )
+
+/*++
+
+Routine Description:
+
+ Find a certificate claim for a specific server in the claim list.
+
+Arguments:
+
+ pHeader (PLLS_CERT_DB_CERTIFICATE_HEADER)
+ Header containing the claim list to search.
+ pszServerName (LPTSTR)
+ Name of the server for which the claim is sought.
+
+Return Value:
+
+ The index of the found claim, or -1 if not found.
+
+--*/
+
+{
+ // assumes db is already locked for shared or exclusive access
+
+ int iClaim;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ if ( !lstrcmpi( pHeader->Claims[ iClaim ].ServerName, pszServerName ) )
+ {
+ break;
+ }
+ }
+
+ if ( (DWORD)iClaim >= pHeader->NumClaims )
+ {
+ iClaim = -1;
+ }
+
+ return iClaim;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbPrune()
+
+/*++
+
+Routine Description:
+
+ Remove entries in the database which have expired. Entries expire if they
+ have not been re-replicated in LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX
+ seconds (3 days as of this writing).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ DWORD CurrentDate;
+ DWORD MinimumDate;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ CurrentDate = DateSystemGet();
+ MinimumDate = CurrentDate - LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX;
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
+ {
+ // Note that we prune entries made in the future, too, to avoid having an incorrect date
+ // forcing us to keep an entry forever.
+ //
+ // For this application, it's better to keep fewer entries rather than more, as the
+ // fewer entries we have, the less restrictive the system is.
+ //
+ // Don't prune local entries.
+
+ if ( ( ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate < MinimumDate )
+ || ( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate > CurrentDate ) )
+ && lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
+ {
+ // remove claim
+ MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
+ &CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
+ CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
+
+ CertDbHeaderList[ iHeader ].NumClaims--;
+ }
+ else
+ {
+ // keep this claim
+ iClaim++;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbRemoveLocalClaims()
+
+/*++
+
+Routine Description:
+
+ Remove entries in the database corresponding to the local server.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ] = TEXT("");
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( ok )
+ {
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; )
+ {
+ if ( !lstrcmpi( szComputerName, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName ) )
+ {
+ // remove claim
+ MoveMemory( &CertDbHeaderList[ iHeader ].Claims[ iClaim ],
+ &CertDbHeaderList[ iHeader ].Claims[ iClaim+1 ],
+ CertDbHeaderList[ iHeader ].NumClaims - ( iClaim + 1 ) );
+
+ CertDbHeaderList[ iHeader ].NumClaims--;
+ }
+ else
+ {
+ // keep this claim
+ iClaim++;
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbLogViolations()
+
+/*++
+
+Routine Description:
+
+ Log violations of certificate license agreements to the event log of the
+ local server.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+ HANDLE hEventLog;
+ DWORD dwTotalQuantity;
+ HINSTANCE hDll;
+ DWORD cch;
+ LPTSTR pszViolationServerEntryFormat;
+ LPTSTR pszViolationFormat;
+ LPTSTR pszViolationServerEntryList;
+ LPTSTR pszNextViolationServerEntry;
+ TCHAR szNumLicenses[ 20 ];
+ TCHAR szMaxLicenses[ 20 ];
+ TCHAR szCertificateID[ 20 ];
+ LPTSTR apszSubstStrings[ 4 ];
+ DWORD cbViolationServerList;
+ LPTSTR pszViolationServerList;
+
+ // get rid of out-dated entries
+ CertDbPrune();
+
+ hDll = LoadLibrary( TEXT( "LLSRPC.DLL" ) );
+ ASSERT( NULL != hDll );
+
+ if ( NULL != hDll )
+ {
+ // format for part of logged message that lists server and #licenses
+ cch = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_FROM_HMODULE,
+ hDll,
+ LLS_EVENT_CERT_VIOLATION_SERVER_ENTRY,
+ GetSystemDefaultLangID(),
+ (LPVOID) &pszViolationServerEntryFormat,
+ 0,
+ NULL );
+ ASSERT( 0 != cch );
+
+ if ( 0 != cch )
+ {
+ hEventLog = RegisterEventSource( NULL, TEXT("LicenseService") );
+
+ if ( NULL != hEventLog )
+ {
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ dwTotalQuantity = 0;
+
+ // tally the number of licenses claimed against this certificate
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ if ( dwTotalQuantity + (DWORD)CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity < dwTotalQuantity )
+ {
+ // overflow!
+ dwTotalQuantity = ULONG_MAX;
+ break;
+ }
+ else
+ {
+ // add to tally
+ dwTotalQuantity += CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity;
+ }
+ }
+
+ if ( dwTotalQuantity > CertDbHeaderList[ iHeader ].MaxQuantity )
+ {
+ // this certificate is in violation
+
+ // create message we're going to log
+ cbViolationServerList = CertDbHeaderList[ iHeader ].NumClaims
+ * sizeof( TCHAR )
+ * ( lstrlen( pszViolationServerEntryFormat )
+ + 20
+ + MAX_COMPUTERNAME_LENGTH );
+ pszViolationServerList = LocalAlloc( LPTR, cbViolationServerList );
+ ASSERT( NULL != pszViolationServerList );
+
+ if ( NULL != pszViolationServerList )
+ {
+ // create an entry for each server in violation, stringing them
+ // together in pszViolationServerList
+ pszNextViolationServerEntry = pszViolationServerList;
+
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ _ltow( CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity, szNumLicenses, 10 );
+
+ apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName;
+ apszSubstStrings[ 1 ] = szNumLicenses;
+
+ cch = FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ pszViolationServerEntryFormat,
+ 0,
+ 0,
+ pszNextViolationServerEntry,
+ cbViolationServerList - ( pszNextViolationServerEntry - pszViolationServerList ),
+ (LPVOID) apszSubstStrings );
+ ASSERT( 0 != cch );
+
+ pszNextViolationServerEntry += lstrlen( pszNextViolationServerEntry );
+ }
+
+ _ultow( CertDbHeaderList[ iHeader ].CertificateID, szCertificateID, 10 );
+ _ultow( dwTotalQuantity, szNumLicenses, 10 );
+ _ultow( CertDbHeaderList[ iHeader ].MaxQuantity, szMaxLicenses, 10 );
+
+ apszSubstStrings[ 0 ] = CertDbHeaderList[ iHeader ].Product;
+ apszSubstStrings[ 1 ] = szCertificateID;
+ apszSubstStrings[ 2 ] = szNumLicenses;
+ apszSubstStrings[ 3 ] = szMaxLicenses;
+ apszSubstStrings[ 4 ] = pszViolationServerList;
+
+ // log the violation
+ if ( NULL != hEventLog )
+ {
+ ReportEvent( hEventLog,
+ EVENTLOG_ERROR_TYPE,
+ 0,
+ LLS_EVENT_CERT_VIOLATION,
+ NULL,
+ 5,
+ 0,
+ apszSubstStrings,
+ NULL );
+ }
+
+ LocalFree( pszViolationServerList );
+ }
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ LocalFree( pszViolationServerEntryFormat );
+
+ DeregisterEventSource( hEventLog );
+ }
+ }
+
+ FreeLibrary( hDll );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbPack( LPDWORD pcchProductStrings,
+ LPTSTR * ppchProductStrings,
+ LPDWORD pdwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders,
+ LPDWORD pdwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims )
+
+/*++
+
+Routine Description:
+
+ Pack the certificate database into manageable chunks that can be saved or
+ replicated.
+
+Arguments:
+
+ pcchProductStrings (LPDWORD)
+ On return, holds the size (in characters) of the buffer pointed to by
+ *ppchProductStrings.
+ ppchProductStrings (LPTSTR *)
+ On return, points to the buffer containing the product strings component
+ of the database.
+ pdwNumHeaders (LPDWORD)
+ On return, holds the number of certificate headers in the array pointed
+ to by *ppHeaders.
+ ppHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0 *)
+ On return, holds a pointer to the certificate header array component of
+ the database.
+ pdwNumClaims (LPDWORD)
+ On return, holds the number of certificate claims in the array pointed
+ to by *ppHeaders.
+ ppClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0 *)
+ On return, holds a pointer to the certificate claim array component of
+ the database.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_NO_MEMORY.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+
+ DWORD cchProductStrings = 0;
+ LPTSTR pchProductStrings = NULL;
+ DWORD dwNumHeaders = 0;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ DWORD dwNumClaims = 0;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+
+ LPTSTR pchNextProductString;
+ int iHeader;
+ int iClaim;
+
+ CertDbPrune();
+ CertDbUpdateLocalClaims();
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ if ( 0 != CertDbHeaderListSize )
+ {
+ // how big are all of our strings put together?
+ // hom many certificate claims are there?
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ cchProductStrings += 1 + lstrlen( CertDbHeaderList[ iHeader ].Product );
+ dwNumClaims += CertDbHeaderList[ iHeader ].NumClaims;
+ }
+ dwNumHeaders = CertDbHeaderListSize;
+
+ pchProductStrings = LocalAlloc( LMEM_FIXED, cchProductStrings * sizeof( TCHAR ) );
+ pHeaders = LocalAlloc( LMEM_FIXED, dwNumHeaders * sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) );
+ pClaims = LocalAlloc( LMEM_FIXED, dwNumClaims * sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) );
+
+ if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ // pack the product strings
+ pchNextProductString = pchProductStrings;
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ lstrcpy( pchNextProductString, CertDbHeaderList[ iHeader ].Product );
+ pchNextProductString += 1 + lstrlen( pchNextProductString );
+ }
+
+ // now pack away the rest of our structures
+ iClaim = 0;
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ pHeaders[ iHeader ].CertificateID = CertDbHeaderList[ iHeader ].CertificateID;
+ pHeaders[ iHeader ].MaxQuantity = CertDbHeaderList[ iHeader ].MaxQuantity;
+ pHeaders[ iHeader ].ExpirationDate = CertDbHeaderList[ iHeader ].ExpirationDate;
+ pHeaders[ iHeader ].NumClaims = CertDbHeaderList[ iHeader ].NumClaims;
+
+ if ( CertDbHeaderList[ iHeader ].NumClaims )
+ {
+ memcpy( &pClaims[ iClaim ],
+ CertDbHeaderList[ iHeader ].Claims,
+ CertDbHeaderList[ iHeader ].NumClaims * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) );
+
+ iClaim += CertDbHeaderList[ iHeader ].NumClaims;
+ }
+ }
+
+ // all done!
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ *pcchProductStrings = cchProductStrings;
+ *ppchProductStrings = pchProductStrings;
+
+ *pdwNumHeaders = dwNumHeaders;
+ *ppHeaders = pHeaders;
+
+ *pdwNumClaims = dwNumClaims;
+ *ppClaims = pClaims;
+ }
+ else
+ {
+ if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
+ if ( NULL != pHeaders ) LocalFree( pHeaders );
+ if ( NULL != pClaims ) LocalFree( pClaims );
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbUnpack( DWORD cchProductStrings,
+ LPTSTR pchProductStrings,
+ DWORD dwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders,
+ DWORD dwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims,
+ BOOL bReplicated )
+
+/*++
+
+Routine Description:
+
+ Pack the certificate database into manageable chunks that can be saved or
+ replicated.
+
+Arguments:
+
+ cchProductStrings (DWORD)
+ The size (in characters) of the buffer pointed to by pchProductStrings.
+ pchProductStrings (LPTSTR)
+ The buffer containing the product strings component of the database.
+ dwNumHeaders (DWORD)
+ The number of certificate headers in the array pointed to by pHeaders.
+ pHeaders (PREPL_CERT_DB_CERTIFICATE_HEADER_0)
+ The certificate header array component of the database.
+ dwNumClaims (DWORD)
+ The number of certificate claims in the array pointed to by pHeaders.
+ pClaims (PREPL_CERT_DB_CERTIFICATE_CLAIM_0)
+ The certificate claim array component of the database.
+ bReplicated (BOOL)
+ Indicates whether this information was replicated. This is used to
+ determine the time at which this information will expire.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ LPTSTR pchNextProductString;
+ LPBYTE pb;
+ int iHeader;
+ int iClaim;
+ int iClaimBase;
+ LLS_LICENSE_INFO_1 lic;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD cchComputerName = sizeof( szComputerName ) / sizeof( *szComputerName );
+ BOOL ok;
+
+ ok = GetComputerName( szComputerName, &cchComputerName );
+ ASSERT( ok );
+
+ if ( !ok )
+ {
+ // in this case, we'll just add in the local entries, too
+ // under normal circumstances (i.e., as long as the cert db isn't corrupt),
+ // this is harmless and is preferrable to failing to unpack
+ *szComputerName = TEXT( '\0' );
+ }
+
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ pchNextProductString = pchProductStrings;
+
+ // these fields are irrelevant!
+ lic.Date = 0;
+ lic.Admin = NULL;
+ lic.Comment = NULL;
+ lic.Vendor = NULL;
+ lic.Source = NULL;
+ lic.AllowedModes = 0;
+
+ iClaimBase = 0;
+ for ( iHeader=0; (DWORD)iHeader < dwNumHeaders; iHeader++ )
+ {
+ if ( 0 != pHeaders[ iHeader ].NumClaims )
+ {
+ // certificate-specific fields
+ lic.Product = pchNextProductString;
+ lic.CertificateID = pHeaders[ iHeader ].CertificateID;
+ lic.MaxQuantity = pHeaders[ iHeader ].MaxQuantity;
+ lic.ExpirationDate = pHeaders[ iHeader ].ExpirationDate;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeaders[ iHeader ].NumClaims; iClaim++ )
+ {
+ if ( lstrcmpi( szComputerName, pClaims[ iClaimBase + iClaim ].ServerName ) )
+ {
+ // not the local server
+
+ // claim-specific field
+ lic.Quantity = pClaims[ iClaimBase + iClaim ].Quantity;
+
+ nt = CertDbClaimEnter( pClaims[ iClaimBase + iClaim ].ServerName, &lic, TRUE, bReplicated ? 0 : pClaims[ iClaimBase + iClaim ].ReplicationDate );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ // even if we encounter an error, go ahead and unpack the rest of the records
+ }
+ }
+
+ iClaimBase += pHeaders[ iHeader ].NumClaims;
+ }
+
+ pchNextProductString += 1 + lstrlen( pchNextProductString );
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbSave()
+
+/*++
+
+Routine Description:
+
+ Save the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS, Windows error, or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+ LLS_CERT_DB_FILE_HEADER FileHeader;
+ DWORD cchProductStrings = 0;
+ LPTSTR pchProductStrings = NULL;
+ DWORD dwNumHeaders = 0;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ DWORD dwNumClaims = 0;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+ DWORD dwBytesWritten;
+ BOOL ok;
+
+ nt = CertDbPack( &cchProductStrings, &pchProductStrings, &dwNumHeaders, &pHeaders, &dwNumClaims, &pClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( dwNumHeaders )
+ {
+ nt = EBlock( pchProductStrings, cchProductStrings * sizeof( TCHAR ) );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = EBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * dwNumHeaders );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = EBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * dwNumClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ if ( NULL != CertDbFile )
+ {
+ CloseHandle( CertDbFile );
+ }
+
+ CertDbFile = LlsFileInit( CertDbFileName, LLS_CERT_DB_FILE_VERSION, sizeof( LLS_CERT_DB_FILE_HEADER ) );
+
+ if ( NULL == CertDbFile )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ FileHeader.NumCertificates = dwNumHeaders;
+ FileHeader.ProductStringSize = cchProductStrings;
+ FileHeader.NumClaims = dwNumClaims;
+
+ ok = WriteFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesWritten, NULL );
+
+ if ( ok )
+ {
+ ok = WriteFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesWritten, NULL );
+ }
+ }
+ }
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ }
+ }
+ }
+ }
+
+ LocalFree( pchProductStrings );
+ LocalFree( pHeaders );
+ LocalFree( pClaims );
+ }
+ }
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ LogEvent( LLS_EVENT_SAVE_CERT_DB, 0, NULL, nt );
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbLoad()
+
+/*++
+
+Routine Description:
+
+ Load the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS, Windows error, or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt = STATUS_SUCCESS;
+ DWORD dwVersion;
+ DWORD dwDataSize;
+ LLS_CERT_DB_FILE_HEADER FileHeader;
+ LPTSTR pchProductStrings = NULL;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders = NULL;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims = NULL;
+ DWORD dwBytesRead;
+ BOOL ok;
+
+ if ( NULL != CertDbFile )
+ {
+ CloseHandle( CertDbFile );
+ CertDbFile = NULL;
+ }
+
+ if ( FileExists( CertDbFileName ) )
+ {
+ CertDbFile = LlsFileCheck( CertDbFileName, &dwVersion, &dwDataSize );
+
+ if ( NULL == CertDbFile )
+ {
+ nt = GetLastError();
+ }
+ else if ( ( LLS_CERT_DB_FILE_VERSION != dwVersion )
+ || ( sizeof( FileHeader ) != dwDataSize ) )
+ {
+ nt = STATUS_FILE_INVALID;
+ }
+ else
+ {
+ ok = ReadFile( CertDbFile, &FileHeader, sizeof( FileHeader ), &dwBytesRead, NULL );
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else if ( FileHeader.NumCertificates )
+ {
+ pchProductStrings = LocalAlloc( LMEM_FIXED, sizeof( TCHAR ) * FileHeader.ProductStringSize );
+ pHeaders = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
+ pClaims = LocalAlloc( LMEM_FIXED, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
+
+ if ( ( NULL == pchProductStrings ) || ( NULL == pHeaders ) || ( NULL == pClaims ) )
+ {
+ ASSERT( FALSE );
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ ok = ReadFile( CertDbFile, pchProductStrings, FileHeader.ProductStringSize * sizeof( TCHAR ), &dwBytesRead, NULL );
+
+ if ( ok )
+ {
+ ok = ReadFile( CertDbFile, pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates, &dwBytesRead, NULL );
+
+ if ( ok )
+ {
+ ok = ReadFile( CertDbFile, pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims, &dwBytesRead, NULL );
+ }
+ }
+
+ if ( !ok )
+ {
+ nt = GetLastError();
+ }
+ else
+ {
+ nt = DeBlock( pchProductStrings, sizeof( TCHAR ) * FileHeader.ProductStringSize );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = DeBlock( pHeaders, sizeof( REPL_CERT_DB_CERTIFICATE_HEADER_0 ) * FileHeader.NumCertificates );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = DeBlock( pClaims, sizeof( REPL_CERT_DB_CERTIFICATE_CLAIM_0 ) * FileHeader.NumClaims );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = CertDbUnpack( FileHeader.ProductStringSize,
+ pchProductStrings,
+ FileHeader.NumCertificates,
+ pHeaders,
+ FileHeader.NumClaims,
+ pClaims,
+ FALSE );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( NULL != pchProductStrings ) LocalFree( pchProductStrings );
+ if ( NULL != pHeaders ) LocalFree( pHeaders );
+ if ( NULL != pClaims ) LocalFree( pClaims );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ LogEvent( LLS_EVENT_LOAD_CERT_DB, 0, NULL, nt );
+ }
+ else
+ {
+ CertDbPrune();
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbInit()
+
+/*++
+
+Routine Description:
+
+ Initialize the certificate database.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+
+{
+ RtlInitializeResource( &CertDbHeaderListLock );
+
+ CertDbFile = NULL;
+
+ CertDbHeaderList = NULL;
+ CertDbHeaderListSize = 0;
+
+ return STATUS_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+void CertDbUpdateLocalClaims()
+
+/*++
+
+Routine Description:
+
+ Synchronize the certificate database with the purchase history.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD dwPurchaseNdx;
+ LLS_LICENSE_INFO_1 lic;
+ PLICENSE_PURCHASE_RECORD pPurchase;
+
+ RtlAcquireResourceExclusive( &LicenseListLock, TRUE );
+ RtlAcquireResourceExclusive( &CertDbHeaderListLock, TRUE );
+
+ // first dump all current entries for the local server
+ CertDbRemoveLocalClaims();
+
+ // these fields are irrelevant!
+ lic.Date = 0;
+ lic.Admin = NULL;
+ lic.Comment = NULL;
+ lic.Source = NULL;
+ lic.Vendor = NULL;
+ lic.AllowedModes = 0;
+
+ // add in all secure purchases
+ for ( dwPurchaseNdx = 0; dwPurchaseNdx < PurchaseListSize; dwPurchaseNdx++ )
+ {
+ pPurchase = &PurchaseList[ dwPurchaseNdx ];
+
+ if ( 0 != pPurchase->CertificateID )
+ {
+ lic.Product = ( pPurchase->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? pPurchase->Service->ServiceName
+ : pPurchase->PerServerService->ServiceName;
+
+ lic.CertificateID = pPurchase->CertificateID;
+ lic.MaxQuantity = pPurchase->MaxQuantity;
+ lic.ExpirationDate = pPurchase->ExpirationDate;
+ lic.Quantity = pPurchase->NumberLicenses;
+
+ CertDbClaimEnter( NULL, &lic, FALSE, 0 );
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+ RtlReleaseResource( &LicenseListLock );
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense,
+ LPDWORD pdwNumClaims,
+ PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets )
+
+/*++
+
+Routine Description:
+
+ Retrieve a list of all servers with licenses installed from a given
+ certificate and the number of licenses installed on each.
+
+Arguments:
+
+ pLicense (PLLS_LICENSE_INFO_1)
+ License describing the certificate for which the claims are sought.
+ pdwNumClaims (LPDWORD)
+ On return, holds the number of claims in the array pointed to by
+ *ppTargets.
+ ppTargets (PLLS_CERTIFICATE_CLAIM_INFO_0 *)
+ On return, holds an array describing all claims made on this
+ certificate.
+
+Return Value:
+
+ STATUS_SUCCESS
+ STATUS_NOT_FOUND
+ STATUS_NO_MEMORY
+
+--*/
+
+{
+ NTSTATUS nt;
+ PLLS_CERT_DB_CERTIFICATE_HEADER pHeader;
+ int iClaim;
+
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ // is the certificate in the db?
+ pHeader = CertDbHeaderFind( pLicense );
+
+ if ( NULL == pHeader )
+ {
+ // not here!
+ nt = STATUS_NOT_FOUND;
+ }
+ else
+ {
+ *ppTargets = MIDL_user_allocate( pHeader->NumClaims * sizeof( LLS_CERTIFICATE_CLAIM_INFO_0 ) );
+
+ if ( NULL == *ppTargets )
+ {
+ nt = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ *pdwNumClaims = pHeader->NumClaims;
+
+ for ( iClaim=0; (DWORD)iClaim < pHeader->NumClaims; iClaim++ )
+ {
+ lstrcpy( (*ppTargets)[ iClaim ].ServerName, pHeader->Claims[ iClaim ].ServerName );
+ (*ppTargets)[ iClaim ].Quantity = pHeader->Claims[ iClaim ].Quantity;
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+ return nt;
+}
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+void CertDbDebugDump()
+
+/*++
+
+Routine Description:
+
+ Dump contents of certificate database to debug console.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int iHeader;
+ int iClaim;
+
+ RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE );
+
+ for ( iHeader=0; (DWORD)iHeader < CertDbHeaderListSize; iHeader++ )
+ {
+ dprintf( TEXT("\n(%3d) Product : %s\n"), iHeader, CertDbHeaderList[ iHeader ].Product );
+ dprintf( TEXT(" CertificateID : %d\n"), CertDbHeaderList[ iHeader ].CertificateID );
+ dprintf( TEXT(" MaxQuantity : %d\n"), CertDbHeaderList[ iHeader ].MaxQuantity );
+ dprintf( TEXT(" ExpirationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].ExpirationDate ) );
+
+ for ( iClaim=0; (DWORD)iClaim < CertDbHeaderList[ iHeader ].NumClaims; iClaim++ )
+ {
+ dprintf( TEXT("\n (%3d) ServerName : %s\n"), iClaim, CertDbHeaderList[ iHeader ].Claims[ iClaim ].ServerName );
+ dprintf( TEXT(" ReplicationDate : %s\n"), TimeToString( CertDbHeaderList[ iHeader ].Claims[ iClaim ].ReplicationDate ) );
+ dprintf( TEXT(" Quantity : %d\n"), CertDbHeaderList[ iHeader ].Claims[ iClaim ].Quantity );
+ }
+ }
+
+ RtlReleaseResource( &CertDbHeaderListLock );
+
+} // CertDbDebugDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/certdb.h b/private/net/svcdlls/lls/server/certdb.h
new file mode 100644
index 000000000..9dd169c41
--- /dev/null
+++ b/private/net/svcdlls/lls/server/certdb.h
@@ -0,0 +1,129 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ certdb.h
+
+Abstract:
+
+
+Author:
+
+ Jeff Parham (jeffparh) 16-Nov-1995
+
+Revision History:
+
+--*/
+
+#ifndef _CERTDB_H_
+#define _CERTDB_H_
+
+// maximum time (in seconds) allowed to pass between certificate replications
+// before we remove the apparently no longer used data. this is so that, for
+// example, if a machine goes down (taking its licenses with it), the licenses
+// it had registered won't forever keep the user from reinstalling them.
+#define LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX ( 60 * 60 * 72 )
+
+#define LLS_CERT_DB_FILE_VERSION ( 0x0201 )
+
+typedef struct _LLS_CERT_DB_FILE_HEADER
+{
+ ULONG ProductStringSize;
+ ULONG NumCertificates;
+ ULONG NumClaims;
+} LLS_CERT_DB_FILE_HEADER, *PLLS_CERT_DB_FILE_HEADER;
+
+typedef struct _LLS_CERT_DB_CERTIFICATE_CLAIM
+{
+ TCHAR ServerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+ DWORD ReplicationDate;
+ LONG Quantity;
+} LLS_CERT_DB_CERTIFICATE_CLAIM, *PLLS_CERT_DB_CERTIFICATE_CLAIM;
+
+typedef struct _LLS_CERT_DB_CERTIFICATE_HEADER
+{
+ LPTSTR Product;
+ DWORD CertificateID;
+ DWORD MaxQuantity;
+ DWORD ExpirationDate;
+
+ DWORD NumClaims;
+ PLLS_CERT_DB_CERTIFICATE_CLAIM Claims;
+} LLS_CERT_DB_CERTIFICATE_HEADER, *PLLS_CERT_DB_CERTIFICATE_HEADER;
+
+
+extern RTL_RESOURCE CertDbHeaderListLock;
+extern PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList;
+extern DWORD CertDbHeaderListSize;
+extern HANDLE CertDbFile;
+
+
+NTSTATUS
+CertDbInit();
+
+NTSTATUS
+CertDbLoad();
+
+NTSTATUS
+CertDbSave();
+
+void
+CertDbLogViolations();
+
+void
+CertDbPrune();
+
+void
+CertDbRemoveLocalClaims();
+
+void
+CertDbUpdateLocalClaims();
+
+NTSTATUS
+CertDbClaimEnter( LPTSTR pszServerName,
+ PLLS_LICENSE_INFO_1 pLicense,
+ BOOL bIsTotal,
+ DWORD ReplicationDate );
+
+BOOL
+CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense );
+
+PLLS_CERT_DB_CERTIFICATE_HEADER
+CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense );
+
+PLLS_CERT_DB_CERTIFICATE_HEADER
+CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense );
+
+int
+CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader,
+ LPTSTR pszServerName );
+
+NTSTATUS
+CertDbPack( LPDWORD pcchProductStrings,
+ LPTSTR * ppchProductStrings,
+ LPDWORD pdwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders,
+ LPDWORD pdwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims );
+
+NTSTATUS
+CertDbUnpack( DWORD cchProductStrings,
+ LPTSTR pchProductStrings,
+ DWORD dwNumHeaders,
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 pHeaders,
+ DWORD dwNumClaims,
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 pClaims,
+ BOOL bReplicated );
+
+NTSTATUS
+CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense,
+ LPDWORD pdwNumClaims,
+ PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets );
+
+#if DBG
+void CertDbDebugDump();
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.c b/private/net/svcdlls/lls/server/llssrv.c
new file mode 100644
index 000000000..9af40a16a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.c
@@ -0,0 +1,1087 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.c
+
+Abstract:
+
+ Main routine to setup the exception handlers and initialize everything
+ to listen to LPC and RPC port requests.
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added certificate database support.
+ o Expanded file load time (the update limit sent to the service
+ controller) to account for certificate database loading.
+ o Reordered initialization such that the license purchase subsystem
+ is initialized before the service subsystem. (The service
+ subsystem now uses the license subsystem.)
+ o Increased internal version number.
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <tchar.h>
+
+#include <lm.h>
+#include <alertmsg.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "service.h"
+#include "registry.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+#include "repl.h"
+#include "scaven.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+
+
+VOID LLSRpcInit();
+BOOLEAN LLSpLPCInitialize ( VOID );
+
+
+#define INTERNAL_VERSION 0x0006
+
+#define DEFAULT_LICENSE_CHECK_TIME 24
+#define DEFAULT_REPLICATION_TIME 12 * 60 * 60
+
+CONFIG_RECORD ConfigInfo;
+RTL_CRITICAL_SECTION ConfigInfoLock;
+
+
+VOID LoadAll ( );
+
+#if DBG
+DWORD TraceFlags = 0;
+#endif
+
+//
+// this event is signalled when the service should end
+//
+HANDLE hServerStopEvent = NULL;
+TCHAR MyDomain[MAX_COMPUTERNAME_LENGTH + 2];
+ULONG MyDomainSize;
+
+BOOL IsMaster = FALSE;
+
+//
+// Files
+//
+TCHAR MappingFileName[MAX_PATH + 1];
+TCHAR UserFileName[MAX_PATH + 1];
+TCHAR LicenseFileName[MAX_PATH + 1];
+TCHAR CertDbFileName[MAX_PATH + 1];
+
+
+extern SERVICE_STATUS_HANDLE sshStatusHandle;
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTDomainGet(
+ LPTSTR ServerName,
+ LPTSTR Domain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer;
+
+ lstrcpy(Domain, TEXT(""));
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ if ( (ServerName == NULL) || (ServerName[0] == TEXT('\0')) )
+ ret = LsaOpenPolicy(NULL, &oa, am, &hLSA);
+ else {
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+ }
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ lstrcpy(Domain, pvBuffer->Name.Buffer);
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ return ret;
+
+} // NTDomainGet
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTIsPDC(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_LSA_SERVER_ROLE_INFO pvBuffer;
+ BOOL IsPDC = FALSE;
+
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyLsaServerRoleInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ if (pvBuffer->LsaServerRole == PolicyServerRolePrimary)
+ IsPDC = TRUE;
+
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ return IsPDC;
+
+} // NTIsPDC
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+LlsTimeGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ SYSTEMTIME SysTime;
+
+ GetLocalTime(&SysTime);
+
+ Seconds = (SysTime.wHour * 24 * 60) + (SysTime.wMinute * 60) + (SysTime.wSecond);
+ return Seconds;
+
+} // LlsTimeGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoRegistryUpdate( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD ReplicationType, ReplicationTime;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ConfigInfoRegistryUpdate\n"));
+#endif
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ //
+ // Update values from Registry
+ //
+ ReplicationTime = ConfigInfo.ReplicationTime;
+ ReplicationType = ConfigInfo.ReplicationType;
+ ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer,
+ &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime,
+ &ConfigInfo.LogLevel );
+
+ if ( (ConfigInfo.ReplicationTime == 0) && (LLS_REPLICATION_TYPE_TIME != ConfigInfo.ReplicationType) )
+ ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
+
+ //
+ // Adjust replication time if it has changed
+ //
+ if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType))
+ ReplicationTimeSet();
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoRegistryUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoUpdate( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL InDomain = FALSE;
+ BOOL IsPDC = FALSE;
+ USHORT cbTotalAvail, cbBuffer;
+ LPBYTE pbBuffer;
+ NET_API_STATUS uRet;
+ PSERVER_INFO_101 pServer1;
+ DWORD ReplicationType, ReplicationTime;
+ TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1];
+ NT_PRODUCT_TYPE NtType;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ConfigInfoUpdate\n"));
+#endif
+ //
+ // Try to get a domain
+ //
+ lstrcpy(pDomain, TEXT(""));
+ if ( !NTDomainGet(NULL, pDomain) ) {
+ InDomain = TRUE;
+
+ //
+ // If we aren't a BDC/PDC then count us as a member
+ //
+ NtType = NtProductLanManNt;
+ RtlGetNtProductType(&NtType);
+ if (NtType != NtProductLanManNt)
+ IsPDC = FALSE;
+ else {
+ //
+ // Let's check if we are a PDC...
+ //
+ IsPDC = NTIsPDC(ConfigInfo.ComputerName);
+ }
+
+ } else {
+ IsPDC = TRUE;
+ InDomain = FALSE;
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+
+ //
+ // If we are in a domain, and not the PDC then we replicate to the PDC
+ //
+ if (!IsPDC && InDomain) {
+ //
+ // Get the PDC of the domain
+ //
+ uRet = NetGetDCName(NULL, pDomain, &pbBuffer);
+ if (uRet == 0) {
+ lstrcpy(ConfigInfo.ReplicateTo, (LPWSTR) pbBuffer);
+ NetApiBufferFree(pbBuffer);
+ ConfigInfo.IsMaster = FALSE;
+ ConfigInfo.Replicate = TRUE;
+ } else {
+ InDomain = FALSE;
+ memset(ConfigInfo.ReplicateTo, 0, sizeof(ConfigInfo.ReplicateTo));
+#if DBG
+ dprintf(TEXT("LLS: (WARNING) NetGetDCName: 0x%lX\n"), uRet);
+#endif
+ }
+ }
+
+ //
+ // Update values from Registry
+ //
+ ReplicationTime = ConfigInfo.ReplicationTime;
+ ReplicationType = ConfigInfo.ReplicationType;
+ ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer,
+ &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime,
+ &ConfigInfo.LogLevel );
+
+ //
+ // Have all registy init'd values - now need to figure out who to
+ // replicate to.
+ //
+ // If we are not in a domain or are a PDC then we can go to the
+ // Enterprise Server.
+ //
+ if (IsPDC || !InDomain) {
+ if (ConfigInfo.UseEnterprise) {
+ ConfigInfo.IsMaster = FALSE;
+ ConfigInfo.Replicate = TRUE;
+
+ //
+ // Make sure we have an enterprise server to go to
+ //
+ if ( ConfigInfo.EnterpriseServer[0] == TEXT('\0') ) {
+ ConfigInfo.UseEnterprise = FALSE;
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+ } else {
+ //
+ // Base ReplicateTo on enterprise server name
+ //
+ if (ConfigInfo.EnterpriseServer[0] != TEXT('\\'))
+ lstrcpy(ConfigInfo.ReplicateTo, TEXT("\\\\"));
+ else
+ lstrcpy(ConfigInfo.ReplicateTo, TEXT(""));
+
+ lstrcat(ConfigInfo.ReplicateTo, ConfigInfo.EnterpriseServer);
+ }
+ } else
+ ConfigInfo.IsMaster = TRUE;
+ } else
+ ConfigInfo.UseEnterprise = FALSE;
+
+ if (ConfigInfo.IsMaster == FALSE) {
+ if ( (ConfigInfo.ReplicateTo == NULL) || (lstrlen(ConfigInfo.ReplicateTo) == 0) ||
+ ( (*ConfigInfo.ReplicateTo == TEXT('\\')) && (lstrlen(ConfigInfo.ReplicateTo) < 3) )) {
+ ConfigInfo.IsMaster = TRUE;
+ ConfigInfo.Replicate = FALSE;
+ }
+ }
+
+ //
+ // Adjust replication time if it has changed
+ //
+ if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType))
+ ReplicationTimeSet();
+
+ IsMaster = ConfigInfo.IsMaster;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoInit( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD Size;
+ TCHAR DataPath[MAX_PATH + 1];
+
+ //
+ // First zero init the memory
+ //
+ memset(&ConfigInfo, 0, sizeof(CONFIG_RECORD));
+
+ ConfigInfo.ComputerName = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR));
+ ConfigInfo.ReplicateTo = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
+ ConfigInfo.EnterpriseServer = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR));
+ ConfigInfo.SystemDir = LocalAlloc(LPTR, (MAX_PATH + 1) * sizeof(TCHAR));
+
+ if ((ConfigInfo.ComputerName == NULL) || (ConfigInfo.ReplicateTo == NULL) || (ConfigInfo.EnterpriseServer == NULL) || (ConfigInfo.SystemDir == NULL) ) {
+ ASSERT(FALSE);
+ }
+
+ ConfigInfo.Version = INTERNAL_VERSION;
+ GetLocalTime(&ConfigInfo.Started);
+
+ //
+ // LastReplicated is just for display, LlsReplTime is what is used to
+ // Calculate it.
+ GetLocalTime(&ConfigInfo.LastReplicated);
+ ConfigInfo.LastReplicatedSeconds = DateSystemGet();
+
+ GetSystemDirectory(ConfigInfo.SystemDir, MAX_PATH);
+ lstrcat(ConfigInfo.SystemDir, TEXT("\\"));
+
+ ConfigInfo.IsMaster = TRUE;
+
+ ConfigInfo.Replicate = FALSE;
+ ConfigInfo.IsReplicating = FALSE;
+
+ ConfigInfo.ReplicationType = REPLICATE_DELTA;
+ ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME;
+
+ Size = MAX_COMPUTERNAME_LENGTH + 1;
+ GetComputerName(ConfigInfo.ComputerName, &Size);
+ NTDomainGet( ConfigInfo.ComputerName, MyDomain);
+ lstrcat(MyDomain, TEXT("\\"));
+ MyDomainSize = (lstrlen(MyDomain) + 1) * sizeof(TCHAR);
+
+ RtlInitializeCriticalSection(&ConfigInfoLock);
+
+ ConfigInfoUpdate();
+
+ //
+ // Create File paths
+ //
+ lstrcpy(MappingFileName, ConfigInfo.SystemDir);
+ lstrcat(MappingFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(MappingFileName, TEXT("\\"));
+ lstrcat(MappingFileName, TEXT(MAP_FILE_NAME));
+
+ lstrcpy(UserFileName, ConfigInfo.SystemDir);
+ lstrcat(UserFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(UserFileName, TEXT("\\"));
+ lstrcat(UserFileName, TEXT(USER_FILE_NAME));
+
+ lstrcpy(CertDbFileName, ConfigInfo.SystemDir);
+ lstrcat(CertDbFileName, TEXT(LLS_FILE_SUBDIR));
+ lstrcat(CertDbFileName, TEXT("\\"));
+ lstrcat(CertDbFileName, TEXT(CERT_DB_FILE_NAME));
+
+ lstrcpy(LicenseFileName, ConfigInfo.SystemDir);
+ lstrcat(LicenseFileName, TEXT(LICENSE_FILE_NAME));
+
+ //
+ // Make sure our directory is there.
+ //
+ lstrcpy(DataPath, ConfigInfo.SystemDir);
+ lstrcat(DataPath, TEXT(LLS_FILE_SUBDIR));
+ CreateDirectory(DataPath, NULL);
+
+} // ConfigInfoInit
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD WINAPI
+LLSTopLevelExceptionHandler(
+ struct _EXCEPTION_POINTERS *ExceptionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ The Top Level exception filter for LLSMain.exe.
+
+ This ensures the entire process will be cleaned up if any of
+ the threads fail. Since LLSMain.exe is a distributed application,
+ it is better to fail the entire process than allow random threads
+ to continue executing.
+
+Arguments:
+
+ ExceptionInfo - Identifies the exception that occurred.
+
+
+Return Values:
+
+ EXCEPTION_EXECUTE_HANDLER - Terminate the process.
+
+ EXCEPTION_CONTINUE_SEARCH - Continue processing as though this filter
+ was never called.
+
+
+--*/
+{
+ HANDLE hModule;
+
+
+ //
+ // Raise an alert
+ //
+
+ hModule = LoadLibraryA("netapi32");
+
+ if ( hModule != NULL ) {
+ NET_API_STATUS (NET_API_FUNCTION *NetAlertRaiseExFunction)
+ (LPTSTR, LPVOID, DWORD, LPTSTR);
+
+
+ NetAlertRaiseExFunction =
+ (NET_API_STATUS (NET_API_FUNCTION *) (LPTSTR, LPVOID, DWORD, LPTSTR))
+ GetProcAddress(hModule, "NetAlertRaiseEx");
+
+ if ( NetAlertRaiseExFunction != NULL ) {
+ NTSTATUS Status;
+ UNICODE_STRING Strings;
+
+ char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)];
+ PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message;
+
+ //
+ // Build the variable data
+ //
+
+ admin->alrtad_errcode = ALERT_UnhandledException;
+ admin->alrtad_numstrings = 0;
+
+ Strings.Buffer = (LPWSTR) ALERT_VAR_DATA(admin);
+ Strings.Length = 0;
+ Strings.MaximumLength = ALERTSZ;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionCode,
+ 16,
+ &Strings );
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Length += sizeof(WCHAR);
+
+ Status = RtlAppendUnicodeToString( &Strings, L"LLS" );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+ Strings.MaximumLength -= Strings.Length + sizeof(WCHAR);
+ Strings.Length = 0;
+
+ Status = RtlIntegerToUnicodeString(
+ (ULONG)ExceptionInfo->ExceptionRecord->ExceptionAddress,
+ 16,
+ &Strings );
+ }
+
+ }
+
+ if ( NT_SUCCESS(Status) ) {
+ if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ admin->alrtad_numstrings++;
+ *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0';
+ Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1;
+
+ (VOID) (*NetAlertRaiseExFunction)(
+ ALERT_ADMIN_EVENT,
+ message,
+ (DWORD)((PCHAR)Strings.Buffer -
+ (PCHAR)message),
+ L"LLS" );
+ }
+
+ }
+
+
+ }
+
+ (VOID) FreeLibrary( hModule );
+ }
+
+
+ //
+ // Just continue processing the exception.
+ //
+
+ return EXCEPTION_CONTINUE_SEARCH;
+
+} // LLSTopLevelExceptionHandler
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceStart (
+ DWORD dwArgc,
+ LPTSTR *lpszArgv
+ )
+/*++
+
+Routine Description:
+
+ The code that starts everything, is really the main().
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwWait;
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN EnableAlignmentFaults = TRUE;
+ KPRIORITY BasePriority;
+
+ ///////////////////////////////////////////////////
+ //
+ // Service initialization
+ //
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ //
+ // Create the event object. The control handler function signals
+ // this event when it receives the "stop" control code.
+ //
+ hServerStopEvent = CreateEvent(
+ NULL, // no security attributes
+ TRUE, // manual reset event
+ FALSE, // not-signalled
+ NULL); // no name
+
+ if ( hServerStopEvent == NULL)
+ goto Cleanup;
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+
+ //
+ // Define a top-level exception handler for the entire process.
+ //
+
+ (VOID) SetErrorMode( SEM_FAILCRITICALERRORS );
+
+ (VOID) SetUnhandledExceptionFilter( &LLSTopLevelExceptionHandler );
+
+ //
+ // Turn on alignment fault fixups. This is necessary because
+ // several structures stored in the registry have qword aligned
+ // fields. They are nicely aligned in our structures, but they
+ // end up being forced out of alignment when being stored because
+ // the registry api require data to be passed following a wierd
+ // length header.
+ //
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessEnableAlignmentFaultFixup,
+ (PVOID) &EnableAlignmentFaults,
+ sizeof(BOOLEAN)
+ );
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ //
+ // Run the LLS in the foreground.
+ //
+ // Several processes which depend on the LLS (like the lanman server)
+ // run in the foreground. If we don't run in the foreground, they'll
+ // starve waiting for us.
+ //
+
+ BasePriority = FOREGROUND_BASE_PRIORITY;
+
+ Status = NtSetInformationProcess(
+ NtCurrentProcess(),
+ ProcessBasePriority,
+ &BasePriority,
+ sizeof(BasePriority)
+ );
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Registry values...
+ RegistryInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Registry values...
+ ConfigInfoInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ LicenseListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ MasterServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ LocalServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ ServiceListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ MappingListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Per-Seat Table
+ UserListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Service Table
+ ServerListInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize the Certificate Database
+ CertDbInit();
+
+ //
+ // Report the status to the service control manager - need a bit longer
+ // to read in all the data files.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 15000)) // wait hint
+ goto Cleanup;
+
+ // Load data files
+ LoadAll();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize RPC Stuff...
+ LLSRpcInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize Replication...
+ ReplicationInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize scavenger thread...
+ ScavengerInit();
+
+ //
+ // Report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint
+ goto Cleanup;
+
+ // Initialize RegistryMonitor thread...
+ RegistryStartMonitor();
+
+ //
+ // End of initialization
+ //
+ ////////////////////////////////////////////////////////
+
+ //
+ // Tell SCM we are up and running!
+ //
+ if (!ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0)) // wait hint
+ goto Cleanup;
+
+ ////////////////////////////////////////////////////////
+ //
+ // Service is now running, perform work until shutdown
+ //
+ dwWait = WaitForSingleObject(hServerStopEvent, INFINITE);
+
+Cleanup:
+
+ if (hServerStopEvent)
+ CloseHandle(hServerStopEvent);
+
+ if (sshStatusHandle)
+ ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0);
+
+} // ServiceStart
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ServiceStop()
+/*++
+
+Routine Description:
+
+ Stops the service.
+
+ If a ServiceStop procedure is going to take longer than 3 seconds to
+ execute, it should spawn a thread to execute the stop code, and return.
+ Otherwise, the ServiceControlManager will believe that the service has
+ stopped responding.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ if ( hServerStopEvent )
+ SetEvent(hServerStopEvent);
+} // ServiceStop
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ dprintf(TEXT("License Logging Service - Version: 0x%lX\n"), ConfigInfo.Version);
+ dprintf(TEXT(" Started: %u-%u-%u @ %u:%u:%u\n"),
+ (UINT) ConfigInfo.Started.wDay,
+ (UINT) ConfigInfo.Started.wMonth,
+ (UINT) ConfigInfo.Started.wYear,
+ (UINT) ConfigInfo.Started.wHour,
+ (UINT) ConfigInfo.Started.wMinute,
+ (UINT) ConfigInfo.Started.wSecond );
+
+ dprintf(TEXT(" Replication\n"));
+ dprintf(TEXT(" +--------------+\n"));
+ if (ConfigInfo.IsMaster)
+ dprintf(TEXT(" Master Server\n"));
+ else
+ dprintf(TEXT(" NOT Master Server\n"));
+
+ if (ConfigInfo.Replicate)
+ dprintf(TEXT(" Replicates\n"));
+ else
+ dprintf(TEXT(" Does not Replicate\n"));
+
+ if (ConfigInfo.IsReplicating)
+ dprintf(TEXT(" Currently Replicating\n"));
+ else
+ dprintf(TEXT(" NOT Currently Replicating\n"));
+
+ dprintf(TEXT(" Replicates To: %s\n"), ConfigInfo.ReplicateTo);
+ dprintf(TEXT(" Enterprise Server: %s\n"), ConfigInfo.EnterpriseServer);
+
+ if (ConfigInfo.ReplicationType == REPLICATE_DELTA)
+ dprintf(TEXT(" Replicate Every: %lu Seconds\n"), ConfigInfo.ReplicationTime );
+ else
+ dprintf(TEXT(" Replicate @: %lu\n"), ConfigInfo.ReplicationTime );
+
+ dprintf(TEXT("\n Last Replicated: %u-%u-%u @ %u:%u:%u\n\n"),
+ (UINT) ConfigInfo.LastReplicated.wDay,
+ (UINT) ConfigInfo.LastReplicated.wMonth,
+ (UINT) ConfigInfo.LastReplicated.wYear,
+ (UINT) ConfigInfo.LastReplicated.wHour,
+ (UINT) ConfigInfo.LastReplicated.wMinute,
+ (UINT) ConfigInfo.LastReplicated.wSecond );
+
+ dprintf(TEXT(" Number Servers Currently Replicating: %lu\n"), ConfigInfo.NumReplicating);
+
+ dprintf(TEXT(" Current Backoff Time Delta: %lu\n"), ConfigInfo.BackoffTime);
+
+ dprintf(TEXT(" Current Replication Speed: %lu\n"), ConfigInfo.ReplicationSpeed);
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+} // ConfigInfoDebugDump
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.h b/private/net/svcdlls/lls/server/llssrv.h
new file mode 100644
index 000000000..1601a4f0e
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.h
@@ -0,0 +1,109 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsSrv.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added certificate database support.
+
+--*/
+
+#ifndef _LLS_LLSSRV_H
+#define _LLS_LLSSRV_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAP_FILE_NAME "LlsMap.LLS"
+#define USER_FILE_NAME "LlsUser.LLS"
+#define LICENSE_FILE_NAME "CPL.CFG"
+#define CERT_DB_FILE_NAME "LlsCert.LLS"
+
+#define LLS_FILE_SUBDIR "LLS"
+
+
+#define REPLICATE_DELTA 0
+#define REPLICATE_AT 1
+
+#define MAX_USERNAME_LENGTH 256
+#define MAX_DOMAINNAME_LENGTH MAX_COMPUTERNAME_LENGTH
+
+
+/////////////////////////////////////////////////////////////////////////
+typedef struct _CONFIG_RECORD {
+ SYSTEMTIME Started;
+ DWORD Version;
+ LPTSTR SystemDir;
+
+ //
+ // Replication Info
+ //
+ LPTSTR ComputerName;
+ LPTSTR ReplicateTo;
+ LPTSTR EnterpriseServer;
+ DWORD EnterpriseServerDate;
+ DWORD LogLevel;
+
+ // When to replicate
+ ULONG ReplicationType;
+ ULONG ReplicationTime;
+ DWORD UseEnterprise;
+
+ DWORD LastReplicatedSeconds;
+ DWORD NextReplication;
+ SYSTEMTIME LastReplicated;
+
+ ULONG NumReplicating; // Number of machines currently replicating here
+ ULONG BackoffTime;
+ ULONG ReplicationSpeed;
+
+ BOOL IsMaster; // TRUE if is a Master Server (top of repl tree).
+ BOOL Replicate; // Whether this server replicates
+ BOOL IsReplicating; // TRUE if currently replicating
+} CONFIG_RECORD, *PCONFIG_RECORD;
+
+extern CONFIG_RECORD ConfigInfo;
+extern RTL_CRITICAL_SECTION ConfigInfoLock;
+
+extern TCHAR MyDomain[];
+extern ULONG MyDomainSize;
+
+extern BOOL IsMaster;
+
+extern TCHAR MappingFileName[];
+extern TCHAR UserFileName[];
+extern TCHAR LicenseFileName[];
+extern TCHAR CertDbFileName[];
+
+
+DWORD LlsTimeGet();
+VOID ConfigInfoUpdate();
+VOID ConfigInfoRegistryUpdate( );
+
+/////////////////////////////////////////////////////////////////////////
+#if DBG
+
+VOID ConfigInfoDebugDump();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/llssrv.rc b/private/net/svcdlls/lls/server/llssrv.rc
new file mode 100644
index 000000000..8fd373a89
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llssrv.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server"
+
+#define VER_INTERNALNAME_STR "llssrv.exe"
+#define VER_ORIGINALFILENAME_STR "llssrv.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/server/llsutil.c b/private/net/svcdlls/lls/server/llsutil.c
new file mode 100644
index 000000000..6b52e6791
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llsutil.c
@@ -0,0 +1,982 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ LlsUtil.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
+ running on a given machine.
+ o Enhanced output of TimeToString().
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntlsa.h>
+
+#include <windows.h>
+#include <stdlib.h>
+#include <crypt.h>
+#include <wchar.h>
+
+#include "debug.h"
+#include "llssrv.h"
+
+
+char HeaderString[] = "License Logging System Data File\x01A";
+#define HEADER_SIZE 34
+
+typedef struct _LLS_FILE_HEADER {
+ char Header[HEADER_SIZE];
+ DWORD Version;
+ DWORD DataSize;
+} LLS_FILE_HEADER, *PLLS_FILE_HEADER;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+EBlock(
+ PVOID Data,
+ ULONG DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DATA_KEY PublicKey;
+ CRYPT_BUFFER CryptBuffer;
+
+ //
+ // Init our public key
+ //
+ PublicKey.Length = 4;
+ PublicKey.MaximumLength = 4;
+ PublicKey.Buffer = LocalAlloc(LPTR, 4);
+
+ if (PublicKey.Buffer != NULL) {
+ ((char *) (PublicKey.Buffer))[0] = '7';
+ ((char *) (PublicKey.Buffer))[1] = '7';
+ ((char *) (PublicKey.Buffer))[2] = '7';
+ ((char *) (PublicKey.Buffer))[3] = '7';
+
+ CryptBuffer.Length = DataSize;
+ CryptBuffer.MaximumLength = DataSize;
+ CryptBuffer.Buffer = (PVOID) Data;
+ Status = RtlEncryptData2(&CryptBuffer, &PublicKey);
+
+ LocalFree(PublicKey.Buffer);
+ } else
+ Status = STATUS_NO_MEMORY;
+
+ return Status;
+} // EBlock
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+DeBlock(
+ PVOID Data,
+ ULONG DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DATA_KEY PublicKey;
+ CRYPT_BUFFER CryptBuffer;
+
+ //
+ // Init our public key
+ //
+ PublicKey.Length = 4;
+ PublicKey.MaximumLength = 4;
+ PublicKey.Buffer = LocalAlloc(LPTR, 4);
+ if (PublicKey.Buffer != NULL) {
+ ((char *) (PublicKey.Buffer))[0] = '7';
+ ((char *) (PublicKey.Buffer))[1] = '7';
+ ((char *) (PublicKey.Buffer))[2] = '7';
+ ((char *) (PublicKey.Buffer))[3] = '7';
+
+ CryptBuffer.Length = DataSize;
+ CryptBuffer.MaximumLength = DataSize;
+ CryptBuffer.Buffer = (PVOID) Data;
+ Status = RtlDecryptData2(&CryptBuffer, &PublicKey);
+
+ LocalFree(PublicKey.Buffer);
+ } else
+ Status = STATUS_NO_MEMORY;
+
+ return Status;
+} // DeBlock
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+FileExists(
+ LPTSTR FileName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return (BOOL) RtlDoesFileExists_U(FileName);
+
+} // FileExists
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lsplitpath(
+ const TCHAR *path,
+ TCHAR *drive,
+ TCHAR *dir,
+ TCHAR *fname,
+ TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+ Splits a path name into its individual components
+
+ Took the _splitpath and _makepath routines and converted them to
+ be NT (long file name) and Unicode friendly.
+
+Arguments:
+ Entry:
+ path - pointer to path name to be parsed
+ drive - pointer to buffer for drive component, if any
+ dir - pointer to buffer for subdirectory component, if any
+ fname - pointer to buffer for file base name component, if any
+ ext - pointer to buffer for file name extension component, if any
+
+ Exit:
+ drive - pointer to drive string. Includes ':' if a drive was given.
+ dir - pointer to subdirectory string. Includes leading and
+ trailing '/' or '\', if any.
+ fname - pointer to file base name
+ ext - pointer to file extension, if any. Includes leading '.'.
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *p;
+ TCHAR *last_slash = NULL, *dot = NULL;
+ unsigned len;
+
+ // init these so we don't exit with bogus values
+ drive[0] = TEXT('\0');
+ dir[0] = TEXT('\0');
+ fname[0] = TEXT('\0');
+ ext[0] = TEXT('\0');
+
+ if (path[0] == TEXT('\0'))
+ return;
+
+ /*+---------------------------------------------------------------------+
+ | Assume that the path argument has the following form, where any or |
+ | all of the components may be missing. |
+ | |
+ | <drive><dir><fname><ext> |
+ | |
+ | drive: |
+ | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a |
+ | ':' or a '\' in the case of a UNC path. |
+ | dir: |
+ | 0 to _MAX_DIR-1 characters in the form of an absolute path |
+ | (leading '/' or '\') or relative path, the last of which, if |
+ | any, must be a '/' or '\'. E.g - |
+ | |
+ | absolute path: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ |
+ | relative path: |
+ | top\next\last\ ; or |
+ | top/next/last/ |
+ | Mixed use of '/' and '\' within a path is also tolerated |
+ | fname: |
+ | 0 to _MAX_FNAME-1 characters not including the '.' character |
+ | ext: |
+ | 0 to _MAX_EXT-1 characters where, if any, the first must be a |
+ | '.' |
+ +---------------------------------------------------------------------+*/
+
+ // extract drive letter and :, if any
+ if ( path[0] && (path[1] == TEXT(':')) ) {
+ if (drive) {
+ drive[0] = path[0];
+ drive[1] = path[1];
+ drive[2] = TEXT('\0');
+ }
+ path += 2;
+ }
+
+ // if no drive then check for UNC pathname
+ if (drive[0] == TEXT('\0'))
+ if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
+ // got a UNC path so put server-sharename into drive
+ drive[0] = path[0];
+ drive[1] = path[1];
+ path += 2;
+
+ p = &drive[2];
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ if (*path == TEXT('\0'))
+ return;
+
+ // now sitting at the share - copy this as well (copy slash first)
+ *p++ = *path++;
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ // tack on terminating NULL
+ *p = TEXT('\0');
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract path string, if any. Path now points to the first character|
+ | of the path, if any, or the filename or extension, if no path was |
+ | specified. Scan ahead for the last occurence, if any, of a '/' or |
+ | '\' path separator character. If none is found, there is no path. |
+ | We will also note the last '.' character found, if any, to aid in |
+ | handling the extension. |
+ +---------------------------------------------------------------------+*/
+
+ for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
+ if (*p == TEXT('/') || *p == TEXT('\\'))
+ // point to one beyond for later copy
+ last_slash = p + 1;
+ else if (*p == TEXT('.'))
+ dot = p;
+ }
+
+ if (last_slash) {
+
+ // found a path - copy up through last_slash or max. characters allowed,
+ // whichever is smaller
+ if (dir) {
+ len = __min((last_slash - path), (_MAX_DIR - 1));
+ lstrcpyn(dir, path, len + 1);
+ dir[len] = TEXT('\0');
+ }
+ path = last_slash;
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract file name and extension, if any. Path now points to the |
+ | first character of the file name, if any, or the extension if no |
+ | file name was given. Dot points to the '.' beginning the extension,|
+ | if any. |
+ +---------------------------------------------------------------------+*/
+
+ if (dot && (dot >= path)) {
+ // found the marker for an extension - copy the file name up to the
+ // '.'.
+ if (fname) {
+ len = __min((dot - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ *(fname + len) = TEXT('\0');
+ }
+
+ // now we can get the extension - remember that p still points to the
+ // terminating nul character of path.
+ if (ext) {
+ len = __min((p - dot), (_MAX_EXT - 1));
+ lstrcpyn(ext, dot, len + 1);
+ ext[len] = TEXT('\0');
+ }
+ }
+ else {
+ // found no extension, give empty extension and copy rest of string
+ // into fname.
+ if (fname) {
+ len = __min((p - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ fname[len] = TEXT('\0');
+ }
+ if (ext) {
+ *ext = TEXT('\0');
+ }
+ }
+
+} // lsplitpath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lmakepath(
+ TCHAR *path,
+ const TCHAR *drive,
+ const TCHAR *dir,
+ const TCHAR *fname,
+ const TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+ Create a path name from its individual components.
+
+Arguments:
+ Entry:
+ char *path - pointer to buffer for constructed path
+ char *drive - pointer to drive component, may or may not contain
+ trailing ':'
+ char *dir - pointer to subdirectory component, may or may not include
+ leading and/or trailing '/' or '\' characters
+ char *fname - pointer to file base name component
+ char *ext - pointer to extension component, may or may not contain
+ a leading '.'.
+
+ Exit:
+ path - pointer to constructed path name
+
+Return Value:
+
+
+--*/
+
+{
+ const TCHAR *p;
+
+ /*+---------------------------------------------------------------------+
+ | we assume that the arguments are in the following form (although we |
+ | do not diagnose invalid arguments or illegal filenames (such as |
+ | names longer than 8.3 or with illegal characters in them) |
+ | |
+ | drive: |
+ | A or A: |
+ | dir: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ ; or |
+ | |
+ | either of the above forms with either/both the leading and |
+ | trailing / or \ removed. Mixed use of '/' and '\' is also |
+ | tolerated |
+ | fname: |
+ | any valid file name |
+ | ext: |
+ | any valid extension (none if empty or null ) |
+ +---------------------------------------------------------------------+*/
+
+ // copy drive
+ if (drive && *drive)
+ while (*drive)
+ *path++ = *drive++;
+
+ // copy dir
+ if ((p = dir) && *p) {
+ do {
+ *path++ = *p++;
+ }
+ while (*p);
+ if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
+ *path++ = TEXT('\\');
+ }
+ }
+
+ // copy fname
+ if (p = fname) {
+ while (*p) {
+ *path++ = *p++;
+ }
+ }
+
+ // copy ext, including 0-terminator - check to see if a '.' needs to be
+ // inserted.
+ if (p = ext) {
+ if (*p && *p != TEXT('.')) {
+ *path++ = TEXT('.');
+ }
+ while (*path++ = *p++)
+ ;
+ }
+ else {
+ // better add the 0-terminator
+ *path = TEXT('\0');
+ }
+
+} // lmakepath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileBackupCreate(
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HANDLE hFile = NULL;
+ DWORD dwFileNumber = 0;
+ TCHAR Drive[_MAX_DRIVE + 1];
+ TCHAR Dir[_MAX_DIR + 1];
+ TCHAR FileName[_MAX_FNAME + 1];
+ TCHAR Ext[_MAX_EXT + 1];
+ TCHAR NewExt[_MAX_EXT + 1];
+ TCHAR NewPath[MAX_PATH + 1];
+
+ //
+ // Make sure file exists
+ //
+ if (!FileExists(FileName))
+ return;
+
+ //
+ // Split name into constituent parts...
+ //
+ lsplitpath(Path, Drive, Dir, FileName, Ext);
+
+ // Find next backup number...
+ // Files are backed up as .xxx where xxx is a number in the form .001,
+ // the first backup is stored as .001, second as .002, etc...
+ do {
+ //
+ // Create new file name with backup extension
+ //
+ dwFileNumber++;
+ wsprintf(NewExt, TEXT("%03u"), dwFileNumber);
+ lmakepath(NewPath, Drive, Dir, FileName, NewExt);
+
+ } while ( FileExists(NewPath) );
+
+ MoveFile( Path, NewPath );
+
+} // FileBackupCreate
+
+
+/////////////////////////////////////////////////////////////////////////
+HANDLE
+LlsFileInit(
+ LPTSTR FileName,
+ DWORD Version,
+ DWORD DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HANDLE hFile = NULL;
+ LLS_FILE_HEADER Header;
+ DWORD BytesWritten;
+
+#ifdef DEBUG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LlsFileInit\n"));
+#endif
+
+ if (FileName == NULL)
+ return NULL;
+
+ strcpy(Header.Header, HeaderString);
+ Header.Version = Version;
+ Header.DataSize = DataSize;
+
+ SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
+ hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ if (!WriteFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesWritten, NULL)) {
+ CloseHandle(hFile);
+ hFile = NULL;
+ }
+ }
+
+ return hFile;
+} // LlsFileInit
+
+
+/////////////////////////////////////////////////////////////////////////
+HANDLE
+LlsFileCheck(
+ LPTSTR FileName,
+ LPDWORD Version,
+ LPDWORD DataSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL FileOK = FALSE;
+ HANDLE hFile = NULL;
+ LLS_FILE_HEADER Header;
+ DWORD FileSize;
+ DWORD BytesRead;
+
+#ifdef DEBUG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LlsFileCheck\n"));
+#endif
+
+ if (FileName == NULL)
+ return NULL;
+
+ //
+ // We are assuming the file exists
+ //
+ SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
+ hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE) {
+ FileSize = GetFileSize(hFile, NULL);
+
+ //
+ // Make sure there is enough data there to read
+ //
+ if (FileSize > (sizeof(LLS_FILE_HEADER) + 1)) {
+ if (ReadFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesRead, NULL)) {
+ if ( !_strcmpi(Header.Header, HeaderString) ) {
+ //
+ // Data checks out - so return datalength
+ //
+ *Version = Header.Version;
+ *DataSize = Header.DataSize;
+ FileOK = TRUE;
+ }
+ }
+ }
+
+ //
+ // If we opened the file and something was wrong - close it.
+ //
+ if (!FileOK) {
+ CloseHandle(hFile);
+ hFile = NULL;
+ }
+ }
+
+ return hFile;
+
+} // LlsFileCheck
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+DateSystemGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ LARGE_INTEGER SysTime;
+
+ NtQuerySystemTime(&SysTime);
+ RtlTimeToSecondsSince1980(&SysTime, &Seconds);
+ return Seconds;
+
+} // DateSystemGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+DateLocalGet(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ Seconds since midnight.
+
+--*/
+
+{
+ DWORD Seconds;
+ LARGE_INTEGER SysTime, LocalTime;
+
+ NtQuerySystemTime(&SysTime);
+ RtlSystemTimeToLocalTime(&SysTime, &LocalTime);
+ RtlTimeToSecondsSince1980(&LocalTime, &Seconds);
+ return Seconds;
+
+} // DateLocalGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+InAWorkgroup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function determines whether we are a member of a domain, or of
+ a workgroup. First it checks to make sure we're running on a Windows NT
+ system (otherwise we're obviously in a domain) and if so, queries LSA
+ to get the Primary domain SID, if this is NULL, we're in a workgroup.
+
+ If we fail for some random unexpected reason, we'll pretend we're in a
+ workgroup (it's more restrictive).
+
+Arguments:
+ None
+
+Return Value:
+
+ TRUE - We're in a workgroup
+ FALSE - We're in a domain
+
+--*/
+{
+ NT_PRODUCT_TYPE ProductType;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE Handle;
+ NTSTATUS Status;
+ PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
+ DWORD Result = FALSE;
+
+
+ Status = RtlGetNtProductType(&ProductType);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ dprintf(TEXT("ERROR LLS Could not get Product type\n"));
+#endif
+ return TRUE;
+ }
+
+ if (ProductType == NtProductLanManNt) {
+ return(FALSE);
+ }
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Status = LsaOpenPolicy(NULL,
+ &ObjectAttributes,
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &Handle);
+
+ if (!NT_SUCCESS(Status)) {
+#if DBG
+ dprintf(TEXT("ERROR LLS: Could not open LSA Policy Database\n"));
+#endif
+ return TRUE;
+ }
+
+ Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
+ (PVOID *) &PolicyPrimaryDomainInfo);
+
+ if (NT_SUCCESS(Status)) {
+
+ if (PolicyPrimaryDomainInfo->Sid == NULL) {
+ Result = TRUE;
+ }
+ else {
+ Result = FALSE;
+ }
+ }
+
+ if (PolicyPrimaryDomainInfo) {
+ LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
+ }
+
+ LsaClose(Handle);
+
+ return(Result);
+} // InAWorkgroup
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ )
+{
+
+ HANDLE LogHandle;
+ WORD wEventType;
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ TEXT("LicenseService")
+ );
+
+ if (LogHandle == NULL) {
+#if DBG
+ dprintf(TEXT("LLS RegisterEventSourceW failed %lu\n"), GetLastError());
+#endif
+ return;
+ }
+
+ switch ( MessageId >> 30 )
+ {
+ case STATUS_SEVERITY_INFORMATIONAL:
+ case STATUS_SEVERITY_SUCCESS:
+ wEventType = EVENTLOG_INFORMATION_TYPE;
+ break;
+ case STATUS_SEVERITY_WARNING:
+ wEventType = EVENTLOG_WARNING_TYPE;
+ break;
+ case STATUS_SEVERITY_ERROR:
+ default:
+ wEventType = EVENTLOG_ERROR_TYPE;
+ break;
+ }
+
+ if (ErrorCode == ERROR_SUCCESS) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ wEventType,
+ 0, // event category
+ MessageId,
+ NULL,
+ (WORD)NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ //
+ // Log the error code specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ wEventType,
+ 0, // event category
+ MessageId,
+ NULL,
+ (WORD)NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+} // LogEvent
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber )
+
+/*++
+
+Routine Description:
+
+ Retrieve the build number of Windows NT running on a given machine.
+
+Arguments:
+
+ pszServerName (LPTSTR)
+ Name of the server to check.
+ pdwBuildNumber (LPDWORD)
+ On return, holds the build number of the server (e.g., 1057 for the
+ release version of Windows NT 3.51).
+
+Return Value:
+
+ ERROR_SUCCESS or Win error code.
+
+--*/
+
+{
+ LONG lError;
+ HKEY hKeyLocalMachine;
+
+ lError = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not connect to remote registry, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ HKEY hKeyCurrentVersion;
+
+ lError = RegOpenKeyEx( hKeyLocalMachine,
+ TEXT( "Software\\Microsoft\\Windows NT\\CurrentVersion" ),
+ 0,
+ KEY_READ,
+ &hKeyCurrentVersion );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not open key, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ DWORD dwType;
+ TCHAR szWinNtBuildNumber[ 16 ];
+ DWORD cbWinNtBuildNumber = sizeof( szWinNtBuildNumber );
+
+ lError = RegQueryValueEx( hKeyCurrentVersion,
+ TEXT( "CurrentBuildNumber" ),
+ NULL,
+ &dwType,
+ (LPBYTE) &szWinNtBuildNumber,
+ &cbWinNtBuildNumber );
+
+ if ( ERROR_SUCCESS != lError )
+ {
+#if DBG
+ dprintf( TEXT("WinNtBuildNumberGet(): Could not query value, error %ld.\n"), lError );
+#endif
+ }
+ else
+ {
+ *pdwBuildNumber = (DWORD) _wtol( szWinNtBuildNumber );
+ }
+
+ RegCloseKey( hKeyCurrentVersion );
+ }
+
+ RegCloseKey( hKeyLocalMachine );
+ }
+
+ return (DWORD) lError;
+}
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+TimeToString(
+ ULONG Seconds
+ )
+{
+ TIME_FIELDS tf;
+ LARGE_INTEGER Time, LTime;
+ static TCHAR TimeString[100];
+
+ if ( 0 == Seconds )
+ {
+ lstrcpy(TimeString, TEXT("None"));
+ }
+ else
+ {
+ RtlSecondsSince1980ToTime(Seconds, &Time);
+ RtlSystemTimeToLocalTime(&Time, &LTime);
+ RtlTimeToTimeFields(&LTime, &tf);
+
+ wsprintf(TimeString, TEXT("%02hd/%02hd/%04hd @ %02hd:%02hd:%02hd"), tf.Month, tf.Day, tf.Year, tf.Hour, tf.Minute, tf.Second);
+ }
+
+ return TimeString;
+
+} // TimeToString
+
+#endif
+
diff --git a/private/net/svcdlls/lls/server/llsutil.h b/private/net/svcdlls/lls/server/llsutil.h
new file mode 100644
index 000000000..ae4feb24a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/llsutil.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ LlsUtil.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
+ running on a given machine.
+
+--*/
+
+
+#ifndef _LLS_LLSUTIL_H
+#define _LLS_LLSUTIL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS EBlock( PVOID Data, ULONG DataSize );
+NTSTATUS DeBlock( PVOID Data, ULONG DataSize );
+
+BOOL FileExists( LPTSTR FileName );
+VOID lsplitpath( const TCHAR *path, TCHAR *drive, TCHAR *dir, TCHAR *fname, TCHAR *ext );
+VOID lmakepath( TCHAR *path, const TCHAR *drive, const TCHAR *dir, const TCHAR *fname, const TCHAR *ext );
+VOID FileBackupCreate( LPTSTR Path );
+HANDLE LlsFileInit( LPTSTR FileName, DWORD Version, DWORD DataSize );
+HANDLE LlsFileCheck( LPTSTR FileName, LPDWORD Version, LPDWORD DataSize );
+
+DWORD DateSystemGet( );
+DWORD DateLocalGet( );
+DWORD InAWorkgroup( VOID );
+VOID LogEvent( DWORD MessageId, DWORD NumberOfSubStrings, LPWSTR *SubStrings, DWORD ErrorCode );
+
+DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber );
+
+#if DBG
+
+LPTSTR TimeToString( ULONG Seconds );
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/makefile b/private/net/svcdlls/lls/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/server/makefile.inc b/private/net/svcdlls/lls/server/makefile.inc
new file mode 100644
index 000000000..fc603fc9b
--- /dev/null
+++ b/private/net/svcdlls/lls/server/makefile.inc
@@ -0,0 +1 @@
+obj\$(TARGET_DIRECTORY)\llssrv.res: $(SOURCES_PATH)llssrv.rc
diff --git a/private/net/svcdlls/lls/server/mapping.c b/private/net/svcdlls/lls/server/mapping.c
new file mode 100644
index 000000000..fe3dc4645
--- /dev/null
+++ b/private/net/svcdlls/lls/server/mapping.c
@@ -0,0 +1,756 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Mapping.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+
+ULONG MappingListSize = 0;
+PMAPPING_RECORD *MappingList = NULL;
+RTL_RESOURCE MappingListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListInit()
+
+/*++
+
+Routine Description:
+
+
+ The table is linear so a binary search can be used on the table. We
+ assume that adding new Mappings is a relatively rare occurance, since
+ we need to sort it each time.
+
+ The Mapping table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+
+ RtlInitializeResource(&MappingListLock);
+
+
+} // MappingListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl MappingListCompare(const void *arg1, const void *arg2) {
+ PMAPPING_RECORD Svc1, Svc2;
+
+ Svc1 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg1);
+ Svc2 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name);
+
+} // MappingListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl MappingUserListCompare(const void *arg1, const void *arg2) {
+ LPTSTR User1, User2;
+
+ User1 = (LPTSTR) *((LPTSTR *) arg1);
+ User2 = (LPTSTR) *((LPTSTR *) arg2);
+
+ return lstrcmpi( User1, User2);
+
+} // MappingUserListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListFind(
+ LPTSTR MappingName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MappingList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to found Mapping table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) MappingListSize - 1;
+ LONG cur;
+ int match;
+ PMAPPING_RECORD Mapping;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListFind\n"));
+#endif
+
+ if ((MappingName == NULL) || (MappingListSize == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Mapping = MappingList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(MappingName, Mapping->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Mapping;
+ }
+
+ return NULL;
+
+} // MappingListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+MappingUserListFind(
+ LPTSTR User,
+ ULONG NumEntries,
+ LPTSTR *Users
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ LPTSTR pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListFind\n"));
+#endif
+
+ if (NumEntries == 0)
+ return NULL;
+
+ end = (LONG) NumEntries - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ pUser = Users[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(User, pUser);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return pUser;
+ }
+
+ return NULL;
+
+} // MappingUserListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListUserFind( LPTSTR User )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PMAPPING_RECORD pMap = NULL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ if (MappingList == NULL)
+ goto MappingListUserFindExit;
+
+ while ((i < MappingListSize) && (pMap == NULL)) {
+ if (MappingUserListFind(User, MappingList[i]->NumMembers, MappingList[i]->Members ) != NULL)
+ pMap = MappingList[i];
+ i++;
+ }
+
+MappingListUserFindExit:
+ RtlReleaseResource(&MappingListLock);
+
+ return pMap;
+} // MappingListUserFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingListAdd(
+ LPTSTR MappingName,
+ LPTSTR Comment,
+ ULONG Licenses
+ )
+
+/*++
+
+Routine Description:
+
+ Adds a Mapping to the Mapping table.
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD NewMapping;
+ LPTSTR NewMappingName;
+ LPTSTR NewComment;
+ PMAPPING_RECORD CurrentRecord = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListAdd\n"));
+#endif
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ CurrentRecord = MappingListFind(MappingName);
+ if (CurrentRecord != NULL) {
+ return CurrentRecord;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (MappingList == NULL)
+ MappingList = (PMAPPING_RECORD *) LocalAlloc(LPTR, sizeof(PMAPPING_RECORD));
+ else
+ MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (MappingList == NULL) {
+ MappingListSize = 0;
+ return NULL;
+ }
+
+ NewMapping = LocalAlloc(LPTR, sizeof(MAPPING_RECORD));
+ if (NewMapping == NULL)
+ return NULL;
+
+ MappingList[MappingListSize] = NewMapping;
+
+ NewMappingName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(MappingName) + 1) * sizeof(TCHAR));
+ if (NewMappingName == NULL) {
+ LocalFree(NewMapping);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewMapping->Name = NewMappingName;
+ lstrcpy(NewMappingName, MappingName);
+
+ //
+ // Allocate space for Comment
+ //
+ NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR));
+ if (NewComment == NULL) {
+ LocalFree(NewMapping);
+ LocalFree(NewMappingName);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewMapping->Comment = NewComment;
+ lstrcpy(NewComment, Comment);
+
+ NewMapping->NumMembers = 0;
+ NewMapping->Members = NULL;
+ NewMapping->Licenses = Licenses;
+ NewMapping->LicenseListSize = 0;
+ NewMapping->LicenseList = NULL;
+ NewMapping->Flags = (LLS_FLAG_LICENSED | LLS_FLAG_SUITE_AUTO);
+
+ MappingListSize++;
+
+ // Have added the entry - now need to sort it in order of the Mapping names
+ qsort((void *) MappingList, (size_t) MappingListSize, sizeof(PMAPPING_RECORD), MappingListCompare);
+
+ return NewMapping;
+
+} // MappingListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListDelete(
+ LPTSTR MappingName
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListDelete\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Make sure there are no members to the mapping
+ //
+ if (Mapping->NumMembers != 0)
+ return STATUS_MEMBER_IN_GROUP;
+
+ //
+ // Check if this is the last Mapping
+ //
+ if (MappingListSize == 1) {
+ LocalFree(Mapping->Name);
+ LocalFree(Mapping->Comment);
+ LocalFree(Mapping);
+ LocalFree(MappingList);
+ MappingListSize = 0;
+ MappingList = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last mapping so find it in the list
+ //
+ i = 0;
+ while ((i < MappingListSize) && (lstrcmpi(MappingList[i]->Name, MappingName)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < MappingListSize) {
+ memcpy(&MappingList[i-1], &MappingList[i], sizeof(PMAPPING_RECORD));
+ i++;
+ }
+
+ MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize - 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ ASSERT(MappingList != NULL);
+ if (MappingList == NULL)
+ MappingListSize = 0;
+ else
+ MappingListSize--;
+
+ //
+ // Now free up the record
+ //
+ LocalFree(Mapping->Name);
+ LocalFree(Mapping->Comment);
+ LocalFree(Mapping);
+
+ return STATUS_SUCCESS;
+
+} // MappingListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+PMAPPING_RECORD
+MappingUserListAdd(
+ LPTSTR MappingName,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ LPTSTR NewName;
+ LPTSTR pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListAdd\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return NULL;
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members);
+
+ if (pUser != NULL) {
+ return Mapping;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (Mapping->Members == NULL)
+ Mapping->Members = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR));
+ else
+ Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (Mapping->Members == NULL) {
+ Mapping->NumMembers = 0;
+ return NULL;
+ }
+
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(User) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ return NULL;
+
+ // now copy it over...
+ Mapping->Members[Mapping->NumMembers] = NewName;
+ lstrcpy(NewName, User);
+
+ Mapping->NumMembers++;
+
+ // Have added the entry - now need to sort it in order of the Mapping names
+ qsort((void *) Mapping->Members, (size_t) Mapping->NumMembers, sizeof(LPTSTR), MappingUserListCompare);
+
+ return Mapping;
+
+} // MappingUserListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingUserListDelete(
+ LPTSTR MappingName,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ MappingName -
+
+Return Value:
+
+ Pointer to added Mapping table entry, or NULL if failed.
+
+--*/
+
+{
+ PMAPPING_RECORD Mapping;
+ LPTSTR pUser;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingUserListDelete\n"));
+#endif
+
+ //
+ // Get mapping record based on name given
+ //
+ Mapping = MappingListFind(MappingName);
+ if (Mapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Find the given user
+ //
+ pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members);
+ if (pUser == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Check if this is the last user
+ //
+ if (Mapping->NumMembers == 1) {
+ LocalFree(pUser);
+ LocalFree(Mapping->Members);
+ Mapping->Members = NULL;
+ Mapping->NumMembers = 0;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last member so find it in the list
+ //
+ i = 0;
+ while ((i < Mapping->NumMembers) && (lstrcmpi(Mapping->Members[i], User)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < Mapping->NumMembers) {
+ memcpy(&Mapping->Members[i-1], &Mapping->Members[i], sizeof(LPTSTR));
+ i++;
+ }
+
+ Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers - 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ ASSERT(Mapping->Members != NULL);
+ if (Mapping->Members == NULL)
+ Mapping->NumMembers = 0;
+ else
+ Mapping->NumMembers--;
+
+ LocalFree(pUser);
+ return STATUS_SUCCESS;
+
+} // MappingUserListDelete
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize);
+ if (MappingList == NULL)
+ goto MappingListDebugDumpExit;
+
+ for (i = 0; i < MappingListSize; i++) {
+ dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"),
+ MappingList[i]->Name, MappingList[i]->Flags, MappingList[i]->LicenseListSize, MappingList[i]->Licenses, MappingList[i]->NumMembers, MappingList[i]->Comment);
+ }
+
+MappingListDebugDumpExit:
+ RtlReleaseResource(&MappingListLock);
+
+ return;
+} // MappingListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PMAPPING_RECORD Mapping = NULL;
+
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+ dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize);
+
+ if (Data == NULL)
+ goto MappingListDebugInfoDumpExit;
+
+ if (MappingList == NULL)
+ goto MappingListDebugInfoDumpExit;
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+ Mapping = MappingListFind((LPTSTR) Data);
+
+ if (Mapping != NULL) {
+ dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"),
+ Mapping->Name, Mapping->Flags, Mapping->LicenseListSize, Mapping->Licenses, Mapping->NumMembers, Mapping->Comment);
+
+ if (Mapping->NumMembers != 0)
+ dprintf(TEXT("\nMembers\n"));
+
+ for (i = 0; i < Mapping->NumMembers; i++)
+ dprintf(TEXT(" %s\n"), Mapping->Members[i]);
+
+ if (Mapping->LicenseListSize != 0)
+ dprintf(TEXT("\nLicenseTable\n"));
+
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"),
+ Mapping->LicenseList[i]->Flags,
+ Mapping->LicenseList[i]->RefCount,
+ Mapping->LicenseList[i]->LicensesNeeded,
+ Mapping->LicenseList[i]->Service->Name );
+
+ } else
+ dprintf(TEXT("Mapping not found: %s\n"), (LPTSTR) Data);
+ }
+
+MappingListDebugInfoDumpExit:
+ RtlReleaseResource(&MappingListLock);
+
+} // MappingListDebugInfoDump
+
+#endif
+
+
diff --git a/private/net/svcdlls/lls/server/mapping.h b/private/net/svcdlls/lls/server/mapping.h
new file mode 100644
index 000000000..29fb4289b
--- /dev/null
+++ b/private/net/svcdlls/lls/server/mapping.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Mapping.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_MAPPING_H
+#define _LLS_MAPPING_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct _USER_LICENSE_RECORD;
+
+typedef struct _MAPPING_RECORD {
+ LPTSTR Name;
+ DWORD Flags;
+ LPTSTR Comment;
+ ULONG Licenses;
+ ULONG NumMembers;
+ LPTSTR *Members;
+
+ ULONG LicenseListSize;
+ struct _USER_LICENSE_RECORD **LicenseList;
+} MAPPING_RECORD, *PMAPPING_RECORD;
+
+
+VOID MappingListInit();
+PMAPPING_RECORD MappingListFind( LPTSTR MappingName );
+LPTSTR MappingUserListFind( LPTSTR User, ULONG NumEntries, LPTSTR *Users );
+PMAPPING_RECORD MappingListAdd( LPTSTR MappingName, LPTSTR Comment, ULONG Licenses );
+NTSTATUS MappingListDelete( LPTSTR MappingName );
+PMAPPING_RECORD MappingUserListAdd( LPTSTR MappingName, LPTSTR User );
+PMAPPING_RECORD MappingListUserFind( LPTSTR User );
+NTSTATUS MappingUserListDelete( LPTSTR MappingName, LPTSTR User );
+
+extern ULONG MappingListSize;
+extern PMAPPING_RECORD *MappingList;
+extern RTL_RESOURCE MappingListLock;
+
+#if DBG
+
+VOID MappingListDebugDump();
+VOID MappingListDebugInfoDump( PVOID Data );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/msvctbl.c b/private/net/svcdlls/lls/server/msvctbl.c
new file mode 100644
index 000000000..44e5a8124
--- /dev/null
+++ b/private/net/svcdlls/lls/server/msvctbl.c
@@ -0,0 +1,712 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ msvctbl.c
+
+Abstract:
+
+ Master Service Table routines. Handles all access to the master service
+ table kept for per-seat information.
+
+ =========================== DATA STRUCTURES =============================
+
+ MasterServiceTable (PMASTER_SERVICE_RECORD *)
+ MasterServiceList (PMASTER_SERVICE_RECORD *)
+ Each of these points to an array of pointers to dynamically allocated
+ MASTER_SERVICE_RECORDs. There is exactly one MASTER_SERVICE_RECORD
+ for each (product, version) pairing; e.g., (SQL 4.0, SNA 2.0,
+ SNA 2.1). The MasterServiceTable is never re-ordered, so a valid
+ index into this table is guaranteed to always dereference to the same
+ (product, version). The MasterServiceList contains the same data
+ sorted lexicographically by product name (and therefore the data
+ pointed to by a specific index may change over time as new
+ (product, version) pairs are added to the table). Each table
+ contains MasterServiceListSize entries.
+
+ RootServiceList (PMASTER_SERVICE_ROOT *)
+ This points to an array of pointers to dynamically allocated
+ MASTER_SERVICE_ROOTs. There is exactly one MASTER_SERVICE_ROOT
+ for each product family. Each MASTER_SERVICE_ROOT contains a
+ pointer to an array of indices into the MasterServiceTable
+ corresponding to all the products in the family, sorted by
+ ascending version number. The RootServiceList itself is
+ sorted lexicographically by ascending family name. It contains
+ RootServiceListSize entries.
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added a few comments.
+ o Added parameter to LicenseServiceListFind().
+ o Fixed benign bug in memory allocation:
+ sizeof(PULONG) -> sizeof(ULONG).
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Master Service Table - Keeps list of products (SQL, SNA, etc.) with a
+// sub-list for each version of the product.
+//
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_SERVICE_TABLE_ENTRIES 10
+
+ULONG RootServiceListSize = 0;
+PMASTER_SERVICE_ROOT *RootServiceList = NULL;
+
+ULONG MasterServiceListSize = 0;
+PMASTER_SERVICE_RECORD *MasterServiceList = NULL;
+PMASTER_SERVICE_RECORD *MasterServiceTable = NULL;
+
+TCHAR BackOfficeStr[100];
+PMASTER_SERVICE_RECORD BackOffice;
+
+
+RTL_RESOURCE MasterServiceListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListInit()
+
+/*++
+
+Routine Description:
+
+ Creates the Master Service table, used for tracking the services and
+ session count. This will pull the initial services from the registry.
+
+ The table is linear so a binary search can be used on the table, so
+ some extra records are initialized so that each time we add a new
+ service we don't have to do a realloc. We also assume that adding
+ new services is a relatively rare occurance, since we need to sort
+ it each time.
+
+ The service table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+ The service table has two default entries for FilePrint and REMOTE_ACCESS.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int nLen;
+ HANDLE LlsDLLHandle = NULL;
+
+ RtlInitializeResource(&MasterServiceListLock);
+
+ memset(BackOfficeStr, 0, sizeof(BackOffice));
+ BackOffice = NULL;
+ LlsDLLHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ ASSERT(LlsDLLHandle != NULL);
+
+ if (LlsDLLHandle != NULL) {
+ nLen = LoadString(LlsDLLHandle, IDS_BACKOFFICE, BackOfficeStr, sizeof(BackOfficeStr));
+
+ if (nLen != 0) {
+ BackOffice = MasterServiceListAdd( BackOfficeStr, BackOfficeStr, 0 );
+ } else {
+#if DBG
+ dprintf(TEXT("LLS ERROR: Could not load BackOffice string\n"));
+#endif
+ }
+
+ }
+
+} // MasterServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort MasterServiceList by product name
+int __cdecl MasterServiceListCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg1);
+ Svc2 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // MasterServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort the Services array of indices pointed to by the
+// MASTER_SERVICE_ROOT structure by product version number
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg1)];
+ Svc2 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg2)];
+
+ return (int) Svc1->Version - Svc2->Version;
+
+} // MServiceRecordCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+// used by qsort to sort the RootServiceList array of product families
+// by family name
+int __cdecl MServiceRootCompare(const void *arg1, const void *arg2) {
+ PMASTER_SERVICE_ROOT Svc1, Svc2;
+
+ Svc1 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg1);
+ Svc2 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // MServiceRootCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_ROOT
+MServiceRootFind(
+ LPTSTR ServiceName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) RootServiceListSize - 1;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_ROOT ServiceRoot;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MServiceRootFind\n"));
+#endif
+
+ if ((RootServiceListSize == 0) || (ServiceName == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ ServiceRoot = RootServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(ServiceName, ServiceRoot->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return ServiceRoot;
+ }
+
+ return NULL;
+
+} // MServiceRootFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_RECORD
+MasterServiceListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on MasterServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MasterServiceListFind\n"));
+#endif
+
+ if ((Name == NULL) || (MasterServiceListSize == 0))
+ return NULL;
+
+ end = (LONG) MasterServiceListSize - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = MasterServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // MasterServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PMASTER_SERVICE_RECORD
+MasterServiceListAdd(
+ LPTSTR FamilyName,
+ LPTSTR Name,
+ DWORD Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ ULONG i;
+ ULONG SessionLimit = 0;
+ BOOL PerSeatLicensing = FALSE;
+ LPTSTR NewServiceName, pDisplayName;
+ PMASTER_SERVICE_RECORD Service = NULL;
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+ PULONG ServiceList;
+ PLICENSE_SERVICE_RECORD pLicense;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MasterServiceListAdd\n"));
+#endif
+
+ //
+ // Mask off low word of version - as it doesn't matter to licensing
+ //
+ Version &= 0xFFFF0000;
+
+ Service = MasterServiceListFind(Name);
+ if (Service != NULL)
+ return Service;
+
+ //
+ // Try to find a root node for that family of products
+ //
+ ServiceRoot = MServiceRootFind(FamilyName);
+
+ if (ServiceRoot == NULL) {
+ //
+ // No root record - so create a new one
+ //
+ if (RootServiceList == NULL)
+ RootServiceList = (PMASTER_SERVICE_ROOT *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_ROOT));
+ else
+ RootServiceList = (PMASTER_SERVICE_ROOT *) LocalReAlloc(RootServiceList, sizeof(PMASTER_SERVICE_ROOT) * (RootServiceListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (RootServiceList == NULL) {
+ ASSERT(FALSE);
+ RootServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Root.
+ //
+ ServiceRoot = (PMASTER_SERVICE_ROOT) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_ROOT));
+ if (ServiceRoot == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ RootServiceList[RootServiceListSize] = ServiceRoot;
+
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(ServiceRoot);
+ return NULL;
+ }
+
+ // now copy it over...
+ ServiceRoot->Name = NewServiceName;
+ lstrcpy(NewServiceName, FamilyName);
+
+ //
+ // Initialize stuff for list of various versions of this product
+ //
+ ServiceRoot->ServiceTableSize = 0;
+ ServiceRoot->Services = NULL;
+ ServiceRoot->Flags = 0;
+
+ RtlInitializeResource(&ServiceRoot->ServiceLock);
+
+ RootServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) RootServiceList, (size_t) RootServiceListSize, sizeof(PMASTER_SERVICE_ROOT), MServiceRootCompare);
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ //
+ // Whether added or found, ServiceRoot points to the Root Node entry.
+ // Now double check to see if another thread just got done adding the
+ // actual service before we got the write lock.
+ //
+ RtlAcquireResourceShared(&ServiceRoot->ServiceLock, TRUE);
+ Service = MasterServiceListFind(Name);
+
+ if (Service == NULL) {
+ //
+ // No Service Record - so create a new one
+ //
+ RtlConvertSharedToExclusive(&ServiceRoot->ServiceLock);
+ ServiceList = ServiceRoot->Services;
+ if (ServiceList == NULL)
+ ServiceList = (PULONG) LocalAlloc(LPTR, sizeof(ULONG));
+ else
+ ServiceList = (PULONG) LocalReAlloc(ServiceList, sizeof(ULONG) * (ServiceRoot->ServiceTableSize + 1), LHND);
+
+ if (MasterServiceList == NULL) {
+ MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
+ MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD));
+ } else {
+ MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceList, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
+ MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceTable, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND);
+ }
+
+ ServiceRoot->Services = ServiceList;
+
+ //
+ // Make sure we could allocate service table
+ //
+ if ((ServiceList == NULL) || (MasterServiceList == NULL) || (MasterServiceTable == NULL)) {
+ ASSERT(FALSE);
+ ServiceRoot->ServiceTableSize = 0;
+ MasterServiceListSize = 0;
+ MasterServiceList = NULL;
+ MasterServiceTable = NULL;
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ //
+ // Allocate space for saving off Service Record.
+ //
+ Service = (PMASTER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ ServiceList[ServiceRoot->ServiceTableSize] = MasterServiceListSize;
+ MasterServiceList[MasterServiceListSize] = Service;
+ MasterServiceTable[MasterServiceListSize] = Service;
+
+ //
+ // ...DisplayName
+ //
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service);
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->Name = NewServiceName;
+ lstrcpy(NewServiceName, Name);
+
+ //
+ // Init rest of values.
+ //
+ Service->Version= Version;
+ Service->LicensesUsed = 0;
+ Service->LicensesClaimed = 0;
+ Service->next = 0;
+ Service->Index = MasterServiceListSize;
+ Service->Family = ServiceRoot;
+
+ pLicense = LicenseServiceListFind(Service->Name, FALSE);
+ if (pLicense == NULL)
+ Service->Licenses = 0;
+ else
+ Service->Licenses = pLicense->NumberLicenses;
+
+ //
+ // Init next pointer
+ //
+ i = 0;
+ while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < Version))
+ i++;
+
+ if (i > 0) {
+ Service->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
+ MasterServiceTable[ServiceRoot->Services[i - 1]]->next = Service->Index + 1;
+ }
+
+ ServiceRoot->ServiceTableSize++;
+ MasterServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the versions
+ qsort((void *) ServiceList, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+
+ // And sort the list the UI uses (sorted by service name)
+ qsort((void *) MasterServiceList, (size_t) MasterServiceListSize, sizeof(PMASTER_SERVICE_RECORD), MasterServiceListCompare);
+ }
+
+ RtlReleaseResource(&ServiceRoot->ServiceLock);
+ return Service;
+
+} // MasterServiceListAdd
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+//
+// DEBUG INFORMATION DUMP ROUTINES
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceRootDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
+ if (RootServiceList == NULL)
+ goto MasterServiceRootDebugDumpExit;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
+ i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
+ }
+
+MasterServiceRootDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceRootDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceRootDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize);
+ if (RootServiceList == NULL)
+ goto MasterServiceRootDebugDumpExit;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"),
+ i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name);
+ }
+
+MasterServiceRootDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceRootDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ dprintf(TEXT("Master Service Table, # Entries: %lu\n"), MasterServiceListSize);
+ if (MasterServiceList == NULL)
+ goto MasterServiceListDebugDumpExit;
+
+ for (i = 0; i < MasterServiceListSize; i++) {
+ dprintf(TEXT("%3lu) [%3lu] LU: %4lu LP: %4lu LC: %4lu MS: %4lu HM: %4lu Next: %3lu Svc: %s %lX\n"),
+ i + 1, MasterServiceList[i]->Index,
+ MasterServiceList[i]->LicensesUsed, MasterServiceList[i]->Licenses, MasterServiceList[i]->LicensesClaimed,
+ MasterServiceList[i]->MaxSessionCount, MasterServiceList[i]->HighMark,
+ MasterServiceList[i]->next, MasterServiceList[i]->Name, MasterServiceList[i]->Version);
+ }
+
+MasterServiceListDebugDumpExit:
+ RtlReleaseResource(&MasterServiceListLock);
+
+ return;
+} // MasterServiceListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MasterServiceListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PMASTER_SERVICE_RECORD CurrentRecord = NULL;
+
+ dprintf(TEXT("Master Service Table, # Entries: %lu\n"), RootServiceListSize);
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+// CurrentRecord = MasterServiceListFind((LPWSTR) Data);
+ if (CurrentRecord != NULL) {
+ }
+ }
+
+} // MasterServiceListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/msvctbl.h b/private/net/svcdlls/lls/server/msvctbl.h
new file mode 100644
index 000000000..84c19a3c0
--- /dev/null
+++ b/private/net/svcdlls/lls/server/msvctbl.h
@@ -0,0 +1,151 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ msvctbl.h
+
+Abstract:
+
+ See msvctbl.c
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added comments.
+
+--*/
+
+
+#ifndef _LLS_MSVCTBL_H
+#define _LLS_MSVCTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define IDS_BACKOFFICE 1500
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The master service record is for license usage tracking. We have
+// A master ROOT record for a family of products (say SQL Server) and
+// a sub linked-list of each of the specific versions in order of the
+// version number. When we do license checking we can move on up the tree
+// to higher level of licenses.
+//
+// There is also a mapping table kept for each of the ROOT records. This
+// tracks if the mapping license count has already been used.
+//
+struct _MASTER_SERVICE_ROOT;
+
+typedef struct _MASTER_SERVICE_RECORD
+{
+ ULONG Index; // index at which
+ // a pointer to this
+ // structure may be
+ // found in the
+ // MasterServiceTable
+
+ LPTSTR Name; // product name
+
+ DWORD Version; // version of the
+ // product;
+ // major.minor ->
+ // (major << 16)
+ // | minor, e.g.,
+ // 5.2 -> 0x50002
+
+ struct _MASTER_SERVICE_ROOT * Family; // pointer to the
+ // product family,
+ // e.g., "SNA 2.1"
+ // -> "SNA"
+
+ ULONG Licenses;
+ ULONG LicensesUsed;
+ ULONG LicensesClaimed;
+
+ ULONG MaxSessionCount;
+ ULONG HighMark;
+
+ ULONG next; // index at which
+ // a pointer to the
+ // next ascending
+ // version of this
+ // product may be
+ // found in the
+ // MasterServiceTable
+ // NOTE: index is
+ // 1-based, so if
+ // next == 0 there
+ // are no more, and
+ // if non-zero then
+ // the next version
+ // is at index next-1
+
+} MASTER_SERVICE_RECORD, *PMASTER_SERVICE_RECORD;
+
+typedef struct _MASTER_SERVICE_ROOT
+{
+ LPTSTR Name; // name of this product family
+
+ DWORD Flags;
+
+ RTL_RESOURCE ServiceLock; // lock for changes to the
+ // Services array (below)
+
+ ULONG ServiceTableSize; // number of entries in Services
+ // array (below)
+
+ ULONG * Services; // array of indices into the
+ // MasterServiceTable of the various
+ // (product,version) pairs
+ // belonging to this family;
+ // sorted in order of ascending
+ // version
+} MASTER_SERVICE_ROOT, *PMASTER_SERVICE_ROOT;
+
+extern ULONG RootServiceListSize;
+extern PMASTER_SERVICE_ROOT *RootServiceList;
+
+extern ULONG MasterServiceListSize;
+extern PMASTER_SERVICE_RECORD *MasterServiceList;
+extern PMASTER_SERVICE_RECORD *MasterServiceTable;
+
+extern RTL_RESOURCE MasterServiceListLock;
+
+extern TCHAR BackOfficeStr[];
+extern PMASTER_SERVICE_RECORD BackOffice;
+
+
+VOID MasterServiceListInit();
+PMASTER_SERVICE_RECORD MServiceRecordFind( DWORD Version, ULONG NumServiceEntries, PULONG ServiceList );
+PMASTER_SERVICE_ROOT MServiceRootFind( LPTSTR ServiceName );
+PMASTER_SERVICE_RECORD MasterServiceListFind( LPTSTR DisplayName );
+PMASTER_SERVICE_RECORD MasterServiceListAdd( LPTSTR FamilyName, LPTSTR Name, DWORD Version );
+
+#if DBG
+
+VOID MasterServiceRootDebugDump();
+VOID MasterServiceRootDebugInfoDump( PVOID Data );
+VOID MasterServiceListDebugDump();
+VOID MasterServiceListDebugInfoDump( PVOID Data );
+
+#endif
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/pack.c b/private/net/svcdlls/lls/server/pack.c
new file mode 100644
index 000000000..4c6d2b01f
--- /dev/null
+++ b/private/net/svcdlls/lls/server/pack.c
@@ -0,0 +1,4306 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ pack.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added new fields to purchase record to support secure certificates.
+ o Unified per server purchase model with per seat purchase model for
+ secure certificates; per server model still done in the traditional
+ manner for non-secure certificates (for backwards compatibility).
+ o Removed assertion on LicenseAdd() failure. LicenseAdd() may
+ legitimately fail under certain circumstances.
+ o Fixed bug wherein a memory allocation failure in the LLS routines
+ would result in a corrupt data file (which would AV the server when
+ it was thereafter read). (Bug #14072.)
+ o Added SaveAll() function analogous to LoadAll().
+ o Added support for extended user data packing/unpacking. This was
+ done to save the SUITE_USE flag across restarts of the service.
+ o Removed user table parameters from unpack routines that didn't use
+ them.
+ o Fixed ServerServiceListUnpack() to subtract out old values only when
+ they were previously added to the MasterServiceTable. This fixes
+ problems with the MaxSessionCount and HighMark tallies getting skewed.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+
+#include "llsrpc_s.h"
+#include "lsapi_s.h"
+#include "llsdbg_s.h"
+#include "repl.h"
+#include "pack.h"
+#include "llsevent.h"
+#include "certdb.h"
+
+
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2);
+
+static HANDLE PurchaseFile;
+
+/////////////////////////////////////////////////////////////////////////
+// License List
+//
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListUnpackOld (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PPACK_LICENSE_PURCHASE_RECORD_0 pLicense;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListUnpackOld: Service[%lu] License[%lu]\n"), LicenseServiceTableSize, LicenseTableSize);
+#endif
+
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ if (pLicense->Service < LicenseServiceTableSize)
+ Status = LicenseAdd(LicenseServices[pLicense->Service].ServiceName, TEXT("Microsoft"), pLicense->NumberLicenses, 0, pLicense->Admin, pLicense->Comment, pLicense->Date, LLS_LICENSE_MODE_ALLOW_PER_SEAT, 0, TEXT("None"), 0, NULL );
+ else {
+ ASSERT(FALSE);
+ }
+
+ if (Status != STATUS_SUCCESS) {
+#ifdef DBG
+ dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status);
+#endif
+ // ASSERT(FALSE);
+ }
+
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+
+} // LicenseListUnpackOld
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListStringsUnpackOld (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseServiceStringSize,
+ LPTSTR LicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses,
+
+ ULONG LicenseStringSize,
+ LPTSTR LicenseStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD_0 pLicense;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ pLicense->Admin = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // LicenseListStringsUnpackOld
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListLoadOld()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD_0 Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER_0 FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListLoad\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(LicenseFileName))
+ goto LicenseListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ if ((Version != LICENSE_FILE_VERSION_0) || (DataSize != sizeof(LICENSE_FILE_HEADER_0))) {
+ Status = STATUS_FILE_INVALID;
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ hFile = PurchaseFile;
+ ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER_0), &BytesRead, NULL);
+
+ LicenseServiceTableSize = 0;
+ LicenseServiceStringSize = 0;
+ LicenseTableSize = 0;
+ LicenseStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize);
+
+ if ( LicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceStringSize != 0) {
+ LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR);
+ LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize);
+
+ if ( LicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseTableSize != 0) {
+ LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize);
+
+ if ( Licenses == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseStringSize != 0) {
+ LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR);
+ LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize);
+
+ if ( LicenseStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.LicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseTableSize != 0) )
+ ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseStringSize != 0) )
+ ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ LicenseListStringsUnpackOld( LicenseServiceTableSize, LicenseServices,
+ LicenseServiceStringSize, LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ LicenseStringSize, LicenseStrings );
+
+ //
+ // Unpack the license data
+ //
+ LicenseListUnpackOld( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses );
+
+LicenseListLoadExit:
+
+ // Note: Don't close the License Purchase File (keep it locked).
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status);
+
+} // LicenseListLoadOld
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListPack (
+ ULONG *pLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD *pLicenseServices,
+
+ ULONG *pLicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD *pLicenses,
+
+ ULONG *pPerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD *pPerServerLicenseServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PLICENSE_SERVICE_RECORD pLicenseService;
+ PLICENSE_PURCHASE_RECORD pLicense;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+ PLICENSE_SERVICE_RECORD pPerServerLicenseService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListPack\n"));
+#endif
+
+ ASSERT(pLicenseServices != NULL);
+ ASSERT(pLicenseServiceTableSize != NULL);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+
+ ASSERT(pLicenses != NULL);
+ ASSERT(pLicenseTableSize != NULL);
+
+ *pLicenses = NULL;
+ *pLicenseTableSize = 0;
+
+ ASSERT(pPerServerLicenseServices != NULL);
+ ASSERT(pPerServerLicenseServiceTableSize != NULL);
+
+ *pPerServerLicenseServices = NULL;
+ *pPerServerLicenseServiceTableSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do License Service Table First
+ //
+ TotalRecords = LicenseServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD));
+ if (LicenseServices == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Services tree
+ //
+ for (i = 0; i < LicenseServiceListSize; i++) {
+ pLicenseService = LicenseServiceList[i];
+
+ //
+ // Make index match table in it's current state
+ //
+ pLicenseService->Index = i;
+
+ LicenseServices[i].ServiceName = pLicenseService->ServiceName;
+ LicenseServices[i].NumberLicenses = pLicenseService->NumberLicenses;
+ }
+ }
+
+ *pLicenseServices = LicenseServices;
+ *pLicenseServiceTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Per Server License Service Table
+ //
+ TotalRecords = PerServerLicenseServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0)
+ {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ PerServerLicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD));
+ if (PerServerLicenseServices == NULL)
+ {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the Per Server License Services tree
+ //
+ for (i = 0; i < PerServerLicenseServiceListSize; i++)
+ {
+ pPerServerLicenseService = PerServerLicenseServiceList[i];
+
+ //
+ // Make index match table in it's current state
+ //
+ pPerServerLicenseService->Index = i;
+
+ PerServerLicenseServices[i].ServiceName = pPerServerLicenseService->ServiceName;
+ PerServerLicenseServices[i].NumberLicenses = pPerServerLicenseService->NumberLicenses;
+ }
+ }
+
+ *pPerServerLicenseServices = PerServerLicenseServices;
+ *pPerServerLicenseServiceTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do License Purchase Records
+ //
+ TotalRecords = PurchaseListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Licenses = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_PURCHASE_RECORD));
+ if (Licenses == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ *pLicenseServices = NULL;
+ *pLicenseServiceTableSize = 0;
+ *pPerServerLicenseServices = NULL;
+ *pPerServerLicenseServiceTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Purchase tree
+ //
+ for (i = 0; i < PurchaseListSize; i++) {
+ pLicense = &PurchaseList[i];
+
+ //
+ // License Service table index is fixed-up to what we need
+ //
+ Licenses[i].Service = ( pLicense->AllowedModes & 1 ) ? pLicense->Service->Index
+ : 0xFFFFFFFF;
+ Licenses[i].NumberLicenses = pLicense->NumberLicenses;
+ Licenses[i].Date = pLicense->Date;
+ Licenses[i].Admin = pLicense->Admin;
+ Licenses[i].Comment = pLicense->Comment;
+
+ Licenses[i].PerServerService = ( pLicense->AllowedModes & 2 ) ? pLicense->PerServerService->Index
+ : 0xFFFFFFFF;
+ Licenses[i].AllowedModes = pLicense->AllowedModes;
+ Licenses[i].CertificateID = pLicense->CertificateID;
+ Licenses[i].Source = pLicense->Source;
+ Licenses[i].ExpirationDate = pLicense->ExpirationDate;
+ Licenses[i].MaxQuantity = pLicense->MaxQuantity;
+ Licenses[i].Vendor = pLicense->Vendor;
+ memcpy( Licenses[i].Secrets, pLicense->Secrets, LLS_NUM_SECRETS * sizeof( *pLicense->Secrets ) );
+ }
+ }
+
+ *pLicenses = Licenses;
+ *pLicenseTableSize = TotalRecords;
+ return Status;
+} // LicenseListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListUnpack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ LPTSTR ServiceName;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListUnpack: Service[%lu] PerServerService[%lu] License[%lu]\n"), LicenseServiceTableSize, PerServerLicenseServiceTableSize, LicenseTableSize);
+#endif
+
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ for (i = 0; i < LicenseTableSize; i++)
+ {
+ pLicense = &Licenses[i];
+
+ ServiceName = NULL;
+
+ if ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ {
+ if ( pLicense->PerServerService < PerServerLicenseServiceTableSize )
+ {
+ ServiceName = PerServerLicenseServices[ pLicense->PerServerService ].ServiceName;
+ }
+ else
+ {
+ ASSERT( FALSE );
+ }
+ }
+
+ if ( ( NULL == ServiceName ) && ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ if ( pLicense->Service < LicenseServiceTableSize )
+ {
+ ServiceName = LicenseServices[ pLicense->Service ].ServiceName;
+ }
+ else
+ {
+ ASSERT( FALSE );
+ }
+ }
+
+ if ( NULL == ServiceName )
+ {
+ ASSERT( FALSE );
+ }
+ else
+ {
+ Status = LicenseAdd( ServiceName, pLicense->Vendor, pLicense->NumberLicenses, pLicense->MaxQuantity, pLicense->Admin, pLicense->Comment, pLicense->Date, pLicense->AllowedModes, pLicense->CertificateID, pLicense->Source, pLicense->ExpirationDate, pLicense->Secrets );
+
+ if (Status != STATUS_SUCCESS)
+ {
+#ifdef DBG
+ dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status);
+#endif
+ // ASSERT(FALSE);
+ }
+ }
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+
+} // LicenseListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListStringsPack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG *pLicenseServiceStringSize,
+ LPTSTR *pLicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG *pLicenseStringSize,
+ LPTSTR *pLicenseStrings,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices,
+
+ ULONG *pPerServerLicenseServiceStringSize,
+ LPTSTR *pPerServerLicenseServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ LPTSTR LicenseServiceStrings = NULL;
+ LPTSTR LicenseStrings = NULL;
+ TCHAR *pStr;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsPack\n"));
+#endif
+
+ ASSERT(pLicenseServiceStrings != NULL);
+ ASSERT(pLicenseServiceStringSize != NULL);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+
+ ASSERT(pLicenseStrings != NULL);
+ ASSERT(pLicenseStringSize != NULL);
+
+ *pLicenseStrings = NULL;
+ *pLicenseStringSize = 0;
+
+ ASSERT(pPerServerLicenseServiceStrings != NULL);
+ ASSERT(pPerServerLicenseServiceStringSize != NULL);
+
+ *pPerServerLicenseServiceStrings = NULL;
+ *pPerServerLicenseServiceStringSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do License Service Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (LicenseServiceStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ lstrcpy(pStr, pSvc->ServiceName);
+ pStr = &pStr[lstrlen(pSvc->ServiceName) + 1];
+ }
+ }
+
+ *pLicenseServiceStrings = LicenseServiceStrings;
+ *pLicenseServiceStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Per Server License Service Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++) {
+ pSvc = &PerServerLicenseServices[i];
+
+ StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ PerServerLicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (PerServerLicenseServiceStrings == NULL)
+ {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = PerServerLicenseServiceStrings;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++)
+ {
+ pSvc = &PerServerLicenseServices[i];
+
+ lstrcpy(pStr, pSvc->ServiceName);
+ pStr = &pStr[lstrlen(pSvc->ServiceName) + 1];
+ }
+ }
+
+ *pPerServerLicenseServiceStrings = PerServerLicenseServiceStrings;
+ *pPerServerLicenseServiceStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do License Purchase Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ StringSize = StringSize + lstrlen(pLicense->Vendor) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Admin) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Comment) + 1;
+ StringSize = StringSize + lstrlen(pLicense->Source) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ LicenseStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (LicenseStrings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ *pLicenseServiceStrings = NULL;
+ *pLicenseServiceStringSize = 0;
+ *pPerServerLicenseServiceStrings = NULL;
+ *pPerServerLicenseServiceStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ lstrcpy(pStr, pLicense->Vendor);
+ pStr = &pStr[lstrlen(pLicense->Vendor) + 1];
+
+ lstrcpy(pStr, pLicense->Admin);
+ pStr = &pStr[lstrlen(pLicense->Admin) + 1];
+
+ lstrcpy(pStr, pLicense->Comment);
+ pStr = &pStr[lstrlen(pLicense->Comment) + 1];
+
+ lstrcpy(pStr, pLicense->Source);
+ pStr = &pStr[lstrlen(pLicense->Source) + 1];
+ }
+ }
+
+ *pLicenseStrings = LicenseStrings;
+ *pLicenseStringSize = StringSize;
+
+ return Status;
+} // LicenseListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListStringsUnpack (
+ ULONG LicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices,
+
+ ULONG LicenseServiceStringSize,
+ LPTSTR LicenseServiceStrings,
+
+ ULONG LicenseTableSize,
+ PPACK_LICENSE_PURCHASE_RECORD Licenses,
+
+ ULONG LicenseStringSize,
+ LPTSTR LicenseStrings,
+
+ ULONG PerServerLicenseServiceTableSize,
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices,
+
+ ULONG PerServerLicenseServiceStringSize,
+ LPTSTR PerServerLicenseServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_LICENSE_SERVICE_RECORD pSvc;
+ PPACK_LICENSE_PURCHASE_RECORD pLicense;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LicenseListStringsUnpack\n"));
+#endif
+
+ //
+ // First do per seat license service strings
+ //
+ pStr = LicenseServiceStrings;
+ for (i = 0; i < LicenseServiceTableSize; i++) {
+ pSvc = &LicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Then do per server license service strings
+ //
+ pStr = PerServerLicenseServiceStrings;
+ for (i = 0; i < PerServerLicenseServiceTableSize; i++) {
+ pSvc = &PerServerLicenseServices[i];
+
+ pSvc->ServiceName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = LicenseStrings;
+ for (i = 0; i < LicenseTableSize; i++) {
+ pLicense = &Licenses[i];
+
+ pLicense->Vendor = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Admin = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pLicense->Source = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // LicenseListStringsUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG PerServerLicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+
+ ULONG PerServerLicenseServiceStringSize;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListLoad\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(LicenseFileName))
+ goto LicenseListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ if ( ( Version == LICENSE_FILE_VERSION_0 ) && ( DataSize == sizeof(LICENSE_FILE_HEADER_0) ) ) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+
+ LicenseListLoadOld();
+ return;
+ }
+
+ if ( ( Version != LICENSE_FILE_VERSION ) || ( DataSize != sizeof(LICENSE_FILE_HEADER) ) ) {
+ Status = STATUS_FILE_INVALID;
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ hFile = PurchaseFile;
+ ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesRead, NULL);
+
+ LicenseServiceTableSize = 0;
+ LicenseServiceStringSize = 0;
+ LicenseTableSize = 0;
+ LicenseStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize);
+
+ if ( LicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceStringSize != 0) {
+ LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR);
+ LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize);
+
+ if ( LicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseServiceTableSize != 0) {
+ PerServerLicenseServiceTableSize = FileHeader.PerServerLicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD);
+ PerServerLicenseServices = MIDL_user_allocate(FileHeader.PerServerLicenseServiceTableSize);
+
+ if ( PerServerLicenseServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.PerServerLicenseServiceStringSize != 0) {
+ PerServerLicenseServiceStringSize = FileHeader.PerServerLicenseServiceStringSize / sizeof(TCHAR);
+ PerServerLicenseServiceStrings = MIDL_user_allocate(FileHeader.PerServerLicenseServiceStringSize);
+
+ if ( PerServerLicenseServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseTableSize != 0) {
+ LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize);
+
+ if ( Licenses == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ if (FileHeader.LicenseStringSize != 0) {
+ LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR);
+ LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize);
+
+ if ( LicenseStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LicenseListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.LicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.PerServerLicenseServiceTableSize != 0) )
+ ret = ReadFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.PerServerLicenseServiceStringSize != 0) )
+ ret = ReadFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseTableSize != 0) )
+ ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.LicenseStringSize != 0) )
+ ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LicenseListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ LicenseListStringsUnpack( LicenseServiceTableSize, LicenseServices,
+ LicenseServiceStringSize, LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ LicenseStringSize, LicenseStrings,
+ PerServerLicenseServiceTableSize, PerServerLicenseServices,
+ PerServerLicenseServiceStringSize, PerServerLicenseServiceStrings
+ );
+
+ //
+ // Unpack the license data
+ //
+ LicenseListUnpack( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses, PerServerLicenseServiceTableSize, PerServerLicenseServices );
+
+LicenseListLoadExit:
+
+ // Note: Don't close the License Purchase File (keep it locked).
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status);
+
+} // LicenseListLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG LicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL;
+
+ ULONG LicenseServiceStringSize;
+ LPTSTR LicenseServiceStrings = NULL;
+
+ ULONG PerServerLicenseServiceTableSize;
+ PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL;
+
+ ULONG PerServerLicenseServiceStringSize;
+ LPTSTR PerServerLicenseServiceStrings = NULL;
+
+ ULONG LicenseTableSize;
+ PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL;
+
+ ULONG LicenseStringSize;
+ LPTSTR LicenseStrings = NULL;
+
+ LICENSE_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LicenseListSave\n"));
+#endif
+
+ //
+ // Check if we already have file open
+ //
+ if (PurchaseFile != NULL) {
+ CloseHandle(PurchaseFile);
+ PurchaseFile = NULL;
+ }
+
+ RtlAcquireResourceExclusive(&LicenseListLock, TRUE);
+
+ //
+ // If nothing to save then get-out
+ //
+ if ( (LicenseServiceListSize == 0) && (PerServerLicenseServiceListSize == 0) )
+ goto LicenseListSaveExit;
+
+ //
+ // Pack the license data
+ //
+ Status = LicenseListPack( &LicenseServiceTableSize, &LicenseServices, &LicenseTableSize, &Licenses, &PerServerLicenseServiceTableSize, &PerServerLicenseServices );
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = LicenseListStringsPack( LicenseServiceTableSize, LicenseServices,
+ &LicenseServiceStringSize, &LicenseServiceStrings,
+ LicenseTableSize, Licenses,
+ &LicenseStringSize, &LicenseStrings,
+ PerServerLicenseServiceTableSize, PerServerLicenseServices,
+ &PerServerLicenseServiceStringSize, &PerServerLicenseServiceStrings );
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.LicenseServiceTableSize = LicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD);
+ FileHeader.LicenseServiceStringSize = LicenseServiceStringSize * sizeof(TCHAR);
+ FileHeader.PerServerLicenseServiceTableSize = PerServerLicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD);
+ FileHeader.PerServerLicenseServiceStringSize = PerServerLicenseServiceStringSize * sizeof(TCHAR);
+ FileHeader.LicenseTableSize = LicenseTableSize * sizeof(PACK_LICENSE_PURCHASE_RECORD);
+ FileHeader.LicenseStringSize = LicenseStringSize * sizeof(TCHAR);
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(LicenseServices, FileHeader.LicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Licenses, FileHeader.LicenseTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(LicenseStrings, FileHeader.LicenseStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LicenseListSaveExit;
+
+ //
+ // Save out the header record
+ //
+ PurchaseFile = LlsFileInit(LicenseFileName, LICENSE_FILE_VERSION, sizeof(LICENSE_FILE_HEADER) );
+ if (PurchaseFile == NULL) {
+ Status = GetLastError();
+ goto LicenseListSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ hFile = PurchaseFile;
+
+ ret = WriteFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (LicenseServices != NULL) && (FileHeader.LicenseServiceTableSize != 0))
+ ret = WriteFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (LicenseServiceStrings != NULL) && (FileHeader.LicenseServiceStringSize != 0))
+ ret = WriteFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (PerServerLicenseServices != NULL) && (FileHeader.PerServerLicenseServiceTableSize != 0))
+ ret = WriteFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (PerServerLicenseServiceStrings != NULL) && (FileHeader.PerServerLicenseServiceStringSize != 0))
+ ret = WriteFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (Licenses != NULL) && (FileHeader.LicenseTableSize != 0))
+ ret = WriteFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesWritten, NULL);
+
+ if (ret && (LicenseStrings != NULL) && (FileHeader.LicenseStringSize != 0))
+ ret = WriteFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+LicenseListSaveExit:
+ RtlReleaseResource(&LicenseListLock);
+
+ // Note: Don't close the License Purchase File (keep it locked).
+ if (hFile != NULL)
+ FlushFileBuffers(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (LicenseServices != NULL)
+ MIDL_user_free(LicenseServices);
+
+ if (LicenseServiceStrings != NULL)
+ MIDL_user_free(LicenseServiceStrings);
+
+ if (PerServerLicenseServices != NULL)
+ MIDL_user_free(PerServerLicenseServices);
+
+ if (PerServerLicenseServiceStrings != NULL)
+ MIDL_user_free(PerServerLicenseServiceStrings);
+
+ if (Licenses != NULL)
+ MIDL_user_free(Licenses);
+
+ if (LicenseStrings != NULL)
+ MIDL_user_free(LicenseStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_LICENSE, 0, NULL, Status);
+
+ return Status;
+} // LicenseListSave
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Mapping List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListPack (
+ ULONG *pMappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD *pMappingUsers,
+
+ ULONG *pMappingTableSize,
+ PPACK_MAPPING_RECORD *pMappings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PMAPPING_RECORD pMapping;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingListPack\n"));
+#endif
+
+ ASSERT(pMappingUsers != NULL);
+ ASSERT(pMappingUserTableSize != NULL);
+
+ *pMappingUsers = NULL;
+ *pMappingUserTableSize = 0;
+
+ ASSERT(pMappings != NULL);
+ ASSERT(pMappingTableSize != NULL);
+
+ *pMappings = NULL;
+ *pMappingTableSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Mapping User Table First
+ //
+ TotalRecords = 0;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ for (i = 0; i < MappingListSize; i++)
+ TotalRecords += MappingList[i]->NumMembers;
+
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingUsers = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_USER_RECORD));
+ if (MappingUsers == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the Mapping tree
+ //
+ k = 0;
+ for (i = 0; i < MappingListSize; i++) {
+ pMapping = MappingList[i];
+
+ for (j = 0; j < pMapping->NumMembers; j++) {
+ MappingUsers[k].Mapping = i;
+ MappingUsers[k].Name = pMapping->Members[j];
+ k++;
+ }
+ }
+ }
+
+ *pMappingUsers = MappingUsers;
+ *pMappingUserTableSize = TotalRecords;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Mapping Records
+ //
+ TotalRecords = MappingListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Mappings = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_RECORD));
+ if (Mappings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ *pMappingUsers = NULL;
+ *pMappingUserTableSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the License Purchase tree
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ pMapping = MappingList[i];
+
+ Mappings[i].Name = pMapping->Name;
+ Mappings[i].Comment = pMapping->Comment;
+ Mappings[i].Licenses = pMapping->Licenses;
+ }
+ }
+
+ *pMappings = Mappings;
+ *pMappingTableSize = TotalRecords;
+ return Status;
+} // MappingListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListUnpack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ ULONG i;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListUnpack: Mappings[%lu] TotalUsers[%lu]\n"), MappingTableSize, MappingUserTableSize);
+#endif
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ //
+ // Add the Mappings first
+ //
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ pMap = MappingListAdd(pMapping->Name, pMapping->Comment, pMapping->Licenses);
+
+ ASSERT(pMap != NULL);
+ }
+
+ //
+ // Now add the users to the mappings...
+ //
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ pMap = NULL;
+ if (pUsr->Mapping < MappingTableSize)
+ pMap = MappingUserListAdd(Mappings[pUsr->Mapping].Name, pUsr->Name);
+
+#if DBG
+ if (pMap == NULL) {
+ dprintf(TEXT("pMap: 0x%lX pUsr->Mapping: %lu MappingTableSize: %lu\n"), pMap, pUsr->Mapping, MappingTableSize);
+ ASSERT(FALSE);
+ }
+#endif
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+} // MappingListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListStringsPack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG *pMappingUserStringSize,
+ LPTSTR *pMappingUserStrings,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings,
+
+ ULONG *pMappingStringSize,
+ LPTSTR *pMappingStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ LPTSTR MappingUserStrings = NULL;
+ LPTSTR MappingStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListStringsPack\n"));
+#endif
+
+ ASSERT(pMappingUserStrings != NULL);
+ ASSERT(pMappingUserStringSize != NULL);
+
+ *pMappingUserStrings = NULL;
+ *pMappingUserStringSize = 0;
+
+ ASSERT(pMappingStrings != NULL);
+ ASSERT(pMappingStringSize != NULL);
+
+ *pMappingStrings = NULL;
+ *pMappingStringSize = 0;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Do Mapping User Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ StringSize = StringSize + lstrlen(pUsr->Name) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingUserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (MappingUserStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = MappingUserStrings;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ lstrcpy(pStr, pUsr->Name);
+ pStr = &pStr[lstrlen(pUsr->Name) + 1];
+ }
+ }
+
+ *pMappingUserStrings = MappingUserStrings;
+ *pMappingUserStringSize = StringSize;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Now Do Mapping Strings
+ //
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ StringSize = StringSize + lstrlen(pMapping->Name) + 1;
+ StringSize = StringSize + lstrlen(pMapping->Comment) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ MappingStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (MappingStrings == NULL) {
+ ASSERT(FALSE);
+
+ //
+ // Clean up already alloc'd information
+ //
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ *pMappingUserStrings = NULL;
+ *pMappingUserStringSize = 0;
+
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = MappingStrings;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ lstrcpy(pStr, pMapping->Name);
+ pStr = &pStr[lstrlen(pMapping->Name) + 1];
+
+ lstrcpy(pStr, pMapping->Comment);
+ pStr = &pStr[lstrlen(pMapping->Comment) + 1];
+ }
+ }
+
+ *pMappingStrings = MappingStrings;
+ *pMappingStringSize = StringSize;
+
+ return Status;
+} // MappingListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListStringsUnpack (
+ ULONG MappingUserTableSize,
+ PPACK_MAPPING_USER_RECORD MappingUsers,
+
+ ULONG MappingUserStringSize,
+ LPTSTR MappingUserStrings,
+
+ ULONG MappingTableSize,
+ PPACK_MAPPING_RECORD Mappings,
+
+ ULONG MappingStringSize,
+ LPTSTR MappingStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PPACK_MAPPING_USER_RECORD pUsr;
+ PPACK_MAPPING_RECORD pMapping;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("MappingListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = MappingUserStrings;
+ for (i = 0; i < MappingUserTableSize; i++) {
+ pUsr = &MappingUsers[i];
+
+ pUsr->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+ //
+ // Now do license purchase strings
+ //
+ pStr = MappingStrings;
+ for (i = 0; i < MappingTableSize; i++) {
+ pMapping = &Mappings[i];
+
+ pMapping->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pMapping->Comment = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // MappingListStringsUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingListLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG MappingUserTableSize;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+
+ ULONG MappingUserStringSize;
+ LPTSTR MappingUserStrings = NULL;
+
+ ULONG MappingTableSize;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+
+ ULONG MappingStringSize;
+ LPTSTR MappingStrings = NULL;
+
+ MAPPING_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: MappingListLoad\n"));
+#endif
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(MappingFileName))
+ goto MappingListLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ hFile = LlsFileCheck(MappingFileName, &Version, &DataSize );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto MappingListLoadExit;
+ }
+
+ if ((Version != MAPPING_FILE_VERSION) || (DataSize != sizeof(MAPPING_FILE_HEADER))) {
+ Status = STATUS_FILE_INVALID;
+ goto MappingListLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ ret = ReadFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesRead, NULL);
+
+ MappingUserTableSize = 0;
+ MappingUserStringSize = 0;
+ MappingTableSize = 0;
+ MappingStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.MappingUserTableSize != 0) {
+ MappingUserTableSize = FileHeader.MappingUserTableSize / sizeof(PACK_MAPPING_USER_RECORD);
+ MappingUsers = MIDL_user_allocate(FileHeader.MappingUserTableSize);
+
+ if ( MappingUsers == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingUserStringSize != 0) {
+ MappingUserStringSize = FileHeader.MappingUserStringSize / sizeof(TCHAR);
+ MappingUserStrings = MIDL_user_allocate(FileHeader.MappingUserStringSize);
+
+ if ( MappingUserStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingTableSize != 0) {
+ MappingTableSize = FileHeader.MappingTableSize / sizeof(PACK_MAPPING_RECORD);
+ Mappings = MIDL_user_allocate(FileHeader.MappingTableSize);
+
+ if ( Mappings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ if (FileHeader.MappingStringSize != 0) {
+ MappingStringSize = FileHeader.MappingStringSize / sizeof(TCHAR);
+ MappingStrings = MIDL_user_allocate(FileHeader.MappingStringSize);
+
+ if ( MappingStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto MappingListLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.MappingUserTableSize != 0) )
+ ret = ReadFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingUserStringSize != 0) )
+ ret = ReadFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingTableSize != 0) )
+ ret = ReadFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.MappingStringSize != 0) )
+ ret = ReadFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto MappingListLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(MappingUsers, FileHeader.MappingUserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(MappingUserStrings, FileHeader.MappingUserStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Mappings, FileHeader.MappingTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(MappingStrings, FileHeader.MappingStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ MappingListStringsUnpack( MappingUserTableSize, MappingUsers,
+ MappingUserStringSize, MappingUserStrings,
+ MappingTableSize, Mappings,
+ MappingStringSize, MappingStrings );
+
+ //
+ // Unpack the data
+ //
+ MappingListUnpack( MappingUserTableSize, MappingUsers, MappingTableSize, Mappings );
+
+MappingListLoadExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ if (Mappings != NULL)
+ MIDL_user_free(Mappings);
+
+ if (MappingStrings != NULL)
+ MIDL_user_free(MappingStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_MAPPING, 0, NULL, Status);
+
+} // MappingListLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MappingListSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG MappingUserTableSize;
+ PPACK_MAPPING_USER_RECORD MappingUsers = NULL;
+
+ ULONG MappingUserStringSize;
+ LPTSTR MappingUserStrings = NULL;
+
+ ULONG MappingTableSize;
+ PPACK_MAPPING_RECORD Mappings = NULL;
+
+ ULONG MappingStringSize;
+ LPTSTR MappingStrings = NULL;
+
+ MAPPING_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: MappingListSave\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ //
+ // If nothing to save then get-out
+ //
+ if (MappingListSize == 0)
+ goto MappingListSaveExit;
+
+ //
+ // Pack the data
+ //
+ Status = MappingListPack( &MappingUserTableSize, &MappingUsers, &MappingTableSize, &Mappings );
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = MappingListStringsPack( MappingUserTableSize, MappingUsers,
+ &MappingUserStringSize, &MappingUserStrings,
+ MappingTableSize, Mappings,
+ &MappingStringSize, &MappingStrings );
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.MappingUserTableSize = MappingUserTableSize * sizeof(PACK_MAPPING_USER_RECORD);
+ FileHeader.MappingUserStringSize = MappingUserStringSize * sizeof(TCHAR);
+ FileHeader.MappingTableSize = MappingTableSize * sizeof(PACK_MAPPING_RECORD);
+ FileHeader.MappingStringSize = MappingStringSize * sizeof(TCHAR);
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(MappingUsers, FileHeader.MappingUserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(MappingUserStrings, FileHeader.MappingUserStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Mappings, FileHeader.MappingTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(MappingStrings, FileHeader.MappingStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto MappingListSaveExit;
+
+ //
+ // Save out the header record
+ //
+ hFile = LlsFileInit(MappingFileName, MAPPING_FILE_VERSION, sizeof(MAPPING_FILE_HEADER) );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto MappingListSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ ret = WriteFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (MappingUsers != NULL) && (FileHeader.MappingUserTableSize != 0))
+ ret = WriteFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesWritten, NULL);
+
+ if (ret && (MappingUserStrings != NULL) && (FileHeader.MappingUserStringSize != 0))
+ ret = WriteFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesWritten, NULL);
+
+ if (ret && (Mappings != NULL) && (FileHeader.MappingTableSize != 0))
+ ret = WriteFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesWritten, NULL);
+
+ if (ret && (MappingStrings != NULL) && (FileHeader.MappingStringSize != 0))
+ ret = WriteFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+MappingListSaveExit:
+ RtlReleaseResource(&MappingListLock);
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (MappingUsers != NULL)
+ MIDL_user_free(MappingUsers);
+
+ if (MappingUserStrings != NULL)
+ MIDL_user_free(MappingUserStrings);
+
+ if (Mappings != NULL)
+ MIDL_user_free(Mappings);
+
+ if (MappingStrings != NULL)
+ MIDL_user_free(MappingStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_MAPPING, 0, NULL, Status);
+
+ return Status;
+} // MappingListSave
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// User List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+UserListPack (
+ DWORD LastReplicated,
+ ULONG UserLevel,
+ ULONG *pUserTableSize,
+ LPVOID *pUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ LPVOID Users = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PUSER_RECORD pUser;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListPack\n"));
+#endif
+
+ ASSERT(pUsers != NULL);
+ ASSERT(pUserTableSize != NULL);
+
+ *pUsers = NULL;
+ *pUserTableSize = 0;
+
+ //
+ // Now walk our tree and figure out how many records we must send.
+ //
+ i = 0;
+ TotalRecords = 0;
+ while (i < UserListNumEntries) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // Walk each service under each user
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if ( (pUser->Services[j].AccessCount > 0) || (pUser->Services[j].LastAccess > LastReplicated) )
+ TotalRecords++;
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ i++;
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT(" LLS Packing %lu User Records\n"), TotalRecords);
+#endif
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Users = MIDL_user_allocate(TotalRecords * ( UserLevel ? sizeof(REPL_USER_RECORD_1)
+ : sizeof(REPL_USER_RECORD_0) ) );
+ if (Users == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ i = 0;
+ j = 0;
+ while ((i < UserListNumEntries) && (j < TotalRecords)) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // Walk each service under each user
+ //
+ k = 0;
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ while (k < pUser->ServiceTableSize) {
+ if ( (pUser->Services[k].AccessCount > 0) || (pUser->Services[k].LastAccess > LastReplicated) ) {
+ if ( 0 == UserLevel )
+ {
+ ((PREPL_USER_RECORD_0)Users)[j].Name = pUser->UserID;
+ ((PREPL_USER_RECORD_0)Users)[j].Service = pUser->Services[k].Service->Index;
+ ((PREPL_USER_RECORD_0)Users)[j].AccessCount = pUser->Services[k].AccessCount;
+ ((PREPL_USER_RECORD_0)Users)[j].LastAccess = pUser->Services[k].LastAccess;
+ }
+ else
+ {
+ ((PREPL_USER_RECORD_1)Users)[j].Name = pUser->UserID;
+ ((PREPL_USER_RECORD_1)Users)[j].Service = pUser->Services[k].Service->Index;
+ ((PREPL_USER_RECORD_1)Users)[j].AccessCount = pUser->Services[k].AccessCount;
+ ((PREPL_USER_RECORD_1)Users)[j].LastAccess = pUser->Services[k].LastAccess;
+ ((PREPL_USER_RECORD_1)Users)[j].Flags = pUser->Flags;
+ }
+
+ //
+ // Reset access count so we don't increment forever
+ //
+ if (LastReplicated != 0)
+ pUser->Services[k].AccessCount = 0;
+
+ j++;
+ }
+
+ k++;
+ }
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ i++;
+ }
+ } // User Records
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("UserListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pUsers = Users;
+ *pUserTableSize = TotalRecords;
+ return Status;
+} // UserListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices,
+
+ ULONG UserLevel,
+ ULONG UserTableSize,
+ LPVOID Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ PUSER_RECORD pUser;
+ PREPL_USER_RECORD_0 pReplUser0 = NULL;
+ PREPL_USER_RECORD_1 pReplUser1 = NULL;
+ PADD_CACHE pAdd = NULL;
+ PADD_CACHE tAdd = NULL;
+ PADD_CACHE lAdd = NULL;
+ ULONG CacheSize = 0;
+ ULONG DataLength;
+ LPTSTR NewName;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("UserListUnpack: [%lu]\n"), UserTableSize);
+#endif
+ //
+ // Walk User table. First fixup service pointers to our local service
+ // table. Next create a big add cache list to dump onto our add-cache
+ // queue.
+ //
+ for (i = 0; i < UserTableSize; i++) {
+ //
+ // Update Index
+ //
+ if ( 0 == UserLevel )
+ {
+ pReplUser0 = &( (PREPL_USER_RECORD_0) Users)[i];
+ pReplUser0->Service = Services[pReplUser0->Service].Index;
+ }
+ else
+ {
+ pReplUser1 = &( (PREPL_USER_RECORD_1) Users)[i];
+ pReplUser1->Service = Services[pReplUser1->Service].Index;
+ }
+
+ //
+ // Now create Add Cache object
+ //
+ tAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE));
+ if (tAdd != NULL) {
+ if ( 0 == UserLevel )
+ {
+ DataLength = (lstrlen(pReplUser0->Name) + 1) * sizeof(TCHAR);
+ }
+ else
+ {
+ DataLength = (lstrlen(pReplUser1->Name) + 1) * sizeof(TCHAR);
+ }
+
+ NewName = LocalAlloc( LPTR, DataLength);
+
+ if (NewName == NULL) {
+ LocalFree(pAdd);
+ ASSERT(FALSE);
+ } else {
+ tAdd->Data = NewName;
+ tAdd->DataType = DATA_TYPE_USERNAME;
+ tAdd->DataLength = DataLength;
+
+ if ( 0 == UserLevel )
+ {
+ lstrcpy( NewName, pReplUser0->Name );
+ tAdd->AccessCount = pReplUser0->AccessCount;
+ tAdd->LastAccess = pReplUser0->LastAccess;
+ tAdd->Flags = LLS_FLAG_SUITE_AUTO;
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ tAdd->Service = MasterServiceTable[pReplUser0->Service];
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+ else
+ {
+ lstrcpy( NewName, pReplUser1->Name );
+ tAdd->AccessCount = pReplUser1->AccessCount;
+ tAdd->LastAccess = pReplUser1->LastAccess;
+ tAdd->Flags = pReplUser1->Flags & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO );
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ tAdd->Service = MasterServiceTable[pReplUser1->Service];
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+
+ //
+ // Now add it to our cache
+ //
+ tAdd->prev = pAdd;
+ pAdd = tAdd;
+
+ //
+ // Keep track of first on (bottom on stack) so we can append
+ // it onto the real add cache.
+ //
+ if (lAdd == NULL)
+ lAdd = pAdd;
+
+ CacheSize++;
+ }
+ } else
+ ASSERT(FALSE);
+ }
+
+ //
+ // Now that we've walked through all the users - update the actual
+ // Add Cache.
+ //
+ if (pAdd != NULL) {
+ RtlEnterCriticalSection(&AddCacheLock);
+ lAdd->prev = AddCache;
+ AddCache = pAdd;
+ AddCacheSize += CacheSize;
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ //
+ // Now must signal the event so we can pull off the new record.
+ //
+ Status = NtSetEvent( LLSAddCacheEvent, NULL );
+ ASSERT(NT_SUCCESS(Status));
+
+ }
+
+} // UserListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+UserListStringsPack (
+ ULONG UserLevel,
+
+ ULONG UserTableSize,
+ LPVOID Users,
+
+ ULONG *pUserStringSize,
+ LPTSTR *pUserStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ LPTSTR UserStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("UserListStringsPack\n"));
+#endif
+
+ ASSERT(pUserStrings != NULL);
+ ASSERT(pUserStringSize != NULL);
+
+ *pUserStrings = NULL;
+ *pUserStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_0) Users)[i].Name );
+ }
+ else
+ {
+ StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_1) Users)[i].Name );
+ }
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ UserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (UserStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = UserStrings;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ lstrcpy( pStr, ((PREPL_USER_RECORD_0) Users)[i].Name );
+ }
+ else
+ {
+ lstrcpy( pStr, ((PREPL_USER_RECORD_1) Users)[i].Name );
+ }
+
+ pStr += 1 + lstrlen( pStr );
+ }
+ }
+
+ *pUserStrings = UserStrings;
+ *pUserStringSize = StringSize;
+
+ return Status;
+} // UserListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListStringsUnpack (
+ ULONG UserLevel,
+
+ ULONG UserTableSize,
+ LPVOID Users,
+
+ ULONG UserStringSize,
+ LPTSTR UserStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("UserListStringsUnpack\n"));
+#endif
+
+ pStr = UserStrings;
+ for (i = 0; i < UserTableSize; i++) {
+ if ( 0 == UserLevel )
+ {
+ ((PREPL_USER_RECORD_0) Users)[i].Name = pStr;
+ }
+ else
+ {
+ ((PREPL_USER_RECORD_1) Users)[i].Name = pStr;
+ }
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // UserListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Service List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServiceListPack (
+ ULONG *pServiceTableSize,
+ PREPL_SERVICE_RECORD *pServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVICE_RECORD Services = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PMASTER_SERVICE_RECORD pService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListPack\n"));
+#endif
+
+ ASSERT(pServices != NULL);
+ ASSERT(pServiceTableSize != NULL);
+ *pServices = NULL;
+ *pServiceTableSize = 0;
+
+ TotalRecords = MasterServiceListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Services = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVICE_RECORD));
+ if (Services == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ for (i = 0; i < MasterServiceListSize; i++) {
+ pService = MasterServiceTable[i];
+
+ Services[i].Name = pService->Name;
+ Services[i].FamilyName = pService->Family->Name;
+ Services[i].Version = pService->Version;
+ Services[i].Index = pService->Index;
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServiceListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServices = Services;
+ *pServiceTableSize = TotalRecords;
+ return Status;
+} // ServiceListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PMASTER_SERVICE_RECORD pService;
+ PREPL_SERVICE_RECORD pSvc;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServiceListUnpack: [%lu]\n"), ServiceTableSize);
+#endif
+ //
+ // Walk services table, adding any new services to our local table.
+ // Fix up the index pointers to match our local services.
+ //
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceTableSize; i++) {
+ pSvc = &Services[i];
+ pService = MasterServiceListAdd(pSvc->FamilyName, pSvc->Name, pSvc->Version );
+
+ if (pService != NULL) {
+ pSvc->Index = pService->Index;
+
+ //
+ // In case this got added from the local service list table and we
+ // didn't have a version # yet.
+ //
+ if ( (pService->Version == 0) && (pSvc->Version != 0) ) {
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+
+ //
+ // Fixup next pointer chain
+ //
+ ServiceRoot = pService->Family;
+ j = 0;
+ while ((j < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[j]]->Version < pSvc->Version))
+ j++;
+
+ pService->next = 0;
+ pService->Version = pSvc->Version;
+ if (j > 0) {
+ if (MasterServiceTable[ServiceRoot->Services[j - 1]]->next == pService->Index + 1)
+ pService->next = 0;
+ else
+ pService->next = MasterServiceTable[ServiceRoot->Services[j - 1]]->next;
+
+ if (MasterServiceTable[ServiceRoot->Services[j - 1]] != pService)
+ MasterServiceTable[ServiceRoot->Services[j - 1]]->next = pService->Index + 1;
+
+ }
+
+ // Resort it in order of the versions
+ qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+ }
+
+ } else {
+ ASSERT(FALSE);
+ pSvc->Index = 0;
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+} // ServiceListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServiceListStringsPack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG *pServiceStringSize,
+ LPTSTR *pServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PREPL_SERVICE_RECORD pService;
+ LPTSTR ServiceStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServiceListStringsPack\n"));
+#endif
+
+ ASSERT(pServiceStrings != NULL);
+ ASSERT(pServiceStringSize != NULL);
+
+ *pServiceStrings = NULL;
+ *pServiceStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ StringSize = StringSize + lstrlen(pService->Name) + 1;
+ StringSize = StringSize + lstrlen(pService->FamilyName) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (ServiceStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = ServiceStrings;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ lstrcpy(pStr, pService->Name);
+ pStr = &pStr[lstrlen(pService->Name) + 1];
+
+ lstrcpy(pStr, pService->FamilyName);
+ pStr = &pStr[lstrlen(pService->FamilyName) + 1];
+ }
+ }
+
+ *pServiceStrings = ServiceStrings;
+ *pServiceStringSize = StringSize;
+
+ return Status;
+} // ServiceListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListStringsUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServiceStringSize,
+ LPTSTR ServiceStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PREPL_SERVICE_RECORD pService;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServiceListStringsUnpack\n"));
+#endif
+
+ pStr = ServiceStrings;
+ for (i = 0; i < ServiceTableSize; i++) {
+ pService = &Services[i];
+
+ pService->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+
+ pService->FamilyName = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // ServiceListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+// Server List
+//
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerListPack (
+ ULONG *pServerTableSize,
+ PREPL_SERVER_RECORD *pServers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVER_RECORD Servers = NULL;
+ ULONG i;
+ ULONG TotalRecords = 0;
+ PSERVER_RECORD pServer;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListPack\n"));
+#endif
+
+ ASSERT(pServers != NULL);
+ ASSERT(pServerTableSize != NULL);
+
+ *pServers = NULL;
+ *pServerTableSize = 0;
+
+ TotalRecords = ServerListSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ Servers = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_RECORD));
+ if (Servers == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ for (i = 0; i < ServerListSize; i++) {
+ pServer = ServerTable[i];
+
+ Servers[i].Name = pServer->Name;
+ Servers[i].MasterServer = pServer->MasterServer;
+ Servers[i].Index = pServer->Index;
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServerListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServers = Servers;;
+ *pServerTableSize = TotalRecords;
+ return Status;
+} // ServerListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PSERVER_RECORD pServer;
+ PREPL_SERVER_RECORD pSrv;
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServerListUnpack: [%lu]\n"), ServerTableSize);
+#endif
+
+ //
+ // Walk server table, adding any new servers to our local table.
+ // Fix up the index pointers to match our local table and re-fix
+ // Service table pointers.
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ComputerName, ConfigInfo.ComputerName);
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+
+ for (i = 0; i < ServerTableSize; i++) {
+ pSrv = &Servers[i];
+
+ if (pSrv->MasterServer != 0)
+ pServer = ServerListAdd(pSrv->Name, Servers[pSrv->MasterServer - 1].Name);
+ else
+ pServer = ServerListAdd(pSrv->Name, ComputerName);
+
+ if (pServer != NULL)
+ pSrv->Index = pServer->Index;
+ else {
+ ASSERT(FALSE);
+ pSrv->Index = 0;
+ }
+ }
+
+ RtlReleaseResource(&ServerListLock);
+
+} // ServerListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerServiceListPack (
+ ULONG *pServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD *pServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+ ULONG i, j, k;
+ ULONG TotalRecords = 0;
+ PSERVER_RECORD pServer;
+ PSERVER_SERVICE_RECORD pServerService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListPack\n"));
+#endif
+
+ ASSERT(pServerServices != NULL);
+ ASSERT(pServerServiceTableSize != NULL);
+
+ *pServerServices = NULL;
+ *pServerServiceTableSize = 0;
+
+ //
+ // Walk the ServerList and find all ServiceRecords
+ for (i = 0; i < ServerListSize; i++)
+ TotalRecords += ServerTable[i]->ServiceTableSize;
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (TotalRecords > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServerServices = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_SERVICE_RECORD));
+ if (ServerServices == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer - walk the user tree
+ //
+ k = 0;
+ for (i = 0; i < ServerListSize; i++) {
+ pServer = ServerTable[i];
+
+ for (j = 0; j < pServer->ServiceTableSize; j++) {
+ ServerServices[k].Server = pServer->Index;
+ ServerServices[k].Service = pServer->Services[j]->Service;
+ ServerServices[k].MaxSessionCount = pServer->Services[j]->MaxSessionCount;
+ ServerServices[k].MaxSetSessionCount = pServer->Services[j]->MaxSetSessionCount;
+ ServerServices[k].HighMark = pServer->Services[j]->HighMark;
+ k++;
+ }
+ }
+ }
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("ServerServiceListPack: [%lu]\n"), TotalRecords);
+#endif
+ *pServerServices = ServerServices;
+ *pServerServiceTableSize = TotalRecords;
+ return Status;
+} // ServerServiceListPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerServiceListUnpack (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PSERVER_RECORD pServer;
+ PREPL_SERVER_SERVICE_RECORD pSrv;
+ PSERVER_SERVICE_RECORD pService;
+ PMASTER_SERVICE_RECORD pMasterService;
+
+#if DBG
+ if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE))
+ dprintf(TEXT("ServerServiceListUnpack: [%lu]\n"), ServerServiceTableSize);
+#endif
+ //
+ // Walk server table, adding any new servers to our local table.
+ // Fix up the index pointers to match our local table and re-fix
+ // Service table pointers.
+ //
+
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < ServerServiceTableSize; i++) {
+ pSrv = &ServerServices[i];
+ pServer = ServerListFind(Servers[pSrv->Server - 1].Name);
+ ASSERT(pServer != NULL);
+
+ if (pServer != NULL) {
+ BOOL bReplaceValues;
+
+ pService = ServerServiceListFind(Services[pSrv->Service].Name, pServer->ServiceTableSize, pServer->Services);
+ bReplaceValues = ( NULL != pService );
+
+ pService = ServerServiceListAdd(Services[pSrv->Service].Name,
+ Services[pSrv->Service].Index,
+ &pServer->ServiceTableSize,
+ &pServer->Services);
+
+ ASSERT(pService != NULL);
+
+ //
+ // Remove any old info
+ //
+ pMasterService = MasterServiceTable[Services[pSrv->Service].Index];
+ if ( bReplaceValues )
+ {
+ pMasterService->MaxSessionCount -= pService->MaxSessionCount;
+ pMasterService->HighMark -= pService->HighMark;
+ }
+
+ //
+ // Now update new info
+ //
+ pService->MaxSessionCount = pSrv->MaxSessionCount;
+ pService->HighMark = pSrv->HighMark;
+ pMasterService->MaxSessionCount += pService->MaxSessionCount;
+ pMasterService->HighMark += pService->HighMark;
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&ServerListLock);
+
+} // ServerServiceListUnpack
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ServerListStringsPack (
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG *pServerStringSize,
+ LPTSTR *pServerStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG i;
+ ULONG StringSize;
+ PREPL_SERVER_RECORD pServer;
+ LPTSTR ServerStrings = NULL;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServerListStringsPack\n"));
+#endif
+
+ ASSERT(pServerStrings != NULL);
+ ASSERT(pServerStringSize != NULL);
+
+ *pServerStrings = NULL;
+ *pServerStringSize = 0;
+
+ //
+ // First walk the list adding up string sizes - to calculate our buff size
+ //
+ StringSize = 0;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ StringSize = StringSize + lstrlen(pServer->Name) + 1;
+ }
+
+ //
+ // Make sure there is anything to replicate
+ //
+ if (StringSize > 0) {
+ //
+ // Create our buffer to hold all of the garbage
+ //
+ ServerStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR));
+ if (ServerStrings == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Fill in the buffer
+ //
+ pStr = ServerStrings;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ lstrcpy(pStr, pServer->Name);
+ pStr = &pStr[lstrlen(pServer->Name) + 1];
+ }
+ }
+
+ *pServerStrings = ServerStrings;
+ *pServerStringSize = StringSize;
+
+ return Status;
+} // ServerListStringsPack
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListStringsUnpack (
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerStringSize,
+ LPTSTR ServerStrings
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ PREPL_SERVER_RECORD pServer;
+ TCHAR *pStr;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("ServerListStringsUnpack\n"));
+#endif
+
+ //
+ // First do license service strings
+ //
+ pStr = ServerStrings;
+ for (i = 0; i < ServerTableSize; i++) {
+ pServer = &Servers[i];
+
+ pServer->Name = pStr;
+
+ //
+ // Move to end of current string
+ //
+ while (*pStr != TEXT('\0'))
+ pStr++;
+
+ // now go past ending NULL
+ pStr++;
+ }
+
+} // ServerListStringsUnpack
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+PackAll (
+ DWORD LastReplicated,
+
+ ULONG *pServiceTableSize,
+ PREPL_SERVICE_RECORD *pServices,
+
+ ULONG *pServerTableSize,
+ PREPL_SERVER_RECORD *pServers,
+
+ ULONG *pServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD *pServerServices,
+
+ ULONG UserLevel,
+ ULONG *pUserTableSize,
+ LPVOID *pUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: PackAll\n"));
+#endif
+
+ //
+ // We need to grab all the locks here so that a service isn't snuck in
+ // behind our backs - since these tables interact with each other.
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ Status = ServiceListPack(pServiceTableSize, pServices);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = ServerListPack(pServerTableSize, pServers);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = ServerServiceListPack(pServerServiceTableSize, pServerServices);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ Status = UserListPack(LastReplicated, UserLevel, pUserTableSize, pUsers);
+ if (Status != STATUS_SUCCESS)
+ goto PackAllExit;
+
+ //
+ // Now update our last used time
+ //
+ LastUsedTime = DateSystemGet() + 1;
+
+PackAllExit:
+ RtlReleaseResource(&ServerListLock);
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+
+ return Status;
+} // PackAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UnpackAll (
+ ULONG ServiceTableSize,
+ PREPL_SERVICE_RECORD Services,
+
+ ULONG ServerTableSize,
+ PREPL_SERVER_RECORD Servers,
+
+ ULONG ServerServiceTableSize,
+ PREPL_SERVER_SERVICE_RECORD ServerServices,
+
+ ULONG UserLevel,
+ ULONG UserTableSize,
+ LPVOID Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UnpackAll\n"));
+#endif
+
+ ServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ ServerListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ ServerServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices);
+ UserListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices, UserLevel, UserTableSize, Users);
+} // UnpackAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LLSDataLoad()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret;
+ DWORD Version, DataSize;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG ServiceTableSize = 0;
+ PREPL_SERVICE_RECORD Services = NULL;
+
+ ULONG ServiceStringSize;
+ LPTSTR ServiceStrings = NULL;
+
+ ULONG ServerServiceTableSize = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+
+ ULONG ServerTableSize = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+
+ ULONG ServerStringSize;
+ LPTSTR ServerStrings = NULL;
+
+ ULONG UserTableSize = 0;
+ LPVOID Users = NULL;
+
+ ULONG UserStringSize;
+ LPTSTR UserStrings = NULL;
+
+ LLS_DATA_FILE_HEADER FileHeader;
+ DWORD BytesRead;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LLSDataLoad\n"));
+#endif
+
+ //
+ // If nothing to load then get-out
+ //
+ if (!FileExists(UserFileName))
+ goto LLSDataLoadExit;
+
+ //
+ // Check the init header
+ //
+ Version = DataSize = 0;
+ hFile = LlsFileCheck(UserFileName, &Version, &DataSize );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto LLSDataLoadExit;
+ }
+
+ if ( ( ( Version != USER_FILE_VERSION_0 )
+ || ( DataSize != sizeof(LLS_DATA_FILE_HEADER_0) ) )
+ && ( ( Version != USER_FILE_VERSION )
+ || ( DataSize != sizeof(LLS_DATA_FILE_HEADER) ) ) )
+ {
+ Status = STATUS_FILE_INVALID;
+ goto LLSDataLoadExit;
+ }
+
+ //
+ // The init header checks out, so load the license header and data blocks
+ //
+ if ( USER_FILE_VERSION_0 == Version )
+ {
+ // 3.51 data file
+ LLS_DATA_FILE_HEADER_0 FileHeader0;
+
+ ret = ReadFile(hFile, &FileHeader0, sizeof(LLS_DATA_FILE_HEADER_0), &BytesRead, NULL);
+
+ if ( ret )
+ {
+ FileHeader.ServiceLevel = 0;
+ FileHeader.ServiceTableSize = FileHeader0.ServiceTableSize;
+ FileHeader.ServiceStringSize = FileHeader0.ServiceStringSize;
+ FileHeader.ServerLevel = 0;
+ FileHeader.ServerTableSize = FileHeader0.ServerTableSize;
+ FileHeader.ServerStringSize = FileHeader0.ServerStringSize;
+ FileHeader.ServerServiceLevel = 0;
+ FileHeader.ServerServiceTableSize = FileHeader0.ServerServiceTableSize;
+ FileHeader.UserLevel = 0;
+ FileHeader.UserTableSize = FileHeader0.UserTableSize;
+ FileHeader.UserStringSize = FileHeader0.UserStringSize;
+ }
+ }
+ else
+ {
+ ret = ReadFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesRead, NULL);
+ }
+
+ if ( ret )
+ {
+ // header read okay; ensure data type levels are okay
+ if ( ( 0 != FileHeader.ServiceLevel )
+ || ( 0 != FileHeader.ServerLevel )
+ || ( 0 != FileHeader.ServerServiceLevel )
+ || ( ( 0 != FileHeader.UserLevel )
+ && ( 1 != FileHeader.UserLevel ) ) )
+ {
+ Status = STATUS_FILE_INVALID;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ ServiceTableSize = 0;
+ ServiceStringSize = 0;
+ ServerServiceTableSize = 0;
+ ServerTableSize = 0;
+ ServerStringSize = 0;
+ UserTableSize = 0;
+ UserStringSize = 0;
+
+ if (ret) {
+ //
+ // Run through and allocate space to read data blocks into
+ //
+ if (FileHeader.ServiceTableSize != 0) {
+ ServiceTableSize = FileHeader.ServiceTableSize / sizeof(REPL_SERVICE_RECORD);
+ Services = MIDL_user_allocate(FileHeader.ServiceTableSize);
+
+ if ( Services == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServiceStringSize != 0) {
+ ServiceStringSize = FileHeader.ServiceStringSize / sizeof(TCHAR);
+ ServiceStrings = MIDL_user_allocate(FileHeader.ServiceStringSize);
+
+ if ( ServiceStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerTableSize != 0) {
+ ServerTableSize = FileHeader.ServerTableSize / sizeof(REPL_SERVER_RECORD);
+ Servers = MIDL_user_allocate(FileHeader.ServerTableSize);
+
+ if ( Servers == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerStringSize != 0) {
+ ServerStringSize = FileHeader.ServerStringSize / sizeof(TCHAR);
+ ServerStrings = MIDL_user_allocate(FileHeader.ServerStringSize);
+
+ if ( ServerStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.ServerServiceTableSize != 0) {
+ ServerServiceTableSize = FileHeader.ServerServiceTableSize / sizeof(REPL_SERVER_SERVICE_RECORD);
+ ServerServices = MIDL_user_allocate(FileHeader.ServerServiceTableSize);
+
+ if ( ServerServices == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.UserTableSize != 0) {
+ UserTableSize = FileHeader.UserTableSize / ( FileHeader.UserLevel ? sizeof(REPL_USER_RECORD_1)
+ : sizeof(REPL_USER_RECORD_0) );
+ Users = MIDL_user_allocate(FileHeader.UserTableSize);
+
+ if ( Users == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ if (FileHeader.UserStringSize != 0) {
+ UserStringSize = FileHeader.UserStringSize / sizeof(TCHAR);
+ UserStrings = MIDL_user_allocate(FileHeader.UserStringSize);
+
+ if ( UserStrings == NULL ) {
+ Status = STATUS_NO_MEMORY;
+ goto LLSDataLoadExit;
+ }
+ }
+
+ }
+
+ if (ret && (FileHeader.ServiceTableSize != 0) )
+ ret = ReadFile(hFile, Services, FileHeader.ServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServiceStringSize != 0) )
+ ret = ReadFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerTableSize != 0) )
+ ret = ReadFile(hFile, Servers, FileHeader.ServerTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerStringSize != 0) )
+ ret = ReadFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.ServerServiceTableSize != 0) )
+ ret = ReadFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.UserTableSize != 0) )
+ ret = ReadFile(hFile, Users, FileHeader.UserTableSize, &BytesRead, NULL);
+
+ if (ret && (FileHeader.UserStringSize != 0) )
+ ret = ReadFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesRead, NULL);
+
+ if (!ret) {
+ Status = GetLastError();
+ goto LLSDataLoadExit;
+ }
+
+ //
+ // Decrypt the data
+ //
+ Status = DeBlock(Services, FileHeader.ServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServiceStrings, FileHeader.ServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Servers, FileHeader.ServerTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServerStrings, FileHeader.ServerStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(ServerServices, FileHeader.ServerServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(Users, FileHeader.UserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = DeBlock(UserStrings, FileHeader.UserStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataLoadExit;
+
+
+ //
+ // Unpack the string data
+ //
+ ServiceListStringsUnpack( ServiceTableSize, Services, ServiceStringSize, ServiceStrings );
+ ServerListStringsUnpack( ServerTableSize, Servers, ServerStringSize, ServerStrings );
+ UserListStringsUnpack( FileHeader.UserLevel, UserTableSize, Users, UserStringSize, UserStrings );
+
+ //
+ // Unpack the data
+ //
+ UnpackAll ( ServiceTableSize, Services, ServerTableSize, Servers,
+ ServerServiceTableSize, ServerServices,
+ FileHeader.UserLevel, UserTableSize, Users );
+
+LLSDataLoadExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (ServiceStrings != NULL)
+ MIDL_user_free(ServiceStrings);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if (ServerStrings != NULL)
+ MIDL_user_free(ServerStrings);
+
+ if (ServerServices != NULL)
+ MIDL_user_free(ServerServices);
+
+ if (Users != NULL)
+ MIDL_user_free(Users);
+
+ if (UserStrings != NULL)
+ MIDL_user_free(UserStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_LOAD_USER, 0, NULL, Status);
+
+} // LLSDataLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSDataSave()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL ret = TRUE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ HANDLE hFile = NULL;
+
+ ULONG ServiceTableSize = 0;
+ PREPL_SERVICE_RECORD Services = NULL;
+
+ ULONG ServiceStringSize;
+ LPTSTR ServiceStrings = NULL;
+
+ ULONG ServerServiceTableSize = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+
+ ULONG ServerTableSize = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+
+ ULONG ServerStringSize;
+ LPTSTR ServerStrings = NULL;
+
+ ULONG UserTableSize = 0;
+ PREPL_USER_RECORD_1 Users = NULL;
+
+ ULONG UserStringSize;
+ LPTSTR UserStrings = NULL;
+
+ LLS_DATA_FILE_HEADER FileHeader;
+ DWORD BytesWritten;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LLSDataSave\n"));
+#endif
+
+ //
+ // Pack the data
+ //
+ Status = PackAll ( 0,
+ &ServiceTableSize, &Services,
+ &ServerTableSize, &Servers,
+ &ServerServiceTableSize, &ServerServices,
+ 1, &UserTableSize, &Users );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Now pack the String data
+ //
+ Status = ServiceListStringsPack( ServiceTableSize, Services, &ServiceStringSize, &ServiceStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ Status = ServerListStringsPack( ServerTableSize, Servers, &ServerStringSize, &ServerStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ Status = UserListStringsPack( 1, UserTableSize, Users, &UserStringSize, &UserStrings );
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Fill out the file header - sizes are byte sizes
+ //
+ FileHeader.ServiceTableSize = ServiceTableSize * sizeof(REPL_SERVICE_RECORD);
+ FileHeader.ServiceStringSize = ServiceStringSize * sizeof(TCHAR);
+ FileHeader.ServerTableSize = ServerTableSize * sizeof(REPL_SERVER_RECORD);
+ FileHeader.ServerStringSize = ServerStringSize * sizeof(TCHAR);
+ FileHeader.ServerServiceTableSize = ServerServiceTableSize * sizeof(REPL_SERVER_SERVICE_RECORD);
+ FileHeader.UserTableSize = UserTableSize * sizeof(REPL_USER_RECORD_1);
+ FileHeader.UserStringSize = UserStringSize * sizeof(TCHAR);
+
+ FileHeader.ServiceLevel = 0;
+ FileHeader.ServerLevel = 0;
+ FileHeader.ServerServiceLevel = 0;
+ FileHeader.UserLevel = 1;
+
+ //
+ // Encrypt the data before saving it out.
+ //
+ Status = EBlock(Services, FileHeader.ServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServiceStrings, FileHeader.ServiceStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Servers, FileHeader.ServerTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServerStrings, FileHeader.ServerStringSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(ServerServices, FileHeader.ServerServiceTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(Users, FileHeader.UserTableSize);
+
+ if (Status == STATUS_SUCCESS)
+ Status = EBlock(UserStrings, FileHeader.UserStringSize);
+
+ if (Status != STATUS_SUCCESS)
+ goto LLSDataSaveExit;
+
+ //
+ // Save out the header record
+ //
+ hFile = LlsFileInit(UserFileName, USER_FILE_VERSION, sizeof(LLS_DATA_FILE_HEADER) );
+ if (hFile == NULL) {
+ Status = GetLastError();
+ goto LLSDataSaveExit;
+ }
+
+ //
+ // Now write out all the data blocks
+ //
+ ret = WriteFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesWritten, NULL);
+
+ if (ret && (Services != NULL) && (FileHeader.ServiceTableSize != 0) )
+ ret = WriteFile(hFile, Services, FileHeader.ServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (ServiceStrings != NULL) && (FileHeader.ServiceStringSize != 0) )
+ ret = WriteFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesWritten, NULL);
+
+ if (ret && (Servers != NULL) && (FileHeader.ServerTableSize != 0) )
+ ret = WriteFile(hFile, Servers, FileHeader.ServerTableSize, &BytesWritten, NULL);
+
+ if (ret && (ServerStrings != NULL) && (FileHeader.ServerStringSize != 0) )
+ ret = WriteFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesWritten, NULL);
+
+ if (ret && (ServerServices != NULL) && (FileHeader.ServerServiceTableSize != 0) )
+ ret = WriteFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesWritten, NULL);
+
+ if (ret && (Users != NULL) && (FileHeader.UserTableSize != 0) )
+ ret = WriteFile(hFile, Users, FileHeader.UserTableSize, &BytesWritten, NULL);
+
+ if (ret && (UserStrings != NULL) && (FileHeader.UserStringSize != 0) )
+ ret = WriteFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesWritten, NULL);
+
+ if (!ret)
+ Status = GetLastError();
+
+LLSDataSaveExit:
+
+ if (hFile != NULL)
+ CloseHandle(hFile);
+
+ //
+ // Run through our tables and clean them up
+ //
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (ServiceStrings != NULL)
+ MIDL_user_free(ServiceStrings);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if (ServerStrings != NULL)
+ MIDL_user_free(ServerStrings);
+
+ if (ServerServices != NULL)
+ MIDL_user_free(ServerServices);
+
+ if (Users != NULL)
+ MIDL_user_free(Users);
+
+ if (UserStrings != NULL)
+ MIDL_user_free(UserStrings);
+
+ //
+ // If there was an error log it.
+ //
+ if (Status != STATUS_SUCCESS)
+ LogEvent(LLS_EVENT_SAVE_USER, 0, NULL, Status);
+
+ return Status;
+} // LLSDataSave
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LoadAll ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: LoadAll\n"));
+#endif
+
+ PurchaseFile = NULL;
+ LicenseListLoad();
+
+ MappingListLoad();
+ LLSDataLoad();
+
+ CertDbLoad();
+
+} // LoadAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SaveAll ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
+ dprintf(TEXT("LLS TRACE: SaveAll\n"));
+#endif
+
+ LicenseListSave();
+ MappingListSave();
+ LLSDataSave();
+ CertDbSave();
+
+} // SaveAll
diff --git a/private/net/svcdlls/lls/server/pack.h b/private/net/svcdlls/lls/server/pack.h
new file mode 100644
index 000000000..7e6feadeb
--- /dev/null
+++ b/private/net/svcdlls/lls/server/pack.h
@@ -0,0 +1,195 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Pack.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added new fields to purchase record to support secure certificates.
+ o Unified per server purchase model with per seat purchase model for
+ secure certificates; per server model still done in the traditional
+ manner for non-secure certificates (for backwards compatibility).
+ o Added SaveAll() function analogous to LoadAll().
+ o Added support for extended user data packing/unpacking. This was
+ done to save the SUITE_USE flag across restarts of the service.
+ o Removed user table parameters from unpack routines that didn't use
+ them.
+
+--*/
+
+#ifndef _LLS_PACK_H
+#define _LLS_PACK_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load Mapping
+//
+#define MAPPING_FILE_VERSION 0x0100
+
+typedef struct _PACK_MAPPING_RECORD {
+ LPTSTR Name;
+ LPTSTR Comment;
+ ULONG Licenses;
+} PACK_MAPPING_RECORD, *PPACK_MAPPING_RECORD;
+
+typedef struct _PACK_MAPPING_USER_RECORD {
+ ULONG Mapping;
+ LPTSTR Name;
+} PACK_MAPPING_USER_RECORD, *PPACK_MAPPING_USER_RECORD;
+
+typedef struct _MAPPING_FILE_HEADER {
+ ULONG MappingUserTableSize;
+ ULONG MappingUserStringSize;
+ ULONG MappingTableSize;
+ ULONG MappingStringSize;
+} MAPPING_FILE_HEADER, *PMAPPING_FILE_HEADER;
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load License
+//
+
+///////////////// OLD (3.51) FORMAT ////////////////////
+#define LICENSE_FILE_VERSION_0 0x0100
+
+typedef struct _PACK_LICENSE_PURCHASE_RECORD_0 {
+ ULONG Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+} PACK_LICENSE_PURCHASE_RECORD_0, *PPACK_LICENSE_PURCHASE_RECORD_0;
+
+typedef struct _LICENSE_FILE_HEADER_0 {
+ ULONG LicenseServiceTableSize;
+ ULONG LicenseServiceStringSize;
+ ULONG LicenseTableSize;
+ ULONG LicenseStringSize;
+} LICENSE_FILE_HEADER_0, *PLICENSE_FILE_HEADER_0;
+
+///////////////// NEW FORMAT ////////////////////
+#define LICENSE_FILE_VERSION 0x0201
+
+typedef struct _PACK_LICENSE_SERVICE_RECORD {
+ LPTSTR ServiceName;
+ LONG NumberLicenses;
+} PACK_LICENSE_SERVICE_RECORD, *PPACK_LICENSE_SERVICE_RECORD;
+
+typedef struct _PACK_LICENSE_PURCHASE_RECORD {
+ ULONG Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+
+ // new for SUR: (see description in purchase.h)
+ ULONG PerServerService;
+ DWORD AllowedModes;
+ DWORD CertificateID;
+ LPTSTR Source;
+ DWORD ExpirationDate;
+ DWORD MaxQuantity;
+ LPTSTR Vendor;
+ DWORD Secrets[ LLS_NUM_SECRETS ];
+} PACK_LICENSE_PURCHASE_RECORD, *PPACK_LICENSE_PURCHASE_RECORD;
+
+typedef struct _LICENSE_FILE_HEADER {
+ ULONG LicenseServiceTableSize;
+ ULONG LicenseServiceStringSize;
+
+ ULONG LicenseTableSize;
+ ULONG LicenseStringSize;
+
+ // new for SUR:
+ ULONG PerServerLicenseServiceTableSize;
+ ULONG PerServerLicenseServiceStringSize;
+
+} LICENSE_FILE_HEADER, *PLICENSE_FILE_HEADER;
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Save / Load LLS Data
+//
+
+///////////////// OLD (3.51) FORMAT ////////////////////
+#define USER_FILE_VERSION_0 0x0100
+
+typedef struct _LLS_DATA_FILE_HEADER_0 {
+ ULONG ServiceTableSize;
+ ULONG ServiceStringSize;
+ ULONG ServerTableSize;
+ ULONG ServerStringSize;
+ ULONG ServerServiceTableSize;
+ ULONG UserTableSize;
+ ULONG UserStringSize;
+} LLS_DATA_FILE_HEADER_0, *PLLS_DATA_FILE_HEADER_0;
+
+///////////////// NEW FORMAT ////////////////////
+#define USER_FILE_VERSION 0x0200
+
+typedef struct _LLS_DATA_FILE_HEADER {
+ ULONG ServiceLevel;
+ ULONG ServiceTableSize;
+ ULONG ServiceStringSize;
+
+ ULONG ServerLevel;
+ ULONG ServerTableSize;
+ ULONG ServerStringSize;
+
+ ULONG ServerServiceLevel;
+ ULONG ServerServiceTableSize;
+
+ ULONG UserLevel;
+ ULONG UserTableSize;
+ ULONG UserStringSize;
+} LLS_DATA_FILE_HEADER, *PLLS_DATA_FILE_HEADER;
+
+
+
+VOID LicenseListLoad();
+NTSTATUS LicenseListSave();
+VOID MappingListLoad();
+NTSTATUS MappingListSave();
+VOID LLSDataLoad();
+NTSTATUS LLSDataSave();
+
+VOID LoadAll ( );
+VOID SaveAll ( );
+
+NTSTATUS ServiceListPack ( ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices );
+VOID ServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS ServerListPack ( ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers );
+VOID ServerListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS ServerServiceListPack ( ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices );
+VOID ServerServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices );
+NTSTATUS UserListPack ( DWORD LastReplicated, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers );
+VOID UserListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users );
+NTSTATUS PackAll ( DWORD LastReplicated, ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices, ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers, ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers );
+VOID UnpackAll ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/perseat.c b/private/net/svcdlls/lls/server/perseat.c
new file mode 100644
index 000000000..3bb7c4333
--- /dev/null
+++ b/private/net/svcdlls/lls/server/perseat.c
@@ -0,0 +1,3351 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ perseat.c
+
+Abstract:
+
+ Routines to handle per-seat licensing. Handles the in-memory cache
+ of useage via the Rtl Generic table functions (these are a generic
+ splay tree package).
+
+ There can be up to three tables kept. The first table is a username
+ table and is the main table. The second table is for SID's, which will
+ be converted into usernames when replicated.
+
+ The SID and username trees are handled in this module as they are used
+ by all modes of the server.
+
+Author:
+
+ Arthur Hanson (arth) 03-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Fixed possible infinite loop in UserListLicenseDelete().
+ o In FamilyLicenseUpdate(), now rescans for BackOffice upgrades
+ regardless of whether the family being updated was BackOffice.
+ This fixes a problem wherein a freed BackOffice license was
+ not being assigned to a user that needed it. (Bug #3299.)
+ o Added support for maintaining the SUITE_USE flag when adding
+ users to the AddCache.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "llsevent.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+//
+// At what # of product do we switch to BackOffice
+//
+#define BACKOFFICE_SWITCH 3
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Actual User and SID Lists, and their access locks
+//
+ULONG UserListNumEntries = 0;
+ULONG SidListNumEntries = 0;
+RTL_GENERIC_TABLE UserList;
+RTL_GENERIC_TABLE SidList;
+
+RTL_RESOURCE UserListLock;
+RTL_RESOURCE SidListLock;
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The enum processes for replication and UI can take awhile to go through
+// all the records, while doing this they need a shared lock on the file.
+// However, if we request an exclusive access during this time the pending
+// exclusive access will block other shared access's. Therefore we get
+// this lock first before attempting either an add or enum.
+//
+// An add will block enums, but neither of these function are as time
+// critical as updating normal user records.
+//
+RTL_RESOURCE UserListAddEnumLock;
+RTL_RESOURCE SidListAddEnumLock;
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The AddCache itself, a critical section to protect access to it and an
+// event to signal the server when there are items on it that need to be
+// processed.
+//
+PADD_CACHE AddCache = NULL;
+ULONG AddCacheSize = 0;
+RTL_CRITICAL_SECTION AddCacheLock;
+HANDLE LLSAddCacheEvent;
+
+DWORD LastUsedTime = 0;
+BOOL UsersDeleted = FALSE;
+
+
+RTL_CRITICAL_SECTION GenTableLock;
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The License List is a linear list of all the licenses the object is
+// using.
+//
+// The license list is kept as part of each user and mapping record, if
+// the user is mapped then the mapping should contain the license list.
+// The structure is a sorted array of pointers to License Records, and
+// access is controled by the ServiceTableLock.
+//
+// The license is identified by the Service Family Name (the license list
+// is sorted on this).
+//
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl
+LicenseListCompare(const void *arg1, const void *arg2) {
+ PUSER_LICENSE_RECORD pLic1, pLic2;
+
+ pLic1 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg1);
+ pLic2 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg2);
+
+ return lstrcmpi( pLic1->Family->Name, pLic2->Family->Name );
+
+} // LicenseListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_LICENSE_RECORD
+LicenseListFind(
+ LPTSTR Name,
+ PUSER_LICENSE_RECORD *pLicenseList,
+ ULONG NumTableEntries
+ )
+
+/*++
+
+Routine Description:
+
+ Find the license in a license list for the given Family of products.
+
+Arguments:
+
+ Name - Name of product family to find license for.
+
+ pLicenseList - Size of the license list to search.
+
+ NumTableEntries - Pointer to the license List to search.
+
+Return Value:
+
+ Pointer to the found License Record, or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) NumTableEntries - 1;
+ LONG cur;
+ int match;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListFind\n"));
+#endif
+
+ if ((Name == NULL) || (pLicenseList == NULL) || (NumTableEntries == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ match = lstrcmpi(Name, pLicenseList[cur]->Family->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return pLicenseList[cur];
+ }
+
+ return NULL;
+
+} // LicenseListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LicenseListDelete(
+ PMASTER_SERVICE_ROOT Family,
+ PUSER_LICENSE_RECORD **pLicenses,
+ PULONG pLicenseListSize
+ )
+
+/*++
+
+Routine Description:
+
+ Delete the given license from the license list.
+
+Arguments:
+
+ Family -
+
+ pLicenses -
+
+ pLicenseListSize -
+
+Return Value:
+
+ STATUS_SUCCESS if successful, else error code.
+
+--*/
+
+{
+ PUSER_LICENSE_RECORD *LicenseList;
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD LicenseRec;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListDelete\n"));
+#endif
+
+ if ( (pLicenses == NULL) || (pLicenseListSize == NULL) )
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ LicenseListSize = *pLicenseListSize;
+ LicenseList = *pLicenses;
+
+ //
+ // Get record based on name given
+ //
+ LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize);
+ if (LicenseRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ //
+ // Check if this is the last user
+ //
+ if (LicenseListSize == 1) {
+ LocalFree(LicenseList);
+ *pLicenseListSize = 0;
+ *pLicenses = NULL;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Not the last so find it in the list
+ //
+ i = 0;
+ while ( (i < LicenseListSize) && (LicenseList[i]->Family != Family) )
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < LicenseListSize) {
+ LicenseList[i-1] = LicenseList[i];
+ i++;
+ }
+
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize - 1), LHND);
+
+ //
+ // Make sure we could allocate table
+ //
+ ASSERT(LicenseList != NULL);
+ if (LicenseList == NULL)
+ LicenseListSize = 0;
+ else
+ LicenseListSize--;
+
+ LocalFree(LicenseRec);
+ *pLicenses = LicenseList;
+ *pLicenseListSize = LicenseListSize;
+
+ return STATUS_SUCCESS;
+
+} // LicenseListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_LICENSE_RECORD
+LicenseListAdd(
+ PMASTER_SERVICE_ROOT Family,
+ PUSER_LICENSE_RECORD **pLicenses,
+ PULONG pLicenseListSize
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an empty license record to the license list. Sets the license
+ family, but not any of the other info.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_LICENSE_RECORD *LicenseList;
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD LicenseRec;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseListAdd\n"));
+#endif
+
+ if ((Family == NULL) || (pLicenses == NULL) || (pLicenseListSize == NULL) )
+ return NULL;
+
+ LicenseList = *pLicenses;
+ LicenseListSize = *pLicenseListSize;
+
+ //
+ // We do a double check here to see if another thread just got done
+ // adding the Mapping, between when we checked last and actually got
+ // the write lock.
+ //
+ LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize );
+
+ if (LicenseRec != NULL) {
+ return LicenseRec;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (LicenseList == NULL)
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalAlloc(LPTR, sizeof(PUSER_LICENSE_RECORD));
+ else
+ LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate Mapping table
+ //
+ if (LicenseList == NULL) {
+ pLicenses = NULL;
+ pLicenseListSize = 0;
+ return NULL;
+ }
+
+ LicenseRec = (PUSER_LICENSE_RECORD) LocalAlloc(LPTR, sizeof(USER_LICENSE_RECORD));
+ if (LicenseRec == NULL)
+ return NULL;
+
+ // now copy it over...
+ LicenseList[LicenseListSize] = LicenseRec;
+ LicenseRec->Family = Family;
+ LicenseRec->Flags = LLS_FLAG_LICENSED;
+ LicenseRec->RefCount = 0;
+ LicenseRec->Service = NULL;
+ LicenseRec->LicensesNeeded = 0;
+
+ LicenseListSize++;
+
+ // Have added the entry - now need to sort it in order of the names
+ qsort((void *) LicenseList, (size_t) LicenseListSize, sizeof(PUSER_LICENSE_RECORD), LicenseListCompare);
+
+ *pLicenses = LicenseList;
+ *pLicenseListSize = LicenseListSize;
+ return LicenseRec;
+
+} // LicenseListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+// These routines are specific to the license list in the user and
+// mapping records.
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserLicenseListFree (
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Walks the license list deleting all entries and freeing up any claimed
+ licenses from the service table. This only cleans up the licenses
+ in a user record (not a mapping) so the # licenses is always 1.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserLicenseListFree\n"));
+#endif
+
+ //
+ // Walk license table and free all licenses
+ //
+ for (i = 0; i < pUser->LicenseListSize; i++) {
+ pUser->LicenseList[i]->Service->LicensesUsed -= 1;
+ pUser->LicenseList[i]->Service->LicensesClaimed -= (1 - pUser->LicenseList[i]->LicensesNeeded);
+ pUser->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ LocalFree(pUser->LicenseList[i]);
+ }
+
+ //
+ // Free related entries in user list
+ //
+ if (pUser->LicenseList != NULL)
+ LocalFree(pUser->LicenseList);
+
+ pUser->LicenseList = NULL;
+ pUser->LicenseListSize = 0;
+ pUser->LicensedProducts = 0;
+
+ //
+ // Get rid of pointers in services table
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++)
+ pUser->Services[i].License = NULL;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan) {
+ //
+ // Set to licensed so scan doesn't assign to ourself
+ //
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+
+ if (pUser->ServiceTableSize > 0)
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+} // UserLicenseListFree
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingLicenseListFree (
+ PMAPPING_RECORD pMap
+ )
+
+/*++
+
+Routine Description:
+
+ Walks the license list in a mapping freeing up any claimed licenses.
+ Like UserLicenseListFree, but for a mapping.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingLicenseListFree\n"));
+#endif
+
+ //
+ // Walk license table and free all licenses
+ //
+ for (i = 0; i < pMap->LicenseListSize; i++) {
+ pMap->LicenseList[i]->Service->LicensesUsed -= pMap->Licenses;
+ pMap->LicenseList[i]->Service->LicensesClaimed -= (pMap->Licenses - pMap->LicenseList[i]->LicensesNeeded);
+ pMap->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ LocalFree(pMap->LicenseList[i]);
+ }
+
+ //
+ // Free related entries in mapping list
+ //
+ if (pMap->LicenseList != NULL)
+ LocalFree(pMap->LicenseList);
+
+ pMap->LicenseList = NULL;
+ pMap->LicenseListSize = 0;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan)
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+
+} // MappingLicenseListFree
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The service table is a linear array of records pointed to by the
+// user record. Each entry contains a pointer into the service table
+// identifying the service, some statistical useage information and a
+// pointer into the license table identifying the license used by the
+// service.
+//
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl
+SvcListCompare(
+ const void *arg1,
+ const void *arg2
+ )
+{
+ PSVC_RECORD pSvc1, pSvc2;
+
+ pSvc1 = (PSVC_RECORD) arg1;
+ pSvc2 = (PSVC_RECORD) arg2;
+
+ return lstrcmpi( pSvc1->Service->Name, pSvc2->Service->Name );
+
+} // SvcListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PSVC_RECORD
+SvcListFind(
+ LPTSTR DisplayName,
+ PSVC_RECORD ServiceList,
+ ULONG NumTableEntries
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on Service List in user
+ record. This is a binary search, however since the string pointers are
+ from the service table and therefore the pointers are fixed, we only
+ need to compare the pointers, not the strings themselves to find a
+ match.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) NumTableEntries - 1;
+ LONG cur;
+ int match;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListFind\n"));
+#endif
+ if ((DisplayName == NULL) || (ServiceList == NULL) || (NumTableEntries == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ match = lstrcmpi(DisplayName, ServiceList[cur].Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return &ServiceList[cur];
+ }
+
+ return NULL;
+
+} // SvcListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SvcListDelete(
+ LPTSTR UserName,
+ LPTSTR ServiceName
+)
+
+/*++
+
+Routine Description:
+
+ Deletes a service record from the service table.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PUSER_LICENSE_RECORD License = NULL;
+ ULONG NumLicenses = 1;
+ ULONG i;
+ BOOL ReScan = FALSE;
+ PMASTER_SERVICE_ROOT Family = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListDelete\n"));
+#endif
+
+ pUserRec = UserListFind(UserName);
+ if (pUserRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( ServiceName, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ //
+ // If we couldn't find it then get out.
+ //
+ if (pService == NULL) {
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ Family = pService->Service->Family;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUserRec->Mapping != NULL)
+ NumLicenses = pUserRec->Mapping->Licenses;
+
+ License = pService->License;
+
+ if (License != NULL) {
+ License->RefCount--;
+
+ //
+ // If this is the last service that uses this license then we need
+ // to get rid of it.
+ //
+ if (License->RefCount == 0) {
+ License->Service->LicensesUsed -= NumLicenses;
+ NumLicenses -= License->LicensesNeeded;
+ License->Service->LicensesClaimed -= NumLicenses;
+
+ //
+ // Do we need to delete it from the mapping or user license table?
+ //
+ if (pUserRec->Mapping != NULL) {
+ if ((License->Service == BackOffice) && (pUserRec->Mapping->Flags & LLS_FLAG_SUITE_AUTO))
+ pUserRec->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ LicenseListDelete(License->Service->Family, &pUserRec->Mapping->LicenseList, &pUserRec->Mapping->LicenseListSize );
+
+ } else {
+ if ((License->Service == BackOffice) && (pUserRec->Flags & LLS_FLAG_SUITE_AUTO))
+ pUserRec->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ LicenseListDelete(License->Service->Family, &pUserRec->LicenseList, &pUserRec->LicenseListSize );
+ }
+
+ //
+ // Freed a license so need to scan and adjust counts
+ //
+ ReScan = TRUE;
+ }
+ }
+
+ if (pService->Flags & LLS_FLAG_LICENSED)
+ pUserRec->LicensedProducts--;
+ else {
+ //
+ // This was an unlicensed product - see if this makes the user
+ // licensed
+ //
+ if (pUserRec->LicensedProducts == (pUserRec->ServiceTableSize - 1))
+ pUserRec->Flags |= LLS_FLAG_LICENSED;
+ }
+
+ //
+ // First check if this is the only entry in the table
+ //
+ if (pUserRec->ServiceTableSize == 1) {
+ LocalFree(pUserRec->Services);
+ pUserRec->Services = NULL;
+ goto SvcListDeleteExit;
+ }
+
+ //
+ // Find this record linearly in the table.
+ //
+ i = 0;
+ while ((i < pUserRec->ServiceTableSize) && (lstrcmpi(pUserRec->Services[i].Service->Name, ServiceName)))
+ i++;
+
+ //
+ // Now move everything below it up.
+ //
+ i++;
+ while (i < pUserRec->ServiceTableSize) {
+ memcpy(&pUserRec->Services[i-1], &pUserRec->Services[i], sizeof(SVC_RECORD));
+ i++;
+ }
+
+ SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize - 1), LHND);
+
+ ASSERT(SvcTable != NULL);
+ if (SvcTable == NULL) {
+ pUserRec->Services = NULL;
+ pUserRec->ServiceTableSize = 0;
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+ return STATUS_SUCCESS;
+ }
+
+ pUserRec->Services = SvcTable;
+
+SvcListDeleteExit:
+ pUserRec->ServiceTableSize--;
+
+ if (pUserRec->ServiceTableSize == 0)
+ pUserRec->Services = NULL;
+
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ if (ReScan)
+ FamilyLicenseUpdate ( Family );
+
+ return STATUS_SUCCESS;
+
+} // SvcListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcListLicenseFree(
+ PUSER_RECORD pUser
+)
+
+/*++
+
+Routine Description:
+
+
+ Walk the services table and free up any licenses they are using. If the
+ licenses are then no longer needed (refCount == 0) then the license is
+ deleted.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListLicenseFree\n"));
+#endif
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+
+ if (pUser->Mapping != NULL)
+ NumLicenses = pUser->Mapping->Licenses;
+ else
+ NumLicenses = 1;
+
+ License = pUser->Services[i].License;
+
+ if (License != NULL) {
+ License->RefCount--;
+
+ //
+ // If this is the last service that uses this license then we need
+ // to get rid of it.
+ //
+ if (License->RefCount == 0) {
+ if ( (pUser->Mapping != NULL) && (License->Service == BackOffice) && (pUser->Mapping->Flags & LLS_FLAG_SUITE_AUTO) )
+ pUser->Mapping->Flags &= ~LLS_FLAG_SUITE_USE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ NumLicenses -= License->LicensesNeeded;
+
+ if (License->Service->LicensesClaimed > 0) {
+ //
+ // Freed a license so need to scan and adjust counts
+ //
+ License->Service->Family->Flags |= LLS_FLAG_UPDATE;
+ ReScan = TRUE;
+ }
+
+ License->Service->LicensesClaimed -= NumLicenses;
+
+ if (pUser->Mapping != NULL)
+ LicenseListDelete(License->Service->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize );
+ else
+ LicenseListDelete(License->Service->Family, &pUser->LicenseList, &pUser->LicenseListSize );
+
+ }
+ }
+
+ pUser->Services[i].License = NULL;
+ }
+
+ pUser->LicensedProducts = 0;
+
+ //
+ // Check if we freed up licenses and need to re-scan the user-table
+ //
+ if (ReScan) {
+ //
+ // Flag license so rescan won't worry about this user
+ //
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < RootServiceListSize; i++) {
+ if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) {
+ RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ FamilyLicenseUpdate( RootServiceList[i] );
+ }
+ }
+ }
+
+} // SvcListLicenseFree
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcListLicenseUpdate(
+ PUSER_RECORD pUser
+)
+
+/*++
+
+Routine Description:
+
+ Walk the services table and assign the appropriate license to each
+ service. This is the opposite of the SvcListLicenseFree Routine.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ ULONG Claimed = 0;
+ PUSER_LICENSE_RECORD License = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcListLicenseUpdate\n"));
+#endif
+
+ //
+ // Check if user is set to use BackOffice
+ if ( pUser->Flags & LLS_FLAG_SUITE_USE ) {
+ //
+ // Go grab a backoffice license to fulfill the suite useage
+ //
+ License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize);
+
+ ASSERT(License != NULL);
+ if (License != NULL) {
+ License->Service = BackOffice;
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ // Can only claim a # of licenses that we have
+ if ( BackOffice->LicensesClaimed < BackOffice->Licenses) {
+ Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed;
+
+ if (Claimed > 1)
+ Claimed = 1;
+
+ }
+
+ //
+ // Adjust license counts
+ //
+ BackOffice->LicensesUsed += 1;
+ BackOffice->LicensesClaimed += Claimed;
+ License->LicensesNeeded = 1 - Claimed;
+
+ //
+ // Figure out if we are licensed or not.
+ //
+ if (License->LicensesNeeded > 0) {
+ //
+ // Not licensed
+ //
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ pUser->Services[i].Flags &= ~LLS_FLAG_LICENSED;
+ pUser->Services[i].License = License;
+ License->RefCount++;
+ }
+ } else {
+ //
+ // Licensed
+ //
+ License->Flags |= LLS_FLAG_LICENSED;
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ pUser->LicensedProducts++;
+ pUser->Services[i].Flags |= LLS_FLAG_LICENSED;
+ pUser->Services[i].License = License;
+ License->RefCount++;
+ }
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ }
+
+ } else {
+ BOOL Licensed = TRUE;
+
+ //
+ // Loop through all the services and make sure they are all
+ // licensed.
+ //
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ SvcLicenseUpdate(pUser, &pUser->Services[i]);
+
+ if ( pUser->Services[i].Flags & LLS_FLAG_LICENSED )
+ pUser->LicensedProducts++;
+ else
+ Licensed = FALSE;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+
+} // SvcListLicenseUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SvcLicenseUpdate(
+ PUSER_RECORD pUser,
+ PSVC_RECORD Svc
+)
+
+/*++
+
+Routine Description:
+
+ For a given service record for a user check and update license compliance.
+ Is a single service record version of SvcListLicenseUpdate.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL UseMapping = FALSE;
+ PMASTER_SERVICE_RECORD LicenseService = NULL;
+ PMASTER_SERVICE_RECORD Service;
+ BOOL ReScan = FALSE;
+ DWORD Flags = 0;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: SvcLicenseUpdate\n"));
+#endif
+
+ if ((pUser == NULL) || (Svc == NULL))
+ return;
+
+ Flags = pUser->Flags;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUser->Mapping != NULL) {
+ NumLicenses = pUser->Mapping->Licenses;
+ UseMapping = TRUE;
+ Flags = pUser->Mapping->Flags;
+ }
+
+ //
+ // Try to find a license record in the license list of the user or mapping
+ // to use. If we are using BackOffice then look for BackOffice license
+ // instead of the service license.
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ Service = BackOffice;
+
+ if (UseMapping)
+ License = LicenseListFind(BackOfficeStr, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(BackOfficeStr, pUser->LicenseList, pUser->LicenseListSize);
+
+ } else {
+ //
+ // Not BackOffice - so look for normal service license
+ //
+ Service = Svc->Service;
+ ASSERT(Service != NULL);
+
+ //
+ // Try to find a license for this family of products
+ //
+ if (UseMapping)
+ License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+ }
+
+ //
+ // Check if we couldn't find a license. If we didn't find it then we need
+ // to create a new license for this.
+ //
+ if (License == NULL) {
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD *LicenseList;
+
+ //
+ // The license list to use depends if we are part of a mapping or not.
+ //
+ if (UseMapping) {
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ LicenseList = pUser->Mapping->LicenseList;
+ } else {
+ LicenseListSize = pUser->LicenseListSize;
+ LicenseList = pUser->LicenseList;
+ }
+
+ //
+ // Check if we need to add a license for BackOffice or just the service
+ // itself.
+ //
+ if (Flags & LLS_FLAG_SUITE_USE)
+ License = LicenseListAdd(BackOffice->Family, &LicenseList, &LicenseListSize);
+ else
+ License = LicenseListAdd(Service->Family, &LicenseList, &LicenseListSize);
+
+ //
+ // Now update the couters in the parent record
+ //
+ if (UseMapping) {
+ pUser->Mapping->LicenseListSize = LicenseListSize;
+ pUser->Mapping->LicenseList = LicenseList;
+ } else {
+ pUser->LicenseListSize = LicenseListSize;
+ pUser->LicenseList = LicenseList;
+ }
+
+ if (License != NULL)
+ License->LicensesNeeded = NumLicenses;
+ }
+
+ //
+ // We have either found an old license or added a new one, either way
+ // License points to it.
+ //
+ if (License != NULL) {
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+
+ //
+ // if we have a license for this family already and the product
+ // version >= current then we are okay, else need to get new license
+ //
+ if ( (License->Service != NULL) && (License->Service->Version >= Service->Version) ) {
+ LicenseService = License->Service;
+ } else {
+ //
+ // we have an old license for this family, but the version
+ // isn't adequate, so we need to try and get a new license.
+ // Walk the family tree looking for the licenses we
+ // need.
+ //
+ LicenseService = Service;
+ while ((LicenseService != NULL) && ( (LicenseService->LicensesUsed + NumLicenses) > LicenseService->Licenses) )
+ if (LicenseService->next > 0)
+ LicenseService = MasterServiceTable[LicenseService->next - 1];
+ else
+ LicenseService = NULL;
+
+ //
+ // if we couldn't find a license just use the old
+ // service.
+ if (LicenseService == NULL)
+ LicenseService = Service;
+ else {
+ //
+ // Need to clean up old stuff
+ //
+ if (License->Service != NULL) {
+ //
+ // If we actually free up any licenses then mark that we need
+ // to rescan to allocate these freed licenses.
+ //
+ if ((NumLicenses - License->LicensesNeeded) > 0)
+ ReScan = TRUE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
+ License->LicensesNeeded = NumLicenses;
+ License->Service = NULL;
+ }
+ }
+ }
+
+ if (LicenseService != NULL) {
+ ULONG Claimed = 0;
+
+ //
+ // If we switched services then adjust LicensesUsed
+ //
+ if (License->Service != LicenseService) {
+ LicenseService->LicensesUsed += NumLicenses;
+
+ if (License->Service != NULL) {
+ License->Service->LicensesUsed -= NumLicenses;
+ }
+ }
+
+ // Can only claim a # of licenses that we have
+ if ( LicenseService->LicensesClaimed < LicenseService->Licenses) {
+ Claimed = LicenseService->Licenses - LicenseService->LicensesClaimed;
+
+ if (Claimed > License->LicensesNeeded)
+ Claimed = License->LicensesNeeded;
+
+ }
+
+ LicenseService->LicensesClaimed += Claimed;
+ License->Service = LicenseService;
+ License->LicensesNeeded -= Claimed;
+
+ if (License->LicensesNeeded != 0)
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ else
+ License->Flags |= LLS_FLAG_LICENSED;
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ if (License->Flags & LLS_FLAG_LICENSED)
+ Svc->Flags |= LLS_FLAG_LICENSED;
+ else
+ Svc->Flags &= ~LLS_FLAG_LICENSED;
+
+ } else
+ Svc->Flags &= ~LLS_FLAG_LICENSED;
+
+ if (Svc->License != License)
+ License->RefCount++;
+
+ Svc->License = License;
+ if (ReScan)
+ FamilyLicenseUpdate ( Service->Family );
+
+} // SvcLicenseUpdate
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Misc licensing Routines.
+//
+// BackOffice and Mappings have a special affect on LicenseUseage and so
+// there are a couple miscelanous routines to handle them.
+//
+// There are also two special cases that cause us to re-walk our lists to
+// fixup the licenses:
+//
+// 1. Sometimes when we add licenses we free up some we already had claimed.
+// Ex: Users of a LicenseGroup used 5 SQL 4.0 licenses but could only
+// claim 2 (there weren't enough). Later we add 5 SQL 5.0 licenses,
+// since we can use these to get into license compliance we free the
+// 2 previously claimed licenses and take the 5 SQL 5.0 licenses. Now
+// we need to re-walk the user table to try and apply the 2 freed
+// licenses.
+//
+// If we switch a user to BackOffice then it will also free up licenses
+// causing us to re-walk the table.
+//
+// 2. If we ever apply new licenses to a user in a mapping then we need
+// to re-walk all the other users in the mapping and update their
+// license compliance.
+//
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MappingLicenseUpdate (
+ PMAPPING_RECORD Mapping,
+ BOOL ReSynch
+ )
+
+/*++
+
+Routine Description:
+
+ Go through all user records for a given mapping and recalc license
+ compliance.
+
+Arguments:
+
+ Mapping - the Mapping to recalc licenses for.
+
+ ReSync - If true all previous licenses are destroyed before the licenses
+ are checked, else only services that currently don't have a
+ license assignment are touched.
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PUSER_LICENSE_RECORD License = NULL;
+ PUSER_RECORD pUser;
+ PSVC_RECORD SvcTable = NULL;
+ BOOL BackOfficeCheck = FALSE;
+ ULONG Claimed;
+ BOOL Licensed = TRUE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: MappingLicenseUpdate\n"));
+#endif
+
+ //
+ // Run through all the users in the mapping - adjust their licenses
+ // based on the licenses the mapping has...
+ //
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ if (!(Mapping->LicenseList[i]->Flags & LLS_FLAG_LICENSED))
+ Licensed = FALSE;
+
+ if (Licensed)
+ Mapping->Flags |= LLS_FLAG_LICENSED;
+ else
+ Mapping->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // If we want to resynch then blow away all old references
+ //
+ if (ReSynch)
+ for (i = 0; i < Mapping->LicenseListSize; i++)
+ Mapping->LicenseList[i]->RefCount = 0;
+
+ //
+ // Special handling if the Mapping uses BackOffice
+ //
+ if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
+ License = LicenseListFind(BackOfficeStr, Mapping->LicenseList, Mapping->LicenseListSize);
+
+ //
+ // If there isn't one (can happen if all users were deleted from
+ // the mapping with BackOffice flag set). Then update everything.
+ //
+ if (License == NULL) {
+ License = LicenseListAdd(BackOffice->Family, &Mapping->LicenseList, &Mapping->LicenseListSize);
+
+ ASSERT(License != NULL);
+ if (License != NULL) {
+ License->Service = BackOffice;
+
+ // Can only claim a # of licenses that we have
+ if ( BackOffice->LicensesClaimed < BackOffice->Licenses) {
+ Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed;
+
+ if (Claimed > Mapping->Licenses)
+ Claimed = Mapping->Licenses;
+
+ }
+
+ BackOffice->LicensesUsed += Mapping->Licenses;
+ BackOffice->LicensesClaimed += Claimed;
+ License->LicensesNeeded = Mapping->Licenses - Claimed;
+
+ Mapping->Flags |= LLS_FLAG_LICENSED;
+ if (License->LicensesNeeded > 0) {
+ License->Flags &= ~LLS_FLAG_LICENSED;
+ Mapping->Flags &= ~LLS_FLAG_LICENSED;
+ }
+ }
+ }
+ }
+
+ //
+ // Run through all the members in the Mapping and update their license
+ // Compliance.
+ //
+ for (i = 0; i < Mapping->NumMembers; i++) {
+ pUser = UserListFind(Mapping->Members[i]);
+
+ if ( (pUser != NULL) && (pUser->Mapping == Mapping) ) {
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ SvcTable = pUser->Services;
+ pUser->LicensedProducts = 0;
+
+ if (Mapping->Flags & LLS_FLAG_SUITE_USE) {
+ if (Mapping->Flags & LLS_FLAG_LICENSED) {
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ pUser->LicensedProducts = pUser->ServiceTableSize;
+ } else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // All Services and users are flagged as per BackOffice
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (ReSynch)
+ SvcTable[j].License = NULL;
+
+ if (SvcTable[j].License == NULL) {
+ SvcTable[j].License = License;
+ License->RefCount++;
+ }
+
+ if (Mapping->Flags & LLS_FLAG_LICENSED) {
+ SvcTable[j].Flags |= LLS_FLAG_LICENSED;
+ } else
+ SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
+ }
+ } else {
+ BOOL Licensed = TRUE;
+
+ //
+ // Fixup all the service records
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (ReSynch)
+ SvcTable[j].License = NULL;
+
+ if (SvcTable[j].License == NULL) {
+ SvcLicenseUpdate(pUser, &SvcTable[j]);
+ BackOfficeCheck = TRUE;
+ }
+ }
+
+ //
+ // Now run through the services again and see if this user is
+ // actually licenses for all the products.
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if ( (SvcTable[j].License != NULL) && (SvcTable[j].License->Flags & LLS_FLAG_LICENSED) ) {
+ SvcTable[j].Flags |= LLS_FLAG_LICENSED;
+ pUser->LicensedProducts++;
+ } else {
+ SvcTable[j].Flags &= ~LLS_FLAG_LICENSED;
+ Licensed = FALSE;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ else
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+
+ }
+ RtlReleaseResource(&MappingListLock);
+
+ //
+ // Check if we need to re-check for BackOffice
+ //
+ if (BackOfficeCheck && (pUser != NULL))
+ UserBackOfficeCheck( pUser );
+
+} // MappingLicenseUpdate
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserMappingAdd (
+ PMAPPING_RECORD Mapping,
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Takes care of re-adjusting the licenses when we add a user to a mapping.
+ We need to free up any old licenses they have and point them to use
+ the licenses the mapping has.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i, j;
+ PUSER_LICENSE_RECORD License = NULL;
+ PSVC_RECORD SvcTable = NULL;
+ BOOL ReScan = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserMappingAdd\n"));
+#endif
+
+ if ( (pUser == NULL) || (Mapping == NULL) )
+ return;
+
+ //
+ // Run though and clean up all old licenses
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ SvcListLicenseFree(pUser);
+ UserLicenseListFree(pUser);
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+
+ pUser->Mapping = Mapping;
+ MappingLicenseUpdate(Mapping, FALSE);
+
+} // UserMappingAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FamilyLicenseUpdate (
+ PMASTER_SERVICE_ROOT Family
+ )
+
+/*++
+
+Routine Description:
+
+ Used when licenses are freed-up or added to a given family of products.
+ Goes through the user list looking for out-of-license conditions for the
+ given family of products and distributes the new licenses.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ PMASTER_SERVICE_RECORD LicenseService = NULL;
+ ULONG i, j;
+ PUSER_RECORD pUser;
+ BOOL UseMapping = FALSE;
+ BOOL ReScan = TRUE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: FamilyLicenseUpdate\n"));
+#endif
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ while (ReScan) {
+ //
+ // Walk user list in order of entry - adding any licenses
+ //
+ ReScan = FALSE;
+ i = 0;
+ while (i < UserListNumEntries) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ //
+ // only worry about un-licensed users
+ //
+ if ( !(pUser->Flags & LLS_FLAG_LICENSED ) ) {
+ //
+ // Find the License?
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ if (pUser->Mapping != NULL) {
+ License = LicenseListFind(Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ NumLicenses = pUser->Mapping->Licenses;
+ } else {
+ License = LicenseListFind(Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+ NumLicenses = 1;
+ }
+
+ //
+ // Make sure we need any extra licenses for this product
+ //
+ if ( (License != NULL) && (License->LicensesNeeded > 0) ) {
+ //
+ // We have found a user using this family of products in need
+ // of more licenses...
+ //
+ LicenseService = License->Service;
+
+ if (pUser->Mapping != NULL)
+ pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
+
+ //
+ // Check if we can satisfy licenses using currently
+ // assigned service
+ //
+ if ((LicenseService->Licenses - LicenseService->LicensesClaimed) >= License->LicensesNeeded) {
+ LicenseService->LicensesClaimed += License->LicensesNeeded;
+ License->LicensesNeeded = 0;
+ } else {
+ //
+ // See if any other service will satisfy it...
+ //
+ while ((LicenseService != NULL) && ((LicenseService->Licenses - LicenseService->LicensesClaimed) < NumLicenses ) )
+ if (LicenseService->next > 0)
+ LicenseService = MasterServiceTable[LicenseService->next - 1];
+ else
+ LicenseService = NULL;
+
+ //
+ // check if we found a service to satisfy licensing needs
+ //
+ if (LicenseService != NULL) {
+ //
+ // Free up any stuff - since we are freeing licenses
+ // we need to re-scan.
+ //
+ ReScan = TRUE;
+
+ License->Service->LicensesUsed -= NumLicenses;
+ License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded);
+
+ //
+ // Now do new stuff
+ //
+ License->Service = LicenseService;
+ License->Service->LicensesUsed += NumLicenses;
+ License->Service->LicensesClaimed += NumLicenses;
+ License->LicensesNeeded = 0;
+ } else {
+ //
+ // Eat any unclaimed licenses
+ //
+ LicenseService = License->Service;
+ if (LicenseService->Licenses > LicenseService->LicensesClaimed) {
+ License->LicensesNeeded -= (LicenseService->Licenses - LicenseService->LicensesClaimed);
+ LicenseService->LicensesClaimed = LicenseService->Licenses;
+ }
+ }
+ }
+
+ //
+ // Check if we got into license
+ //
+ if (License->LicensesNeeded == 0) {
+ BOOL Licensed = TRUE;
+
+ License->Flags |= LLS_FLAG_LICENSED;
+
+ //
+ // this license is fulfilled so scan product list and
+ // adjust flags on any product using this license.
+ //
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].License == License) {
+ pUser->Services[j].Flags |= LLS_FLAG_LICENSED;
+ } else
+ if (!(pUser->Services[j].Flags & LLS_FLAG_LICENSED))
+ Licensed = FALSE;
+ }
+
+ //
+ // Recalc how many products are licensed
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
+ pUser->LicensedProducts++;
+ }
+
+ if (Licensed)
+ pUser->Flags |= LLS_FLAG_LICENSED;
+ }
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ }
+ }
+
+ i++;
+ }
+ }
+
+ //
+ // If this license is for BackOffice, we have applied any licenses to
+ // anything set to use BackOffice. If there are still licenses left
+ // then see if any users should be auto-switched to BackOffice.
+ //
+ // if (Family == BackOffice->Family) {
+ i = 0;
+ while ( (BackOffice->LicensesClaimed < BackOffice->Licenses) && (i < UserListNumEntries) ) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL)
+ UserBackOfficeCheck(pUser);
+
+ i++;
+ }
+ // }
+
+ RtlReleaseResource(&UserListLock);
+
+ //
+ // Run through mapping and re-adjust any that need it.
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
+ MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ MappingLicenseUpdate( MappingList[i], FALSE );
+ }
+ }
+
+} // FamilyLicenseUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListLicenseDelete(
+ PMASTER_SERVICE_RECORD Service,
+ LONG Quantity
+)
+
+/*++
+
+Routine Description:
+
+ This is used when licenses are deleted. It must walk the user-list in
+ the reverse order they were entered (since licenses are applied in a
+ FIFO manner they are removed in a LIFO manner) and delete the required
+ number of licenses from those consumed.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LONG Licenses;
+ ULONG i, j;
+ PUSER_RECORD pUser;
+ PSVC_RECORD pService;
+ ULONG NumLicenses = 1;
+ PUSER_LICENSE_RECORD License = NULL;
+ BOOL UseMapping = FALSE;
+ LONG Claimed;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListLicenseDelete\n"));
+#endif
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ Licenses = 0 - Quantity;
+
+ //
+ // Walk user list in opposite order of entry - removing licenses
+ //
+ i = UserListNumEntries - 1;
+ while (((LONG)i >= 0) && (Licenses > 0)) {
+ pUser = RtlGetElementGenericTable(&UserList, i);
+
+ if (pUser != NULL) {
+ NumLicenses = 1;
+ UseMapping = FALSE;
+
+ //
+ // If we are a mapping then we may use more then one license
+ //
+ if (pUser->Mapping != NULL) {
+ NumLicenses = pUser->Mapping->Licenses;
+ UseMapping = TRUE;
+ }
+
+ //
+ // Try to find a license for this family of products
+ //
+ if (UseMapping)
+ License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize);
+ else
+ License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize);
+
+ if (License != NULL) {
+ //
+ // Check if same as product we adjusted
+ //
+ if (License->Service == Service) {
+ //
+ // Can only release how many we took
+ //
+ Claimed = NumLicenses - License->LicensesNeeded;
+ if (Claimed > 0) {
+ if (Claimed > Licenses) {
+ License->LicensesNeeded += Licenses;
+ License->Service->LicensesClaimed -= Licenses;
+ Licenses = 0;
+ } else {
+ License->LicensesNeeded = NumLicenses;
+ License->Service->LicensesClaimed -= Claimed;
+ Licenses -= Claimed;
+ }
+
+ License->Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // Flag any mapping that we need to recalc uses in the
+ // mapping
+ //
+ if (UseMapping)
+ pUser->Mapping->Flags |= LLS_FLAG_UPDATE;
+
+ //
+ // Scan product list and adjust flags on any
+ // product using this license.
+ //
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ for (j = 0; j < pUser->ServiceTableSize; j++)
+ if (pUser->Services[j].License == License)
+ pUser->Services[j].Flags &= ~LLS_FLAG_LICENSED;
+
+ //
+ // Recalc how many products are licensed
+ //
+ pUser->LicensedProducts = 0;
+ for (j = 0; j < pUser->ServiceTableSize; j++) {
+ if (pUser->Services[j].Flags & LLS_FLAG_LICENSED)
+ pUser->LicensedProducts++;
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ pUser->Flags &= ~LLS_FLAG_LICENSED;
+ }
+ }
+ }
+ }
+
+ i--;
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ //
+ // Run through mapping and re-adjust any that need it.
+ //
+ for (i = 0; i < MappingListSize; i++) {
+ if (MappingList[i]->Flags & LLS_FLAG_UPDATE) {
+ MappingList[i]->Flags &= ~LLS_FLAG_UPDATE;
+ MappingLicenseUpdate( MappingList[i], FALSE );
+ }
+ }
+
+} // UserListLicenseDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserBackOfficeCheck (
+ PUSER_RECORD pUser
+ )
+
+/*++
+
+Routine Description:
+
+ Checks if the user should switch to BackOffice, and if so - does so. If
+ we switch to BackOffice then we need to free up any old licenes the
+ user may be using and claim a BackOffice License.
+
+ Note: We will only switch to BackOffice if there are enough BackOffice
+ licenses available to satisfy our needs.
+
+Arguments:
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD Flags;
+ ULONG i;
+ ULONG LicenseListSize;
+ ULONG NumLicenses = 1;
+ PSVC_RECORD SvcTable = NULL;
+ PUSER_LICENSE_RECORD *LicenseList = NULL;
+ PUSER_LICENSE_RECORD License = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserBackOfficeCheck\n"));
+#endif
+
+ RtlEnterCriticalSection(&pUser->ServiceTableLock);
+ if (pUser->Mapping != NULL) {
+ Flags = pUser->Mapping->Flags;
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ LicenseList = pUser->Mapping->LicenseList;
+ NumLicenses = pUser->Mapping->Licenses;
+ } else {
+ Flags = pUser->Flags;
+ LicenseListSize = pUser->LicenseListSize;
+ LicenseList = pUser->LicenseList;
+ }
+
+ //
+ // If we are already using BackOffice - get out
+ //
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ return;
+ }
+
+ if ( Flags & LLS_FLAG_SUITE_AUTO )
+ //
+ // if we aren't licensed, or the # services == auto switch threshold
+ // then switch to using BackOffice
+ //
+ if ((!(Flags & LLS_FLAG_LICENSED)) || ((LicenseListSize + 1) >= BACKOFFICE_SWITCH) ) {
+ //
+ // Make sure we have licenses for this
+ //
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+ if ( BackOffice->Licenses >= (NumLicenses + BackOffice->LicensesClaimed) ) {
+ //
+ // Free up the old licenses - temporarily claim the BackOffice
+ // licenses so somebody else won't.
+ //
+ BackOffice->LicensesClaimed += NumLicenses;
+ UserLicenseListFree(pUser);
+ BackOffice->LicensesClaimed -= NumLicenses;
+
+ //
+ // UserLicenseListFree might have assigned us a license in
+ // the rescan if we are part of a mapping so check this.
+ //
+ if (pUser->Mapping != NULL)
+ Flags = pUser->Mapping->Flags;
+ else
+ Flags = pUser->Flags;
+
+ //
+ // If we are already using BackOffice - get out
+ //
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ RtlReleaseResource(&MasterServiceListLock);
+ return;
+ }
+
+ //
+ // And if part of a mapping free those up
+ //
+ if (pUser->Mapping != NULL)
+ MappingLicenseListFree(pUser->Mapping);
+
+ //
+ // Now add the BackOffice License
+ //
+ if (pUser->Mapping != NULL) {
+ pUser->Mapping->LicenseList = NULL;
+ pUser->Mapping->LicenseListSize = 0;
+
+ License = LicenseListAdd(BackOffice->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize);
+
+ LicenseList = pUser->Mapping->LicenseList;
+ LicenseListSize = pUser->Mapping->LicenseListSize;
+ } else {
+ pUser->LicenseList = NULL;
+ pUser->LicenseListSize = 0;
+
+ License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize);
+
+ LicenseList = pUser->LicenseList;
+ LicenseListSize = pUser->LicenseListSize;
+ }
+
+ ASSERT(License != NULL);
+ if (License != NULL)
+ License->Service = BackOffice;
+
+ //
+ // if mapping adjust mapping records then go through all users and
+ // adjust them
+ //
+ if (pUser->Mapping != NULL) {
+ pUser->Mapping->Flags |= LLS_FLAG_SUITE_USE;
+ pUser->Mapping->Flags |= LLS_FLAG_LICENSED;
+
+ BackOffice->LicensesUsed += NumLicenses;
+ BackOffice->LicensesClaimed += NumLicenses;
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+ RtlReleaseResource(&MasterServiceListLock);
+
+ MappingLicenseUpdate(pUser->Mapping, TRUE);
+ return;
+ } else {
+ pUser->Flags |= LLS_FLAG_SUITE_USE;
+ pUser->Flags |= LLS_FLAG_LICENSED;
+
+ pUser->LicensedProducts = pUser->ServiceTableSize;
+ BackOffice->LicensesUsed += NumLicenses;
+ BackOffice->LicensesClaimed += NumLicenses;
+
+ //
+ // Run through products & licenses adjusting licenses
+ //
+ SvcTable = pUser->Services;
+ for (i = 0; i < pUser->ServiceTableSize; i++) {
+ SvcTable[i].Flags |= LLS_FLAG_LICENSED;
+ SvcTable[i].License = License;
+ SvcTable[i].License->RefCount++;
+ }
+
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ }
+
+ RtlLeaveCriticalSection(&pUser->ServiceTableLock);
+
+} // UserBackOfficeCheck
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// Utility routines for managing the user and SID lists - used mostly
+// by the splay table routines.
+
+/////////////////////////////////////////////////////////////////////////
+RTL_GENERIC_COMPARE_RESULTS
+SidListCompare (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ FirstStruct -
+
+ SecondStruct -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UseRec1, UseRec2;
+ int ret;
+
+ if ((FirstStruct == NULL) || (SecondStruct == NULL))
+ return GenericEqual;
+
+ UseRec1 = (PUSER_RECORD) FirstStruct;
+ UseRec2 = (PUSER_RECORD) SecondStruct;
+
+ if (UseRec1->IDSize == UseRec2->IDSize) {
+ ret = memcmp((PVOID) UseRec1->UserID, (PVOID) UseRec2->UserID, UseRec1->IDSize);
+ if (ret < 0)
+ return GenericLessThan;
+ else if (ret > 0)
+ return GenericGreaterThan;
+ else
+ return GenericEqual;
+ } else
+ //
+ // Not same size, so just compare length
+ //
+ if (UseRec1->IDSize > UseRec2->IDSize)
+ return GenericGreaterThan;
+ else
+ return GenericLessThan;
+
+} // SidListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+RTL_GENERIC_COMPARE_RESULTS
+UserListCompare (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID FirstStruct,
+ PVOID SecondStruct
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ FirstStruct -
+
+ SecondStruct -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UseRec1, UseRec2;
+ int ret;
+
+ if ((FirstStruct == NULL) || (SecondStruct == NULL))
+ return GenericEqual;
+
+ UseRec1 = (PUSER_RECORD) FirstStruct;
+ UseRec2 = (PUSER_RECORD) SecondStruct;
+
+ ret = lstrcmpi((LPTSTR) UseRec1->UserID, (LPTSTR) UseRec2->UserID);
+
+ if (ret < 0)
+ return GenericLessThan;
+ else if (ret > 0)
+ return GenericGreaterThan;
+ else
+ return GenericEqual;
+
+} // UserListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PVOID
+UserListAlloc (
+ struct _RTL_GENERIC_TABLE *Table,
+ CLONG ByteSize
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ ByteSize -
+
+Return Value:
+
+
+--*/
+
+{
+ return (PVOID) LocalAlloc(LPTR, ByteSize);
+
+} // UserListAlloc
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListFree (
+ struct _RTL_GENERIC_TABLE *Table,
+ PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Table -
+
+ Buffer -
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UserRec;
+
+ if (Buffer == NULL)
+ return;
+
+ UserRec = (PUSER_RECORD) Buffer;
+ LocalFree(UserRec->UserID);
+ LocalFree(UserRec);
+
+} // UserListFree
+
+
+/////////////////////////////////////////////////////////////////////////
+PUSER_RECORD
+UserListFind(
+ LPTSTR UserName
+)
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListFind\n"));
+#endif
+
+ UserRec.UserID = (PVOID) UserName;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ return pUserRec;
+
+} // UserListFind
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListAdd(
+ PMASTER_SERVICE_RECORD Service,
+ ULONG DataType,
+ ULONG DataLength,
+ PVOID Data,
+ ULONG AccessCount,
+ DWORD LastAccess,
+ DWORD FlagsParam
+)
+
+/*++
+
+Routine Description:
+
+ Routine called by the Add cache routine to update the user and/or SID
+ lists with the new service information.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ BOOLEAN Added;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PRTL_GENERIC_TABLE pTable = NULL;
+ PRTL_RESOURCE pLock = NULL;
+ PRTL_RESOURCE pAddEnumLock = NULL;
+ BOOL SIDSwitch = FALSE;
+ BOOL UserLock = FALSE;
+ BOOL UserEnumLock = FALSE;
+ PMAPPING_RECORD pMap = NULL;
+ ULONG ProductLicenses, ProductLicensesUsed, i;
+ BOOL SwitchToBackOffice = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListAdd\n"));
+#endif
+
+ // only 2 bits are used
+ ASSERT( FlagsParam == ( FlagsParam & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO ) ) );
+
+ //
+ // Set up lock and table pointers based on if data is SID or username...
+ //
+ UserRec.UserID = Data;
+ if (DataType == DATA_TYPE_USERNAME) {
+ pTable = &UserList;
+ pLock = &UserListLock;
+ pAddEnumLock = &UserListAddEnumLock;
+ } else if (DataType == DATA_TYPE_SID) {
+ pTable = &SidList;
+ pLock = &SidListLock;
+ pAddEnumLock = &SidListAddEnumLock;
+ }
+
+ if (pTable == NULL)
+ return;
+
+ //
+ // The generic table package copies the record so the record is
+ // temporary, but since we store the string as a pointer the pointer is
+ // copied but the actual memory that is pointed to is kept around
+ // permenantly.
+ //
+ // We have already allocated memory for the Data
+ //
+ UserRec.UserID = Data;
+ UserRec.IDSize = DataLength;
+
+ UserRec.Flags = FlagsParam;
+ UserRec.LicensedProducts = 0;
+ UserRec.LastReplicated = 0;
+ UserRec.ServiceTableSize = 0;
+ UserRec.Services = NULL;
+ UserRec.Mapping = NULL;
+ UserRec.LicenseListSize = 0;
+ UserRec.LicenseList = NULL;
+
+ //
+ // Assume that the user is licensed - we will blast it if they aren't
+ // down below.
+ //
+ UserRec.Flags |= LLS_FLAG_LICENSED;
+
+ //
+ // Need to update list so get exclusive access. First get Add/Enum lock
+ // so we don't block reads if doing an enum.
+ //
+ RtlAcquireResourceExclusive(pAddEnumLock, TRUE);
+ RtlAcquireResourceExclusive(pLock, TRUE);
+
+ pUserRec = (PUSER_RECORD) RtlInsertElementGenericTable(pTable, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
+
+ if (pUserRec == NULL) {
+ ASSERT(FALSE);
+ LocalFree(UserRec.UserID);
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+ return;
+ }
+
+ pUserRec->Flags &= ~LLS_FLAG_DELETED;
+
+ // if auto suite is ever turned off, it's gone for good
+ if ( ! ( FlagsParam & LLS_FLAG_SUITE_AUTO ) )
+ {
+ // set suite use to be that specified in the function parameters
+ pUserRec->Flags &= ~LLS_FLAG_SUITE_AUTO;
+ pUserRec->Flags |= ( FlagsParam & LLS_FLAG_SUITE_USE );
+ }
+
+ //
+ // If for some reason the record is already there then we need to
+ // clean-up the name we allocated.
+ //
+ if (Added == FALSE) {
+ LocalFree(UserRec.UserID);
+
+ //
+ // If this is a SID then check the SID record to find the corresponding
+ // USER_RECORD (it better be there) and update that instead.. Note: We
+ // kludge this by storing the pointer to the user table in the
+ // LastReplicated field.
+ //
+ if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated != 0)) {
+ //
+ // Switch data as approp.
+ //
+ SIDSwitch = TRUE;
+ }
+ } else {
+ //
+ // Do this here so when we release to READ access another thread
+ // won't AV when trying to get access to it.
+ //
+ RtlInitializeCriticalSection(&pUserRec->ServiceTableLock);
+
+ if (DataType == DATA_TYPE_USERNAME) {
+ pMap = MappingListUserFind(UserRec.UserID);
+ pUserRec->Mapping = pMap;
+ UserListNumEntries++;
+ } else
+ SidListNumEntries++;
+
+ }
+
+ //
+ // If this is a SID, and we haven't gotten an appropriate user-rec
+ // then try to de-reference this and get the appropriate user-rec.
+ //
+ if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated == 0)) {
+ TCHAR UserName[MAX_USERNAME_LENGTH + 1];
+ TCHAR DomainName[MAX_DOMAINNAME_LENGTH + 1];
+ TCHAR FullName[MAX_USERNAME_LENGTH + MAX_DOMAINNAME_LENGTH + 2];
+ SID_NAME_USE snu;
+ PUSER_RECORD pUserRec2;
+ DWORD unSize, dnSize;
+
+ unSize = sizeof(UserName);
+ dnSize = sizeof(DomainName);
+ if (LookupAccountSid(NULL, (PSID) Data, UserName, &unSize, DomainName, &dnSize, &snu)) {
+ //
+ // Okay, de-referenced the SID, so go get the user-rec, but pre-pend
+ // domain first...
+ //
+ lstrcpy(FullName, DomainName);
+ lstrcat(FullName, TEXT("\\"));
+ lstrcat(FullName, UserName);
+ UserRec.UserID = FullName;
+ UserRec.IDSize = (lstrlen(FullName) + 1) * sizeof(TCHAR);
+
+ //
+ // Get locks, we will try shared first.
+ //
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ UserLock = TRUE;
+ SIDSwitch = TRUE;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec2 = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+ if (pUserRec2 != NULL) {
+ //
+ // Tarnation! we found it - so use it.
+ //
+ pUserRec->LastReplicated = (ULONG) pUserRec2;
+ } else {
+ //
+ // Dang it all... It ain't in the dern table, so we're gonna
+ // put it there. First need to alloc perm storage for UserID
+ //
+ UserRec.UserID = LocalAlloc(LPTR, UserRec.IDSize);
+ if (UserRec.UserID != NULL) {
+ lstrcpy((LPTSTR) UserRec.UserID, FullName);
+
+ //
+ // Need to update list so get exclusive access. First get
+ // Add/Enum lock so we don't block reads if doing an enum.
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ UserEnumLock = TRUE;
+ RtlConvertSharedToExclusive(&UserListLock);
+ pUserRec2 = (PUSER_RECORD) RtlInsertElementGenericTable(&UserList, (PVOID) &UserRec, sizeof(USER_RECORD), &Added);
+ }
+
+ //
+ // If we couldn't insert it then seomthing is wrong, clean up
+ // and exit.
+ //
+ if (pUserRec2 == NULL) {
+ ASSERT(FALSE);
+
+ if (UserRec.UserID != NULL)
+ LocalFree(UserRec.UserID);
+
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+
+ RtlReleaseResource(&UserListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+ return;
+ }
+
+ //
+ // Update SID USER_REC pointer (LastReplicated) and then finally
+ // free up the SID lock
+ //
+ pUserRec->LastReplicated = (ULONG) pUserRec2;
+
+ if (Added == TRUE) {
+ //
+ // Do this here so when we release to READ access another
+ // thread won't AV when trying to get access to it.
+ //
+ RtlInitializeCriticalSection(&pUserRec2->ServiceTableLock);
+ pMap = MappingListUserFind(UserRec.UserID);
+ pUserRec2->Mapping = pMap;
+ UserListNumEntries++;
+ }
+
+ RtlConvertExclusiveToShared(&UserListLock);
+
+ }
+
+ //
+ // We have found or added a USER_REC for the SID which pUserRec2
+ // points to. Now we need to switch locks and tables.
+ //
+ }
+ }
+
+ //
+ // If we need to munge from SID to User tables, then do so...
+ //
+ if (SIDSwitch) {
+ //
+ // Switch data as approp.
+ //
+ pUserRec = (PUSER_RECORD) pUserRec->LastReplicated;
+ DataType = DATA_TYPE_USERNAME;
+
+ //
+ // Release locks on SID Table
+ //
+ RtlReleaseResource(pLock);
+ RtlReleaseResource(pAddEnumLock);
+
+ //
+ // Now switch locks to User Table
+ //
+ pTable = &UserList;
+ pLock = &UserListLock;
+ pAddEnumLock = &UserListAddEnumLock;
+
+ if (UserEnumLock)
+ RtlReleaseResource(pAddEnumLock);
+
+ if (!UserLock)
+ RtlAcquireResourceShared(pLock, TRUE);
+ } else {
+ //
+ // No longer need exclusive access
+ //
+ RtlConvertExclusiveToShared(pLock);
+
+ //
+ // Also - no longer need Add/Enum Lock
+ //
+ RtlReleaseResource(pAddEnumLock);
+ }
+
+ //
+ // At this point we have either found the old record, or added a new
+ // one. In either case pUserRec points to the correct record.
+ //
+ if (pUserRec != NULL) {
+ //
+ // Check Service table to make sure our service is already there
+ //
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( Service->Name, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ if (pService != NULL) {
+ //
+ // Found entry in service table so just increment count
+ //
+ if (pService->AccessCount + AccessCount < MAX_ACCESS_COUNT)
+ pService->AccessCount += AccessCount;
+ else
+ pService->AccessCount = MAX_ACCESS_COUNT;
+
+ pService->LastAccess = LastAccess;
+ } else {
+ //
+ // Need to add more entries to service table (or create it...)
+ //
+ if (pUserRec->Services == NULL)
+ SvcTable = (PSVC_RECORD) LocalAlloc( LPTR, sizeof(SVC_RECORD));
+ else
+ SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize + 1), LHND);
+
+ pUserRec->Services = SvcTable;
+ ASSERT(SvcTable != NULL);
+ if (SvcTable != NULL) {
+ DWORD Flags;
+
+ if (pUserRec->Mapping != NULL)
+ Flags = pUserRec->Mapping->Flags;
+ else
+ Flags = pUserRec->Flags;
+
+ SvcTable[pUserRec->ServiceTableSize].Service = Service;
+ SvcTable[pUserRec->ServiceTableSize].LastAccess = LastAccess;
+
+ //
+ // Update AccessCount field, but make sure we don't roll over
+ //
+ if (AccessCount < MAX_ACCESS_COUNT)
+ SvcTable[pUserRec->ServiceTableSize].AccessCount = AccessCount;
+ else
+ SvcTable[pUserRec->ServiceTableSize].AccessCount = MAX_ACCESS_COUNT;
+
+ SvcTable[pUserRec->ServiceTableSize].Flags = LLS_FLAG_LICENSED;
+
+ //
+ // Now update the actual license info
+ //
+ SvcLicenseUpdate(pUserRec, &SvcTable[pUserRec->ServiceTableSize]);
+ pUserRec->ServiceTableSize += 1;
+
+ if (SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED)
+ pUserRec->LicensedProducts++;
+
+ //
+ // If the added product isn't licensed then update user flag
+ //
+ if (IsMaster && !(SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED) )
+ pUserRec->Flags &= ~LLS_FLAG_LICENSED;
+
+ // Now that it is all setup - sort the table (so search will work)
+ qsort((void *) pUserRec->Services, (size_t) pUserRec->ServiceTableSize, sizeof(SVC_RECORD), SvcListCompare);
+
+ UserBackOfficeCheck ( pUserRec );
+ }
+
+ }
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ }
+
+ RtlReleaseResource(pLock);
+} // UserListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+//
+// The AddCache routines are a queue of User Identifiers (Username or
+// SID's) and the service being accessed. Records are dequeued by a
+// background thread and handed off to the UserListAdd function.
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+AddCacheManager (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+
+Return Value:
+
+ This thread never exits.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PADD_CACHE pAdd;
+
+ //
+ // Loop forever waiting to be given the opportunity to serve the
+ // greater good.
+ //
+ for ( ; ; ) {
+ //
+ // Wait to be notified that there is work to be done
+ //
+ Status = NtWaitForSingleObject( LLSAddCacheEvent, TRUE, NULL );
+
+ //
+ // Take an item from the add cache.
+ //
+ RtlEnterCriticalSection(&AddCacheLock);
+ while (AddCache != NULL) {
+ pAdd = AddCache;
+ AddCache = AddCache->prev;
+ AddCacheSize--;
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ if (pAdd != NULL) {
+ UserListAdd(pAdd->Service, pAdd->DataType, pAdd->DataLength, pAdd->Data, pAdd->AccessCount, pAdd->LastAccess, pAdd->Flags);
+ LocalFree(pAdd);
+ }
+
+ Sleep(0);
+ //
+ // Need to re-enter critical section to check in the while loop
+ RtlEnterCriticalSection(&AddCacheLock);
+ }
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+ }
+
+} // AddCacheManager
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ DWORD Ignore;
+ HANDLE Thread;
+
+ //
+ // Initialize the generic table.
+ //
+ RtlInitializeGenericTable ( &UserList,
+ UserListCompare,
+ UserListAlloc,
+ UserListFree,
+ (PVOID) TEXT("LLS Table") );
+
+ RtlInitializeCriticalSection(&GenTableLock);
+ RtlInitializeResource(&UserListLock);
+ RtlInitializeResource(&UserListAddEnumLock);
+
+ //
+ // Initialize the SID table.
+ //
+ RtlInitializeGenericTable ( &SidList,
+ SidListCompare,
+ UserListAlloc,
+ UserListFree,
+ (PVOID) TEXT("LLS SID Table") );
+
+ RtlInitializeResource(&SidListLock);
+ RtlInitializeResource(&SidListAddEnumLock);
+
+ //
+ // Now our add cache
+ //
+ RtlInitializeCriticalSection(&AddCacheLock);
+
+ //
+ // Create the Add Cache Management event
+ //
+ Status = NtCreateEvent(
+ &LLSAddCacheEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Create the Add Cache management thread
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) AddCacheManager,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+
+ LastUsedTime = DateSystemGet();
+
+} // UserListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListUpdate(
+ ULONG DataType,
+ PVOID Data,
+ PSERVICE_RECORD Service
+)
+
+/*++
+
+Routine Description:
+
+ Actual start of the perseat license code. Given a SID or UserName find
+ the record in the appropriate table and check the given service. If the
+ service is already there then the info is updated, if it isn't there then
+ the record is put onto the add cache queue for background processing.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ BOOLEAN Added;
+ ULONG DataLength;
+ PSVC_RECORD pService;
+ PSVC_RECORD SvcTable = NULL;
+ PRTL_GENERIC_TABLE pTable = NULL;
+ PRTL_RESOURCE pLock = NULL;
+ PADD_CACHE pAdd = NULL;
+ NTSTATUS NtStatus;
+ BOOL ToAddCache = FALSE;
+ BOOL FullName = TRUE;
+ LPTSTR pName;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: UserListUpdate\n"));
+#endif
+
+ //
+ // Setup table and lock pointers based if Data is UserName or SID
+ //
+ UserRec.UserID = Data;
+ if (DataType == DATA_TYPE_USERNAME) {
+ pTable = &UserList;
+ pLock = &UserListLock;
+ DataLength = (lstrlen((LPWSTR) Data) + 1) * sizeof(TCHAR);
+ } else if (DataType == DATA_TYPE_SID) {
+ pTable = &SidList;
+ pLock = &SidListLock;
+ DataLength = RtlLengthSid((PSID) Data);
+ }
+
+ if (pTable == NULL)
+ return;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceShared(pLock, TRUE);
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(pTable, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+ if (pUserRec == NULL)
+ ToAddCache = TRUE;
+ else {
+ //
+ // pUserRec now points to the record we must update.
+ //
+ // Check Service table to make sure our service is already there
+ //
+ pUserRec->Flags &= ~LLS_FLAG_DELETED;
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ pService = SvcListFind( Service->DisplayName, pUserRec->Services, pUserRec->ServiceTableSize );
+
+ if (pService == NULL)
+ ToAddCache = TRUE;
+ else {
+ //
+ // Found entry in service table so just increment count
+ //
+ pService->AccessCount += 1;
+ pService->LastAccess = LastUsedTime;
+ }
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ }
+
+ RtlReleaseResource(pLock);
+
+ if (ToAddCache) {
+ //
+ // Couldn't find the specific user/service, so put it on the Add Cache.
+ // First alloc memory for the name and Add Cache record.
+ //
+ pAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE));
+ if (pAdd == NULL) {
+ ASSERT(FALSE);
+ return;
+ }
+
+ if (DataType == DATA_TYPE_USERNAME) {
+ FullName = FALSE;
+ pName = (LPTSTR) Data;
+
+ //
+ // Make sure first char isn't backslash, if not then look for
+ // backslash as domain-name. If first char is backslash then get
+ // rid of it.
+ //
+ if (*pName != TEXT('\\'))
+ while ((*pName != TEXT('\0')) && !FullName) {
+ if (*pName == TEXT('\\'))
+ FullName = TRUE;
+
+ pName++;
+ }
+ else
+ ((LPTSTR) Data)++;
+
+ }
+
+ //
+ // If we don't have a fully qualified Domain\Username, then tack the
+ // Domain name onto the name.
+ //
+ if (!FullName) {
+ UserRec.UserID = LocalAlloc( LPTR, DataLength + MyDomainSize);
+
+ if (UserRec.UserID == NULL) {
+ ASSERT(FALSE);
+ LocalFree(pAdd);
+ return;
+ }
+
+ pAdd->Data = UserRec.UserID;
+
+ lstrcpy((LPTSTR) pAdd->Data, MyDomain);
+ lstrcat((LPTSTR) pAdd->Data, (LPTSTR) Data);
+ pAdd->DataLength = DataLength + MyDomainSize;
+
+ } else {
+ UserRec.UserID = LocalAlloc( LPTR, DataLength);
+
+ if (UserRec.UserID == NULL) {
+ ASSERT(FALSE);
+ LocalFree(pAdd);
+ return;
+ }
+
+ pAdd->Data = UserRec.UserID;
+ memcpy(pAdd->Data, Data, DataLength);
+ pAdd->DataLength = DataLength;
+ }
+
+ //
+ // copy over all the data fields into the newly created Add Cache
+ // record.
+ //
+ pAdd->DataType = DataType;
+ pAdd->Service = Service->MasterService;
+ pAdd->AccessCount = 1;
+ pAdd->LastAccess = LastUsedTime;
+ pAdd->Flags = LLS_FLAG_SUITE_AUTO;
+
+ //
+ // Now update the actual Add Cache
+ //
+ RtlEnterCriticalSection(&AddCacheLock);
+ pAdd->prev = AddCache;
+ AddCache = pAdd;
+ AddCacheSize++;
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+ //
+ // Now must signal the event so we can pull off the new record.
+ //
+ NtStatus = NtSetEvent( LLSAddCacheEvent, NULL );
+ ASSERT(NT_SUCCESS(NtStatus));
+ }
+
+} // UserListUpdate
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+AddCacheDebugDump ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PADD_CACHE pAdd;
+ UNICODE_STRING UString;
+ ULONG i = 0;
+
+ RtlEnterCriticalSection(&AddCacheLock);
+
+ dprintf(TEXT("Add Cache Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(ADD_CACHE), AddCacheSize);
+ pAdd = AddCache;
+
+ while (pAdd != NULL) {
+ if (pAdd->DataType == DATA_TYPE_USERNAME)
+ dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
+ ++i,
+ pAdd->Service,
+ pAdd->DataLength,
+ pAdd->Data);
+ else if (pAdd->DataType == DATA_TYPE_SID) {
+ Status = RtlConvertSidToUnicodeString(&UString, (PSID) pAdd->Data, TRUE);
+
+ dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"),
+ ++i,
+ pAdd->Service,
+ pAdd->DataLength,
+ UString.Buffer);
+
+ RtlFreeUnicodeString(&UString);
+ }
+
+ pAdd = pAdd->prev;
+ }
+
+ RtlLeaveCriticalSection(&AddCacheLock);
+
+} // AddCacheDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ dprintf(TEXT("User List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ while (UserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ if (UserRec->Mapping != NULL)
+ dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
+ ++i,
+ TimeToString(UserRec->LastReplicated),
+ UserRec->LicenseListSize,
+ UserRec->ServiceTableSize,
+ UserRec->Flags,
+ UserRec->Mapping->Name,
+ UserRec->IDSize,
+ (LPTSTR) UserRec->UserID );
+ else
+ dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
+ ++i,
+ TimeToString(UserRec->LastReplicated),
+ UserRec->LicenseListSize,
+ UserRec->ServiceTableSize,
+ UserRec->Flags,
+ UserRec->IDSize,
+ (LPTSTR) UserRec->UserID );
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ RtlReleaseResource(&UserListLock);
+} // UserListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugInfoDump(
+ PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD SvcTable = NULL;
+ ULONG i;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ dprintf(TEXT("User List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries);
+
+ //
+ // Only dump user if one was specified.
+ //
+ if (lstrlen((LPWSTR) Data) > 0) {
+ UserRec.UserID = Data;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ if (pUserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ if (pUserRec->Mapping != NULL)
+ dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"),
+ TimeToString(pUserRec->LastReplicated),
+ pUserRec->LicenseListSize,
+ pUserRec->ServiceTableSize,
+ pUserRec->Flags,
+ pUserRec->Mapping->Name,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+ else
+ dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"),
+ TimeToString(pUserRec->LastReplicated),
+ pUserRec->LicenseListSize,
+ pUserRec->ServiceTableSize,
+ pUserRec->Flags,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+
+ //
+ // Now do the service table - but get critical section first.
+ //
+ RtlEnterCriticalSection(&pUserRec->ServiceTableLock);
+ SvcTable = pUserRec->Services;
+
+ if (pUserRec->ServiceTableSize != 0)
+ dprintf(TEXT("\nServiceTable\n"));
+
+ for (i = 0; i < pUserRec->ServiceTableSize; i++)
+ dprintf( TEXT(" AC: %4lu LA: %s Flags: 0x%4lX Svc: %s\n"),
+ SvcTable[i].AccessCount,
+ TimeToString(SvcTable[i].LastAccess),
+ SvcTable[i].Flags,
+ SvcTable[i].Service->Name );
+
+ if (pUserRec->LicenseListSize != 0)
+ dprintf(TEXT("\nLicenseTable\n"));
+
+ for (i = 0; i < pUserRec->LicenseListSize; i++)
+ dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"),
+ pUserRec->LicenseList[i]->Flags,
+ pUserRec->LicenseList[i]->RefCount,
+ pUserRec->LicenseList[i]->LicensesNeeded,
+ pUserRec->LicenseList[i]->Service->Name );
+
+ RtlLeaveCriticalSection(&pUserRec->ServiceTableLock);
+
+ } else
+ dprintf(TEXT("User Not Found: %s\n"), (LPWSTR) Data);
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+} // UserListDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+UserListDebugFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceExclusive(&UserListLock, TRUE);
+
+ RtlEnterCriticalSection(&GenTableLock);
+ RtlLookupElementGenericTable(&UserList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ RtlReleaseResource(&UserListLock);
+} // UserListDebugFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ UNICODE_STRING UString;
+ NTSTATUS NtStatus;
+ PVOID RestartKey = NULL;
+
+ RtlAcquireResourceShared(&SidListLock, TRUE);
+
+ dprintf(TEXT("SID List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
+
+ while (UserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) UserRec->UserID, TRUE);
+ dprintf(TEXT("%4lu) User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
+ ++i,
+ UserRec->LastReplicated,
+ UserRec->ServiceTableSize,
+ UserRec->IDSize,
+ UString.Buffer );
+
+ RtlFreeUnicodeString(&UString);
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey);
+ }
+
+ RtlReleaseResource(&SidListLock);
+} // SidListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugInfoDump(
+ PVOID Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+ PUSER_RECORD pUserRec;
+ PSVC_RECORD SvcTable = NULL;
+ ULONG i;
+
+ RtlAcquireResourceShared(&SidListLock, TRUE);
+ dprintf(TEXT("SID List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries);
+
+ //
+ // Only dump user if one was specified.
+ //
+ if (lstrlen((LPWSTR) Data) > 0) {
+ UserRec.UserID = Data;
+
+ RtlEnterCriticalSection(&GenTableLock);
+ pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&SidList, &UserRec);
+ RtlLeaveCriticalSection(&GenTableLock);
+
+ if (pUserRec != NULL) {
+ //
+ // Dump info for found user-rec
+ //
+ dprintf(TEXT(" User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"),
+ pUserRec->LastReplicated,
+ pUserRec->ServiceTableSize,
+ pUserRec->IDSize,
+ (LPTSTR) pUserRec->UserID );
+
+ // No Service Table for SID's
+ } else
+ dprintf(TEXT("SID Not Found: %s\n"), (LPWSTR) Data);
+ }
+
+ RtlReleaseResource(&SidListLock);
+
+} // SidListDebugInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+SidListDebugFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_RECORD UserRec;
+
+ //
+ // Searching so don't need exclusive access
+ //
+ RtlAcquireResourceExclusive(&SidListLock, TRUE);
+
+ RtlReleaseResource(&SidListLock);
+} // SidListDebugFlush
+
+
+#endif
diff --git a/private/net/svcdlls/lls/server/perseat.h b/private/net/svcdlls/lls/server/perseat.h
new file mode 100644
index 000000000..00257f1c6
--- /dev/null
+++ b/private/net/svcdlls/lls/server/perseat.h
@@ -0,0 +1,235 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ PerSeat.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 12-Jan-1996
+ o Added support for maintaining the SUITE_USE flag when adding
+ users to the AddCache.
+ o Exported function prototype for MappingLicenseListFree().
+
+--*/
+
+#ifndef _LLS_PERSEAT_H
+#define _LLS_PERSEAT_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DATA_TYPE_USERNAME 0
+#define DATA_TYPE_SID 1
+
+#define MAX_ACCESS_COUNT 0xFFFFFFF
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Add cache is here as add records need exclusive access to the user table.
+// Since we may have alot of read requests comming in at once, we don't want
+// to hold up our reply waiting for the exclusive access to be granted, so
+// we just dump it onto the add cache (queue) and continue on.
+//
+// This is even more important with outgoing replication going on as we can
+// have shared access lock on the table for awhile.
+//
+// Incomming replication just bundles up the data and sticks it on the Add
+// Cache to be processed like normal requests.
+//
+struct _ADD_CACHE;
+
+typedef struct _ADD_CACHE {
+ struct _ADD_CACHE *prev;
+ ULONG DataType;
+ ULONG DataLength;
+ PVOID Data;
+ PMASTER_SERVICE_RECORD Service;
+ ULONG AccessCount;
+ DWORD LastAccess;
+ DWORD Flags;
+} ADD_CACHE, *PADD_CACHE;
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// These records are for storing the actual user-useage information.
+//
+typedef struct _USER_LICENSE_RECORD {
+ DWORD Flags;
+ PMASTER_SERVICE_ROOT Family;
+ ULONG RefCount;
+
+ //
+ // Version of product License applies to
+ PMASTER_SERVICE_RECORD Service;
+ ULONG LicensesNeeded;
+} USER_LICENSE_RECORD, *PUSER_LICENSE_RECORD;
+
+typedef struct _SVC_RECORD {
+ //
+ // Actual service this is for
+ //
+ PMASTER_SERVICE_RECORD Service;
+
+ //
+ // What license we took - The product may be SQL 3.0, but in determining
+ // the license we might have grabbed a SQL 4.0 license...
+ //
+ PUSER_LICENSE_RECORD License;
+
+ ULONG AccessCount;
+ DWORD LastAccess;
+ ULONG Suite;
+ DWORD Flags;
+} SVC_RECORD, *PSVC_RECORD;
+
+typedef struct _USER_RECORD {
+ ULONG IDSize;
+ PVOID UserID;
+
+ //
+ // Pointer to mapping to use.
+ //
+ PMAPPING_RECORD Mapping;
+
+ //
+ // Flags is mostly used right now for marking records to be deleted and
+ // if backoffice has been set.
+ //
+ DWORD Flags;
+
+ //
+ // How many products are licensed vs unlicensed
+ //
+ ULONG LicensedProducts;
+
+ //
+ // Date when last replicated. Note: For SID records this is a pointer
+ // into the USER_RECORD for the appropriate user.
+ //
+ ULONG LastReplicated;
+
+ //
+ // Keep only a critical section lock, and not RTL_RESOURCE (for read/write
+ // locks). All updates of existing services (most common case by far) will
+ // be very quick, so exclusive access isn't that bad. RTL_RESOURCE also
+ // would make our table size increase dramatically and add too much extra
+ // processing.
+ //
+ RTL_CRITICAL_SECTION ServiceTableLock;
+
+ //
+ // Service table is a linear buffer, we use the service number to access
+ // into this buffer.
+ //
+ ULONG ServiceTableSize;
+
+ // Stuff per service - linear buffer...
+ PSVC_RECORD Services;
+
+ //
+ // Licenses the user is using
+ //
+ ULONG LicenseListSize;
+ PUSER_LICENSE_RECORD *LicenseList;
+} USER_RECORD, *PUSER_RECORD;
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+extern ULONG UserListNumEntries;
+extern ULONG SidListNumEntries;
+extern RTL_GENERIC_TABLE UserList;
+extern RTL_GENERIC_TABLE SidList;
+
+extern RTL_RESOURCE UserListLock;
+extern RTL_RESOURCE SidListLock;
+
+//
+// The enum processes for replication and UI can take awhile to go through
+// all the records, while doing this they need a shared lock on the file.
+// However, if we request an exclusive access during this time the pending
+// exclusive access will block other shared access's. Therefore we get
+// this lock first before attempting either an add or enum.
+//
+// An add will block enums, but neither of these function are as time
+// critical as updating normal user records.
+//
+extern RTL_RESOURCE UserListAddEnumLock;
+extern RTL_RESOURCE SidListAddEnumLock;
+
+//
+// The AddCache itself, a critical section to protect access to it and an
+// event to signal the server when there are items on it that need to be
+// processed.
+//
+extern PADD_CACHE AddCache;
+extern ULONG AddCacheSize;
+extern RTL_CRITICAL_SECTION AddCacheLock;
+extern HANDLE LLSAddCacheEvent;
+
+extern DWORD LastUsedTime;
+extern BOOL UsersDeleted;
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+PSVC_RECORD SvcListFind( LPTSTR DisplayName, PSVC_RECORD ServiceList, ULONG NumTableEntries );
+NTSTATUS SvcListDelete( LPTSTR UserName, LPTSTR ServiceName );
+VOID SvcListLicenseFree( PUSER_RECORD pUser );
+VOID SvcListLicenseUpdate( PUSER_RECORD pUser );
+
+VOID UserListInit();
+VOID UserListUpdate( ULONG DataType, PVOID Data, PSERVICE_RECORD Service );
+PUSER_RECORD UserListFind( LPTSTR UserName );
+
+VOID UserBackOfficeCheck( PUSER_RECORD pUser );
+
+VOID UserListLicenseDelete( PMASTER_SERVICE_RECORD Service, LONG Quantity );
+VOID UserLicenseListFree ( PUSER_RECORD pUser );
+
+VOID UserMappingAdd ( PMAPPING_RECORD Mapping, PUSER_RECORD pUser );
+VOID FamilyLicenseUpdate ( PMASTER_SERVICE_ROOT Family );
+VOID SvcLicenseUpdate( PUSER_RECORD pUser, PSVC_RECORD Svc );
+
+VOID MappingLicenseListFree ( PMAPPING_RECORD Mapping );
+VOID MappingLicenseUpdate ( PMAPPING_RECORD Mapping, BOOL ReSynch );
+
+
+/////////////////////////////////////////////////////////////////////////
+#if DBG
+
+VOID AddCacheDebugDump( );
+VOID UserListDebugDump( );
+VOID UserListDebugInfoDump( PVOID Data );
+VOID UserListDebugFlush( );
+VOID SidListDebugDump( );
+VOID SidListDebugInfoDump( PVOID Data );
+VOID SidListDebugFlush( );
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/purchase.c b/private/net/svcdlls/lls/server/purchase.c
new file mode 100644
index 000000000..17720ac82
--- /dev/null
+++ b/private/net/svcdlls/lls/server/purchase.c
@@ -0,0 +1,865 @@
+/*++
+
+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 <debug.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"
+
+
+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
+ );
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LicenseListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&LicenseListLock);
+
+} // 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 * ServiceList;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseServiceListFind\n"));
+#endif
+ if (ServiceName == NULL)
+ return NULL;
+
+ if ( UsePerServerList )
+ {
+ end = PerServerLicenseServiceListSize - 1;
+ ServiceList = PerServerLicenseServiceList;
+ }
+ else
+ {
+ end = LicenseServiceListSize - 1;
+ ServiceList = LicenseServiceList;
+ }
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = ServiceList[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;
+
+#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)
+ *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1));
+ else
+ *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalReAlloc(*pServiceList, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (*pServiceList == NULL) {
+ ASSERT(FALSE);
+ *pServiceListSize = 0;
+ return NULL;
+ }
+
+ Service = (PLICENSE_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Create space for saving off the name.
+ //
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->ServiceName = NewServiceName;
+ lstrcpy(NewServiceName, ServiceName);
+
+ (*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 };
+
+ 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;
+ PMASTER_SERVICE_RECORD mService;
+ LLS_LICENSE_INFO_1 lic;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LicenseAdd\n"));
+#endif
+
+ 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;
+ }
+
+ 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.
+ //
+ if (PurchaseList == NULL)
+ PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1));
+ else
+ PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalReAlloc(PurchaseList, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1), LHND);
+
+ //
+ // Make sure we could allocate service table
+ //
+ if (PurchaseList == NULL)
+ {
+ ASSERT(FALSE);
+ PurchaseListSize = 0;
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ PurchaseRecord = &PurchaseList[PurchaseListSize];
+
+ //
+ // Create space for saving off the Admin Name
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Admin) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Admin = NewName;
+ lstrcpy(NewName, Admin);
+
+ //
+ // Create space for saving off the Comment
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Comment = NewName;
+ lstrcpy(NewName, Comment);
+
+ //
+ // Create space for saving off the Source
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Source) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Source = NewName;
+ lstrcpy(NewName, Source);
+
+ //
+ // Create space for saving off the Vendor
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Vendor) + 1) * sizeof(TCHAR));
+ if (NewName == NULL)
+ {
+ ASSERT(FALSE);
+ RtlReleaseResource(&LicenseListLock);
+ return STATUS_NO_MEMORY;
+ }
+
+ // now copy it over...
+ PurchaseRecord->Vendor = NewName;
+ lstrcpy(NewName, Vendor);
+
+ //
+ // 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;
+ PLICENSE_PURCHASE_RECORD PurchaseRecord;
+ 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 == BackOffice) ) )
+ {
+ 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);
+
+ *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;
+
+ //
+ // 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 ];
+
+ lstrcpy( szExpirationDate, TimeToString( PurchaseList[i].ExpirationDate ) );
+
+ 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
+ );
+ }
+ }
+
+LicenseListDebugDumpExit:
+ RtlReleaseResource(&LicenseListLock);
+
+ return;
+} // LicenseListDebugDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/purchase.h b/private/net/svcdlls/lls/server/purchase.h
new file mode 100644
index 000000000..1e18b4879
--- /dev/null
+++ b/private/net/svcdlls/lls/server/purchase.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Purchase.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+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.
+
+--*/
+
+#ifndef _LLS_PURCHASE_H
+#define _LLS_PURCHASE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _LICENSE_SERVICE_RECORD {
+ LPTSTR ServiceName;
+ ULONG Index;
+ LONG NumberLicenses;
+} LICENSE_SERVICE_RECORD, *PLICENSE_SERVICE_RECORD;
+
+
+typedef struct _LICENSE_PURCHASE_RECORD {
+ PLICENSE_SERVICE_RECORD Service;
+ LONG NumberLicenses;
+ DWORD Date;
+ LPTSTR Admin;
+ LPTSTR Comment;
+
+ // added for SUR:
+ PLICENSE_SERVICE_RECORD PerServerService; // points to per server
+ // license tally for this
+ // service
+
+ DWORD AllowedModes; // bit field: 1, allowed
+ // to be used in per seat
+ // mode; 2, per server
+
+ DWORD CertificateID; // identifies the secure
+ // certificate from which
+ // these licenses came, or
+ // 0 if unsecure
+
+ LPTSTR Source; // source of the certificate
+ // currently supported
+ // values are "None" and
+ // "Paper"
+
+ DWORD ExpirationDate; // time at which this
+ // certificate expires
+
+ DWORD MaxQuantity; // the largest number of licenses
+ // that can be installed from this
+ // certificate
+
+ LPTSTR Vendor; // vendor of the product, e.g.,
+ // "Microsoft"
+
+ DWORD Secrets[ LLS_NUM_SECRETS ]; // secrets for LSAPI
+ // challenge mechanism
+
+} LICENSE_PURCHASE_RECORD, *PLICENSE_PURCHASE_RECORD;
+
+
+
+extern ULONG LicenseServiceListSize;
+extern PLICENSE_SERVICE_RECORD *LicenseServiceList;
+
+extern ULONG PerServerLicenseServiceListSize;
+extern PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList;
+
+extern PLICENSE_PURCHASE_RECORD PurchaseList;
+extern ULONG PurchaseListSize;
+
+extern RTL_RESOURCE LicenseListLock;
+
+
+VOID LicenseListInit();
+PLICENSE_SERVICE_RECORD LicenseServiceListFind( LPTSTR ServiceName, BOOL UsePerServerList );
+PLICENSE_SERVICE_RECORD LicenseServiceListAdd( LPTSTR ServiceName, BOOL UsePerServerList );
+ULONG ProductLicensesGet( LPTSTR ServiceName, BOOL UsePerServerList );
+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 );
+
+
+#if DBG
+VOID LicenseListDebugDump( );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/registry.c b/private/net/svcdlls/lls/server/registry.c
new file mode 100644
index 000000000..622019b26
--- /dev/null
+++ b/private/net/svcdlls/lls/server/registry.c
@@ -0,0 +1,1707 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ Registry reading routines for License Server. Can Scan the registry
+ for all License Service entries, or for a specific service.
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Removed unnecessary RegConnect() to local server.
+ o Added secure service list. This list tracks the products that
+ require "secure" license certificates for all licenses; i.e., the
+ products that do not accept the 3.51 Honesty method of "enter the
+ number of license you purchased."
+ o Added routine to update the concurrent limit value in the registry
+ to accurately reflect the connection limit of secure products.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <rpc.h>
+#include <rpcndr.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+#include "llsutil.h"
+
+// #define API_TRACE 1
+
+#define NUM_MAPPING_ENTRIES 2
+LPTSTR NameMappingTable[] = {
+ TEXT("SQL"),
+ TEXT("SNA")
+}; // NameMappingTable
+
+
+LPTSTR NameMappingTable2[] = {
+ TEXT("Microsoft SQL Server"),
+ TEXT("Microsoft SNA Server")
+}; // NameMappingTable2
+
+
+ULONG NumFilePrintEntries = 0;
+LPTSTR *FilePrintTable = NULL;
+
+#define KEY_NAME_SIZE 512
+
+HANDLE LLSRegistryEvent;
+
+
+ULONG LocalServiceListSize = 0;
+PLOCAL_SERVICE_RECORD *LocalServiceList = NULL;
+
+RTL_RESOURCE LocalServiceListLock;
+
+ULONG SecureServiceListSize = 0;
+LPTSTR * SecureServiceList = NULL;
+ULONG SecureServiceBufferSize = 0; // in bytes!
+TCHAR * SecureServiceBuffer = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigInfoRegistryInit(
+ DWORD * pUseEnterprise,
+ LPTSTR pEnterpriseServer,
+ DWORD * pReplicationType,
+ DWORD * pReplicationTime,
+ DWORD * pLogLevel
+ )
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static BOOL ReportedError = FALSE;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ BOOL ret = FALSE;
+ DWORD UseEnterprise, ReplicationType, ReplicationTime;
+ static TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 3];
+ DWORD LogLevel;
+
+ lstrcpy(EnterpriseServer, TEXT(""));
+ UseEnterprise = ReplicationType = ReplicationTime = LogLevel = 0;
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"));
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(UseEnterprise);
+ Status = RegQueryValueEx(hKey2, TEXT("UseEnterprise"), NULL, &dwType, (LPBYTE) &UseEnterprise, &dwSize);
+ if (Status == ERROR_SUCCESS)
+ *pUseEnterprise = UseEnterprise;
+ else
+ UseEnterprise = 0;
+
+
+ if (Status == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(EnterpriseServer);
+ Status = RegQueryValueEx(hKey2, TEXT("EnterpriseServer"), NULL, &dwType, (LPBYTE) &EnterpriseServer, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ lstrcpy(pEnterpriseServer, EnterpriseServer);
+ }
+ else
+ {
+ lstrcpy(pEnterpriseServer, TEXT(""));
+
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for EnterpriseServer\n"));
+#endif
+ }
+ }
+
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for UseEnterprise\n"));
+#endif
+ }
+ }
+
+ dwSize = sizeof(ReplicationType);
+ Status = RegQueryValueEx(hKey2, TEXT("ReplicationType"), NULL, &dwType, (LPBYTE) &ReplicationType, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ dwSize = sizeof(ReplicationTime);
+ Status = RegQueryValueEx(hKey2, TEXT("ReplicationTime"), NULL, &dwType, (LPBYTE) &ReplicationTime, &dwSize);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ *pReplicationType = ReplicationType;
+ *pReplicationTime = ReplicationTime;
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationTime\n"));
+#endif
+ }
+ }
+
+ }
+ else
+ {
+ if (!ReportedError)
+ {
+ ReportedError = TRUE;
+#ifdef DEBUG
+ dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationType\n"));
+#endif
+ }
+ }
+
+ // LogLevel (REG_DWORD): determines how much info is dumped to the EventLog.
+ // Higher values imply more logging. Default: 0.
+ dwSize = sizeof( LogLevel );
+ Status = RegQueryValueEx( hKey2, TEXT("LogLevel"), NULL, &dwType, (LPBYTE) &LogLevel, &dwSize);
+ if ( ERROR_SUCCESS == Status )
+ *pLogLevel = LogLevel;
+ else
+ *pLogLevel = 0;
+
+ // ProductData (REG_BINARY): an encrypted buffer of concatenated service names
+ // that determine which services need to have secure certificates
+ // for license entry
+ Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, NULL, &dwSize );
+ if ( ERROR_SUCCESS == Status )
+ {
+ TCHAR * NewSecureServiceBuffer = NULL;
+ LPTSTR * NewSecureServiceList = NULL;
+ ULONG NewSecureServiceListSize = 0;
+ ULONG NewSecureServiceBufferSize;
+
+ NewSecureServiceBufferSize = dwSize;
+ NewSecureServiceBuffer = LocalAlloc( LMEM_FIXED, NewSecureServiceBufferSize );
+
+ if ( NULL != NewSecureServiceBuffer )
+ {
+ Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, (LPBYTE) NewSecureServiceBuffer, &dwSize);
+
+ if ( ERROR_SUCCESS == Status )
+ {
+ Status = DeBlock( NewSecureServiceBuffer, dwSize );
+
+ if ( ( STATUS_SUCCESS == Status )
+ && ( ( NULL == SecureServiceBuffer )
+ || ( memcmp( NewSecureServiceBuffer, SecureServiceBuffer, dwSize ) ) ) )
+ {
+ // process changes in secure product list
+ DWORD i;
+ DWORD ProductNdx;
+
+ NewSecureServiceListSize = 0;
+
+ // count number of product names contained in the buffer
+ for ( i=0; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ )
+ {
+ // skip to beginning of next product name
+ for ( ; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ );
+ i++;
+
+ if ( i * sizeof( TCHAR) < dwSize )
+ {
+ // properly null-terminated product name
+ NewSecureServiceListSize++;
+ }
+ }
+
+ if ( 0 != NewSecureServiceListSize )
+ {
+ NewSecureServiceList = LocalAlloc( LMEM_FIXED, sizeof( LPTSTR ) * NewSecureServiceListSize );
+
+ if ( NULL != NewSecureServiceList )
+ {
+ for ( i = ProductNdx = 0; ProductNdx < NewSecureServiceListSize; ProductNdx++ )
+ {
+ NewSecureServiceList[ ProductNdx ] = &NewSecureServiceBuffer[i];
+
+ // skip to beginning of next product name
+ for ( ; NewSecureServiceBuffer[i] != TEXT( '\0' ); i++ );
+ i++;
+ }
+
+ // new secure product list read successfully; use it
+ if ( NULL != SecureServiceBuffer )
+ {
+ LocalFree( SecureServiceBuffer );
+ }
+ if ( NULL != SecureServiceList )
+ {
+ LocalFree( SecureServiceList );
+ }
+
+ SecureServiceBuffer = NewSecureServiceBuffer;
+ SecureServiceList = NewSecureServiceList;
+ SecureServiceListSize = NewSecureServiceListSize;
+ SecureServiceBufferSize = NewSecureServiceBufferSize;
+ }
+ }
+ }
+ }
+ }
+
+ // free buffers if we aren't using them anymore
+ if ( ( NULL != NewSecureServiceList )
+ && ( SecureServiceList != NewSecureServiceList ) )
+ {
+ LocalFree( NewSecureServiceList );
+ }
+
+ if ( ( NULL != NewSecureServiceBuffer )
+ && ( SecureServiceBuffer != NewSecureServiceBuffer ) )
+ {
+ LocalFree( NewSecureServiceBuffer );
+ }
+ }
+
+ RegCloseKey(hKey2);
+ }
+
+} // ConfigInfoRegistryInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FilePrintTableInit(
+ )
+
+/*++
+
+Routine Description:
+
+ Builds up the FilePrint mapping table by enumerating the keys in the
+ registry init'd by the various install programs.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2;
+ static TCHAR RegKeyText[512];
+ static TCHAR KeyText[KEY_NAME_SIZE], ClassText[KEY_NAME_SIZE];
+ LONG Status;
+ DWORD index = 0;
+ DWORD KeySize, ClassSize, NumKeys, NumValue, MaxKey, MaxClass, MaxValue, MaxValueData, MaxSD;
+ FILETIME LastWrite;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: FilePrintTableInit\n"));
+#endif
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\FilePrint"));
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ //
+ // Find out how many sub-keys there are to intialize our table size.
+ // The table can still grow dynamically, this just makes having to
+ // realloc it a rare occurance.
+ //
+ ClassSize = KEY_NAME_SIZE;
+ Status = RegQueryInfoKey(hKey2, ClassText, &ClassSize, NULL,
+ &NumKeys, &MaxKey, &MaxClass, &NumValue,
+ &MaxValue, &MaxValueData, &MaxSD, &LastWrite);
+
+ if (Status == ERROR_SUCCESS) {
+ FilePrintTable = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR) * NumKeys);
+
+ while ((Status == ERROR_SUCCESS) && (FilePrintTable != NULL)) {
+ //
+ // Double check in-case we need to expand the table.
+ //
+ if (index > NumKeys) {
+ NumKeys++;
+ FilePrintTable = (LPTSTR *) LocalReAlloc(FilePrintTable, sizeof(LPTSTR) * NumKeys, LHND);
+ }
+
+ if (FilePrintTable != NULL) {
+ //
+ // Now read in the key name and add it to the table
+ //
+ KeySize = KEY_NAME_SIZE;
+ Status = RegEnumKeyEx(hKey2, index, KeyText, &KeySize, NULL, NULL, NULL, &LastWrite);
+ if (Status == ERROR_SUCCESS) {
+ //
+ // Allocate space in our table and copy the key
+ //
+ FilePrintTable[index] = (LPTSTR) LocalAlloc(LPTR, (KeySize + 1) * sizeof(TCHAR));
+
+ if (FilePrintTable[index] != NULL) {
+ lstrcpy(FilePrintTable[index], KeyText);
+ index++;
+ } else
+ Status = (LONG) GetLastError();
+
+ }
+
+ }
+ }
+ }
+#ifdef DEBUG
+ else {
+ dprintf(TEXT("LLS FilePrintTable Error: 0x%lx\n"), Status);
+ }
+#endif
+
+ RegCloseKey( hKey2 );
+ }
+
+ if (FilePrintTable != NULL)
+ NumFilePrintEntries = index;
+ else
+ NumFilePrintEntries = 0;
+
+} // FilePrintTableInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+RegistryMonitor (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+ Watches for any changes in the Licensing Keys, and if any updates our
+ internal information.
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LONG Status = 0;
+ HKEY hKey2 = NULL;
+ NTSTATUS NtStatus = STATUS_SUCCESS;
+ static TCHAR RegKeyText[KEY_NAME_SIZE];
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services"));
+
+ //
+ // Registry is already open so just find the correct keys.
+ //
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_ALL_ACCESS, &hKey2)) != ERROR_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx failed: 0x%lX\n"), Status);
+#endif
+ return (NTSTATUS) Status;
+ }
+
+ //
+ // Loop forever
+ //
+ for ( ; ; ) {
+
+ Status = RegNotifyChangeKeyValue(hKey2, TRUE, REG_NOTIFY_CHANGE_LAST_SET, LLSRegistryEvent, TRUE);
+ if (Status != ERROR_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS RegNotifyChangeKeyValue Failed: %lu\n"), Status);
+#endif
+ }
+
+ NtStatus = NtWaitForSingleObject( LLSRegistryEvent, TRUE, NULL );
+
+ //
+ // Re-synch the lists
+ //
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+ ConfigInfoRegistryUpdate();
+ LocalServiceListConcurrentLimitSet();
+
+ if (NtStatus != STATUS_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS Registry Event Notification Failed: %lu\n"), NtStatus);
+#endif
+ //
+ // If we failed - sleep for 2 minutes before looping
+ //
+ Sleep(120000L);
+ }
+ }
+
+ return NtStatus;
+
+} // RegistryMonitor
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInit( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+ static TCHAR RegKeyText[512];
+ BOOL ret = FALSE;
+ DWORD Mode, ConcurrentLimit;
+
+ Mode = 0;
+ ConcurrentLimit = 0;
+
+ //
+ // Create a key to tell us about any changes in the registry
+ //
+ Status = NtCreateEvent(
+ &LLSRegistryEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Create the FilePrint table
+ //
+ FilePrintTableInit();
+
+} // RegistryInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryStartMonitor( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HANDLE Thread;
+ DWORD Ignore;
+
+ //
+ // Now dispatch a thread to watch for any registry changes
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) RegistryMonitor,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+
+} // RegistryStartMonitor
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInitValues(
+ LPTSTR ServiceName,
+ BOOL *PerSeatLicensing,
+ ULONG *SessionLimit
+ )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+ Service Name -
+
+ PerSeatLicensing -
+
+ SessionLimit -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ DWORD Mode, ConcurrentLimit;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryInitValues\n"));
+#endif
+
+#ifdef SPECIAL_USER_LIMIT
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = SPECIAL_USER_LIMIT;
+#else // #ifdef SPECIAL_USER_LIMIT
+ Mode = 0;
+ ConcurrentLimit = 0;
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ //
+ // First Get Mode
+ //
+ dwSize = sizeof(Mode);
+ Status = RegQueryValueEx(hKey2, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize);
+
+#if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] Mode: %ld\n"), ServiceName, Mode);
+#endif
+ //
+ // Now Concurrent Limit
+ //
+ dwSize = sizeof(ConcurrentLimit);
+ Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize);
+
+#if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] ConcurrentLimit: %ld\n"), ServiceName, ConcurrentLimit);
+#endif
+ RegCloseKey(hKey2);
+
+ }
+
+
+ if (Mode == 0) {
+ *PerSeatLicensing = TRUE;
+ *SessionLimit = 0;
+ } else {
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = ConcurrentLimit;
+ }
+#endif // #else // #ifdef SPECIAL_USER_LIMIT
+} // RegistryInitValues
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryDisplayNameGet(
+ LPTSTR ServiceName,
+ LPTSTR DefaultName,
+ LPTSTR *pDisplayName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Service Name -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ static TCHAR DisplayName[512];
+ LONG Status;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryDisplayNameGet\n"));
+#endif
+
+ lstrcpy(DisplayName, DefaultName);
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey2, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+# if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] DisplayName: %s\n"), ServiceName, DisplayName);
+# endif
+ RegCloseKey(hKey2);
+
+ }
+
+ *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (*pDisplayName != NULL)
+ lstrcpy(*pDisplayName, DisplayName);
+
+} // RegistryDisplayNameGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryFamilyDisplayNameGet(
+ LPTSTR ServiceName,
+ LPTSTR DefaultName,
+ LPTSTR *pDisplayName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Service Name -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ DWORD dwType, dwSize;
+ static TCHAR RegKeyText[512];
+ static TCHAR DisplayName[MAX_PATH + 1];
+ LONG Status;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryFamilyDisplayNameGet\n"));
+#endif
+
+ lstrcpy(DisplayName, DefaultName);
+
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, ServiceName);
+
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey2, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+# if DBG
+ if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS))
+ dprintf(TEXT("Found Reg-Key for [%s] FamilyDisplayName: %s\n"), ServiceName, DisplayName);
+# endif
+ RegCloseKey(hKey2);
+
+ }
+
+ *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (*pDisplayName != NULL)
+ lstrcpy(*pDisplayName, DisplayName);
+} // RegistryFamilyDisplayNameGet
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+ServiceFindInTable(
+ LPTSTR ServiceName,
+ LPTSTR Table[],
+ ULONG TableSize,
+ ULONG *TableIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Does search of table to find matching service name.
+
+Arguments:
+
+ Service Name -
+
+ Table -
+
+ TableSize -
+
+ TableIndex -
+
+Return Value:
+
+ Pointer to found service or NULL if not found.
+
+--*/
+
+{
+ ULONG i = 0;
+ BOOL Found = FALSE;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceFindInTable\n"));
+#endif
+ while ((i < TableSize) && (!Found)) {
+ Found = !lstrcmpi(ServiceName, Table[i]);
+ i++;
+ }
+
+ if (Found) {
+ i--;
+ *TableIndex = i;
+ return Table[i];
+ } else
+ return NULL;
+
+} // ServiceFindInTable
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RegistryInitService(
+ LPTSTR ServiceName,
+ BOOL *PerSeatLicensing,
+ ULONG *SessionLimit
+ )
+
+/*++
+
+Routine Description:
+
+ Gets init values for a given service from the registry. If not found
+ then just returns default values.
+
+Arguments:
+
+ ServiceName -
+
+ PerSeatLicensing -
+
+ SessionLimit -
+
+Return Value:
+
+--*/
+
+{
+ //
+ // These are the default values
+ //
+ ULONG TableEntry;
+ LPTSTR SvcName = NULL;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: RegistryInitService\n"));
+#endif
+ *PerSeatLicensing = FALSE;
+ *SessionLimit = 0;
+
+ //
+ // Check if it is a file/print service - if so don't worry about rest
+ // of registry entries.
+ //
+ if (ServiceFindInTable(ServiceName, FilePrintTable, NumFilePrintEntries, &TableEntry)) {
+ return;
+ }
+
+ //
+ // Not FilePrint - see if we need to map the name.
+ //
+ SvcName = ServiceFindInTable(ServiceName, NameMappingTable2, NUM_MAPPING_ENTRIES, &TableEntry);
+
+ // if it wasn't found, use original ServiceName
+ if (SvcName == NULL)
+ SvcName = ServiceName;
+
+ RegistryInitValues(SvcName, PerSeatLicensing, SessionLimit);
+
+#if DBG
+ if (TraceFlags & TRACE_REGISTRY)
+ if (*PerSeatLicensing)
+ dprintf(TEXT("LLS - Registry Init: PerSeat: Y Svc: %s\n"), SvcName);
+ else
+ dprintf(TEXT("LLS - Registry Init: PerSeat: N Svc: %s\n"), SvcName);
+#endif
+
+} // RegistryInitService
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&LocalServiceListLock);
+
+ //
+ // Now scan the registry and add all the services
+ //
+ LocalServiceListUpdate();
+} // LocalServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl LocalServiceListCompare(const void *arg1, const void *arg2) {
+ PLOCAL_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg1);
+ Svc2 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // LocalServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PLOCAL_SERVICE_RECORD
+LocalServiceListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on LocalServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found server table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) LocalServiceListSize - 1;
+ LONG cur;
+ int match;
+ PLOCAL_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListFind\n"));
+#endif
+
+ if ((LocalServiceListSize == 0) || (Name == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = LocalServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // LocalServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PLOCAL_SERVICE_RECORD
+LocalServiceListAdd(
+ LPTSTR Name,
+ LPTSTR DisplayName,
+ LPTSTR FamilyDisplayName,
+ DWORD ConcurrentLimit,
+ DWORD FlipAllow,
+ DWORD Mode,
+ DWORD HighMark
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PLOCAL_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0'))) {
+#if DBG
+ dprintf(TEXT("Error LLS: LocalServiceListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Try to find the name
+ //
+ Service = LocalServiceListFind(Name);
+ if (Service != NULL) {
+ Service->ConcurrentLimit = ConcurrentLimit;
+ Service->FlipAllow = FlipAllow;
+ Service->Mode = Mode;
+ return Service;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (LocalServiceList == NULL) {
+ LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLOCAL_SERVICE_RECORD));
+ } else {
+ LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalReAlloc(LocalServiceList, sizeof(PLOCAL_SERVICE_RECORD) * (LocalServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if (LocalServiceList == NULL) {
+ ASSERT(FALSE);
+ LocalServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Service = (PLOCAL_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LOCAL_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ LocalServiceList[LocalServiceListSize] = Service;
+
+ //
+ // Name
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->Name = NewName;
+ lstrcpy(NewName, Name);
+
+ //
+ // DisplayName
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service->Name);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->DisplayName = NewName;
+ lstrcpy(NewName, DisplayName);
+
+ //
+ // FamilyDisplayName
+ //
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyDisplayName) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Service->Name);
+ LocalFree(Service->DisplayName);
+ LocalFree(Service);
+ return NULL;
+ }
+
+ // now copy it over...
+ Service->FamilyDisplayName = NewName;
+ lstrcpy(NewName, FamilyDisplayName);
+
+ //
+ // Initialize other stuff
+ //
+ Service->ConcurrentLimit = ConcurrentLimit;
+ Service->FlipAllow = FlipAllow;
+ Service->Mode = Mode;
+ Service->HighMark = HighMark;
+
+ LocalServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) LocalServiceList, (size_t) LocalServiceListSize, sizeof(PLOCAL_SERVICE_RECORD), LocalServiceListCompare);
+
+ return Service;
+
+} // LocalServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListUpdate( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ HKEY hKey3 = NULL;
+ static TCHAR KeyName[MAX_PATH + 1];
+ static TCHAR DisplayName[MAX_PATH + 1];
+ static TCHAR FamilyDisplayName[MAX_PATH + 1];
+ LONG EnumStatus;
+ LONG Status;
+ DWORD iSubKey = 0;
+ DWORD dwType, dwSize;
+ DWORD ConcurrentLimit, FlipAllow, Mode, HighMark;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListUpdate\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ EnumStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo"), 0, KEY_READ, &hKey2);
+
+ while (EnumStatus == ERROR_SUCCESS) {
+ EnumStatus = RegEnumKey(hKey2, iSubKey, KeyName, MAX_PATH + 1);
+ iSubKey++;
+
+ if (EnumStatus == ERROR_SUCCESS) {
+ if ((Status = RegOpenKeyEx(hKey2, KeyName, 0, KEY_READ, &hKey3)) == ERROR_SUCCESS) {
+ dwSize = sizeof(DisplayName);
+ Status = RegQueryValueEx(hKey3, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize);
+
+ dwSize = sizeof(FamilyDisplayName);
+ if (Status == ERROR_SUCCESS) {
+ Status = RegQueryValueEx(hKey3, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) FamilyDisplayName, &dwSize);
+
+ if (Status != ERROR_SUCCESS) {
+ lstrcpy(FamilyDisplayName, DisplayName);
+ Status = ERROR_SUCCESS;
+ }
+ }
+
+ dwSize = sizeof(Mode);
+ if (Status == ERROR_SUCCESS)
+ Status = RegQueryValueEx(hKey3, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize);
+
+ dwSize = sizeof(FlipAllow);
+ if (Status == ERROR_SUCCESS)
+ Status = RegQueryValueEx(hKey3, TEXT("FlipAllow"), NULL, &dwType, (LPBYTE) &FlipAllow, &dwSize);
+
+ dwSize = sizeof(ConcurrentLimit);
+ if (Status == ERROR_SUCCESS)
+ if (Mode == 0)
+ ConcurrentLimit = 0;
+ else
+ Status = RegQueryValueEx(hKey3, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize);
+
+ dwSize = sizeof(HighMark);
+ if (Status == ERROR_SUCCESS) {
+ Status = RegQueryValueEx(hKey3, TEXT("LocalKey"), NULL, &dwType, (LPBYTE) &HighMark, &dwSize);
+ if (Status != ERROR_SUCCESS) {
+ Status = ERROR_SUCCESS;
+ HighMark = 0;
+ }
+ }
+
+ //
+ // If we read in everything then add to our table
+ //
+ if (Status == ERROR_SUCCESS)
+ LocalServiceListAdd(KeyName, DisplayName, FamilyDisplayName, ConcurrentLimit, FlipAllow, Mode, HighMark);
+
+ RegCloseKey(hKey3);
+ }
+ }
+ }
+
+ RegCloseKey(hKey2);
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListHighMarkSet( )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ static TCHAR RegKeyText[512];
+ LONG Status;
+ ULONG i, j;
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListHighMarkSet\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++) {
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+ j = 0;
+ Service = NULL;
+
+ while ( (j < ServiceListSize) && (Service == NULL) ) {
+ if (!lstrcmpi(LocalServiceList[i]->DisplayName, ServiceList[j]->DisplayName) )
+ Service = ServiceList[j];
+
+ j++;
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+ if (Service != NULL) {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, LocalServiceList[i]->Name);
+
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ Status = RegSetValueEx(hKey2, TEXT("LocalKey"), 0, REG_DWORD, (LPBYTE) &Service->HighMark, sizeof(Service->HighMark));
+ RegCloseKey( hKey2 );
+ }
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListHighMarkSet
+
+
+///////////////////////////////////////////////////////////////////////////////
+VOID
+LocalServiceListConcurrentLimitSet( )
+
+/*++
+
+Routine Description:
+
+ Write concurrent limit to the registry for all secure services.
+
+ Modified from LocalServiceListHighMarkSet() implementation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HKEY hKey2 = NULL;
+ TCHAR RegKeyText[512];
+ LONG Status;
+ ULONG i, j;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServiceListConcurrentLimitSet\n"));
+#endif
+
+ RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++)
+ {
+ //
+ // Create registry key-name we are looking for
+ //
+ lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\"));
+ lstrcat(RegKeyText, LocalServiceList[i]->Name);
+
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2);
+
+ if (Status == ERROR_SUCCESS)
+ {
+ DWORD dwConcurrentLimit;
+ DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit );
+ DWORD dwType;
+
+ // don't write unless we have to (to avoid triggering the registry monitor thread)
+ Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit );
+
+ if ( ServiceIsSecure( LocalServiceList[i]->DisplayName ) )
+ {
+ LocalServiceList[i]->ConcurrentLimit = LocalServiceList[i]->Mode ? ProductLicensesGet( LocalServiceList[i]->DisplayName, TRUE ) : 0;
+
+ // secure product
+ if ( ( ERROR_SUCCESS != Status )
+ || ( REG_DWORD != dwType )
+ || ( dwConcurrentLimit != LocalServiceList[i]->ConcurrentLimit ) )
+ {
+ RegCloseKey( hKey2 );
+ Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2);
+
+ ASSERT( ERROR_SUCCESS == Status );
+ if ( ERROR_SUCCESS == Status )
+ {
+ Status = RegSetValueEx(hKey2, TEXT("ConcurrentLimit"), 0, REG_DWORD, (LPBYTE) &LocalServiceList[i]->ConcurrentLimit, sizeof( LocalServiceList[i]->ConcurrentLimit ) );
+ }
+ }
+ }
+
+ RegCloseKey( hKey2 );
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+} // LocalServiceListConcurrentLimitSet
+
+
+///////////////////////////////////////////////////////////////////////////////
+BOOL ServiceIsSecure( LPTSTR ServiceName )
+
+/*++
+
+Routine Description:
+
+ Determine whether a given service disallows 3.51 Honesty-style
+ license purchases.
+
+Arguments:
+
+ ServiceName (LPTSTR)
+ Service to check.
+
+Return Value:
+
+ TRUE if service requires secure certificate,
+ FALSE if it accepts 3.51 Honesty-style license purchases.
+
+--*/
+
+{
+ BOOL IsSecure = FALSE;
+
+ if ( NULL != SecureServiceList )
+ {
+ DWORD i;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( SecureServiceList[i], ServiceName ) )
+ {
+ IsSecure = TRUE;
+ break;
+ }
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+ }
+
+ return IsSecure;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ServiceSecuritySet( LPTSTR ServiceName )
+
+/*++
+
+Routine Description:
+
+ Add a given service to the secure service list.
+
+Arguments:
+
+ ServiceName (LPTSTR)
+ Service to add.
+
+Return Value:
+
+ STATUS_SUCCESS or Win error or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+ DWORD i;
+ BOOL bChangedValue = FALSE;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( SecureServiceList[i], ServiceName ) )
+ {
+ // product already registered as secure
+ break;
+ }
+ }
+
+ if ( i < SecureServiceListSize )
+ {
+ // product already registered as secure
+ nt = STATUS_SUCCESS;
+ }
+ else
+ {
+ TCHAR * NewSecureServiceBuffer;
+ ULONG NewSecureServiceBufferSize;
+
+ NewSecureServiceBufferSize = ( SecureServiceBufferSize ? SecureServiceBufferSize : sizeof( TCHAR ) ) + sizeof( TCHAR ) * ( 1 + lstrlen( ServiceName ) );
+ NewSecureServiceBuffer = LocalAlloc( LPTR, NewSecureServiceBufferSize );
+
+ if ( NULL == NewSecureServiceBuffer )
+ {
+ nt = STATUS_NO_MEMORY;
+ ASSERT( FALSE );
+ }
+ else
+ {
+ if ( NULL != SecureServiceBuffer )
+ {
+ // copy over current secure service strings
+ memcpy( NewSecureServiceBuffer, SecureServiceBuffer, SecureServiceBufferSize - sizeof( TCHAR ) );
+
+ // add new secure service (don't forget last string is followed by 2 nulls)
+ memcpy( (LPBYTE) NewSecureServiceBuffer + SecureServiceBufferSize - sizeof( TCHAR ), ServiceName, NewSecureServiceBufferSize - SecureServiceBufferSize - sizeof( TCHAR ) );
+ }
+ else
+ {
+ // add new secure service (don't forget last string is followed by 2 nulls)
+ memcpy( NewSecureServiceBuffer, ServiceName, NewSecureServiceBufferSize - sizeof( TCHAR ) );
+ }
+
+ ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - 2 * sizeof( TCHAR ) ) );
+ ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - sizeof( TCHAR ) ) );
+
+ // encrypt buffer
+ nt = EBlock( NewSecureServiceBuffer, NewSecureServiceBufferSize );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ HKEY hKeyParameters;
+
+ // save new list to registry
+ nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"), 0, KEY_WRITE, &hKeyParameters );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = RegSetValueEx( hKeyParameters, TEXT( "ProductData" ), 0, REG_BINARY, (LPBYTE) NewSecureServiceBuffer, NewSecureServiceBufferSize );
+ ASSERT( STATUS_SUCCESS == nt );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ bChangedValue = TRUE;
+ }
+ }
+
+ RegCloseKey( hKeyParameters );
+ }
+
+ LocalFree( NewSecureServiceBuffer );
+ }
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ if ( ( STATUS_SUCCESS == nt ) && bChangedValue )
+ {
+ // key updated, now update internal copy
+ ConfigInfoRegistryUpdate();
+ }
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings )
+
+/*++
+
+Routine Description:
+
+ Pack the secure service list into a contiguous buffer for transmission.
+
+ NOTE: If the routine succeeds, the caller must later MIDL_user_free() the
+ buffer at *ppchProductSecurityStrings.
+
+Arguments:
+
+ pcchProductSecurityStrings (LPDWORD)
+ On return, holds the size (in characters) of the buffer pointed to
+ by *ppchProductSecurityStrings.
+ ppchProductSecurityStrings (WCHAR **)
+ On return, holds the address of the buffer allocated to hold the names
+ of the secure products.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_NO_MEMORY.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ *ppchProductSecurityStrings = MIDL_user_allocate( SecureServiceBufferSize );
+
+ if ( NULL == *ppchProductSecurityStrings )
+ {
+ nt = STATUS_NO_MEMORY;
+ ASSERT( FALSE );
+ }
+ else
+ {
+ memcpy( *ppchProductSecurityStrings, SecureServiceBuffer, SecureServiceBufferSize );
+ *pcchProductSecurityStrings = SecureServiceBufferSize / sizeof( TCHAR );
+
+ nt = STATUS_SUCCESS;
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ return nt;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings )
+
+/*++
+
+Routine Description:
+
+ Unpack a secure service list packed by ProductSecurityPack(). The products
+ contained in the pack are added to the current secure product list.
+
+Arguments:
+
+ cchProductSecurityStrings (DWORD)
+ The size (in characters) of the buffer pointed to by pchProductSecurityStrings.
+ pchProductSecurityStrings (WCHAR *)
+ The address of the buffer allocated to hold the names of the secure products.
+
+Return Value:
+
+ STATUS_SUCCESS.
+
+--*/
+
+{
+ DWORD i;
+
+ for ( i=0;
+ ( i < cchProductSecurityStrings )
+ && ( TEXT('\0') != pchProductSecurityStrings[i] );
+ i += 1 + lstrlen( &pchProductSecurityStrings[i] ) )
+ {
+ ServiceSecuritySet( &pchProductSecurityStrings[i] );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#if DBG
+///////////////////////////////////////////////////////////////////////////////
+void ProductSecurityListDebugDump()
+
+/*++
+
+Routine Description:
+
+ Dump contents of product security list to debug console.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( NULL == SecureServiceList )
+ {
+ dprintf( TEXT( "No secure products.\n" ) );
+ }
+ else
+ {
+ DWORD i;
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+
+ for ( i=0; i < SecureServiceListSize; i++ )
+ {
+ dprintf( TEXT( "(%3ld) %s\n" ), (long)i, SecureServiceList[i] );
+ }
+
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+ }
+}
+#endif
diff --git a/private/net/svcdlls/lls/server/registry.h b/private/net/svcdlls/lls/server/registry.h
new file mode 100644
index 000000000..445bd0730
--- /dev/null
+++ b/private/net/svcdlls/lls/server/registry.h
@@ -0,0 +1,83 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Registry.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added secure service list. This list tracks the products that
+ require "secure" license certificates for all licenses; i.e., the
+ products that do not accept the 3.51 Honesty method of "enter the
+ number of licenses you purchased."
+ o Added routine to update the concurrent limit value in the registry
+ to accurately reflect the connection limit of secure products.
+
+--*/
+
+#ifndef _LLS_REGISTRY_H
+#define _LLS_REGISTRY_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _LOCAL_SERVICE_RECORD {
+ LPTSTR Name;
+ LPTSTR DisplayName;
+ LPTSTR FamilyDisplayName;
+ DWORD ConcurrentLimit;
+ DWORD FlipAllow;
+ DWORD Mode;
+ ULONG HighMark;
+} LOCAL_SERVICE_RECORD, *PLOCAL_SERVICE_RECORD;
+
+extern ULONG LocalServiceListSize;
+extern PLOCAL_SERVICE_RECORD *LocalServiceList;
+extern RTL_RESOURCE LocalServiceListLock;
+
+
+VOID RegistryInit( );
+VOID RegistryStartMonitor( );
+VOID ConfigInfoRegistryInit( DWORD *pUseEnterprise, LPTSTR pEnterpriseServer, DWORD *pReplicationType, DWORD *pReplicationTime, DWORD *pLogLevel );
+VOID RegistryInitValues( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit );
+VOID RegistryDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName );
+VOID RegistryFamilyDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName );
+VOID RegistryInitService( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit );
+LPTSTR ServiceFindInTable( LPTSTR ServiceName, LPTSTR Table[], ULONG TableSize, ULONG *TableIndex );
+
+VOID LocalServiceListInit();
+PLOCAL_SERVICE_RECORD LocalServiceListFind( LPTSTR Name );
+PLOCAL_SERVICE_RECORD LocalServiceListAdd( LPTSTR Name, LPTSTR DisplayName, LPTSTR FamilyDisplayName, DWORD ConcurrentLimit, DWORD FlipAllow, DWORD Mode, DWORD SessLimit );
+VOID LocalServiceListUpdate( );
+VOID LocalServiceListHighMarkSet( );
+VOID LocalServiceListConcurrentLimitSet( );
+
+BOOL ServiceIsSecure( LPTSTR ServiceName );
+NTSTATUS ServiceSecuritySet( LPTSTR ServiceName );
+NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings );
+NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings );
+
+#if DBG
+void ProductSecurityListDebugDump();
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/repl.c b/private/net/svcdlls/lls/server/repl.c
new file mode 100644
index 000000000..725c25174
--- /dev/null
+++ b/private/net/svcdlls/lls/server/repl.c
@@ -0,0 +1,641 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ repl.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added replication of certificate database and secure service list.
+ o Log failure to connect during replication only if the target server
+ is running a build in which license server should be available (i.e.,
+ 1057 (3.51) or greater). If the target server does not support
+ license server, log a message to that effect only once.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "debug.h"
+#include "llsutil.h"
+#include <llsapi.h>
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "server.h"
+#include "repl.h"
+#include "llsrpc_s.h"
+#include "pack.h"
+#include "llsevent.h"
+#include "certdb.h"
+#include "registry.h"
+
+HANDLE ReplicationEvent;
+
+HANDLE LlsRPCHandle = NULL;
+FARPROC pLlsReplConnect;
+FARPROC pLlsReplClose;
+FARPROC pLlsFreeMemory;
+
+FARPROC pLlsReplicationRequestW = NULL;
+FARPROC pLlsReplicationServerAddW = NULL;
+FARPROC pLlsReplicationServerServiceAddW = NULL;
+FARPROC pLlsReplicationServiceAddW = NULL;
+FARPROC pLlsReplicationUserAddW = NULL;
+
+PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL;
+PLLS_REPLICATION_CERT_DB_ADD_W pLlsReplicationCertDbAddW = NULL;
+PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W pLlsReplicationProductSecurityAddW = NULL;
+PLLS_REPLICATION_USER_ADD_EX_W pLlsReplicationUserAddExW = NULL;
+PLLS_CONNECT_W pLlsConnectW = NULL;
+PLLS_CLOSE pLlsClose = NULL;
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ReplicationInit ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ DWORD Ignore;
+ HANDLE Thread;
+ NTSTATUS Status;
+ DWORD Time;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationInit\n"));
+#endif
+
+ //
+ // Open up our RPC DLL and init our function references.
+ //
+ LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ ASSERT(LlsRPCHandle != NULL);
+
+ if (LlsRPCHandle != NULL) {
+ pLlsReplConnect = GetProcAddress(LlsRPCHandle, ("LlsReplConnectW"));
+ pLlsReplClose = GetProcAddress(LlsRPCHandle, ("LlsReplClose"));
+ pLlsFreeMemory = GetProcAddress(LlsRPCHandle, ("LlsFreeMemory"));
+ pLlsReplicationRequestW = GetProcAddress(LlsRPCHandle, ("LlsReplicationRequestW"));
+ pLlsReplicationServerAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerAddW"));
+ pLlsReplicationServerServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerServiceAddW"));
+ pLlsReplicationServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServiceAddW"));
+ pLlsReplicationUserAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddW"));
+ pLlsReplicationCertDbAddW = (PLLS_REPLICATION_CERT_DB_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationCertDbAddW"));
+ pLlsReplicationProductSecurityAddW = (PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationProductSecurityAddW"));
+ pLlsReplicationUserAddExW = (PLLS_REPLICATION_USER_ADD_EX_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddExW"));
+ pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED) GetProcAddress(LlsRPCHandle, ("LlsCapabilityIsSupported"));
+ pLlsConnectW = (PLLS_CONNECT_W) GetProcAddress(LlsRPCHandle, ("LlsConnectW"));
+ pLlsClose = (PLLS_CLOSE) GetProcAddress(LlsRPCHandle, ("LlsClose"));
+
+ ASSERT (pLlsReplConnect != NULL);
+ ASSERT (pLlsReplClose != NULL);
+ ASSERT (pLlsFreeMemory != NULL);
+ ASSERT (pLlsReplicationRequestW != NULL);
+ ASSERT (pLlsReplicationServerAddW != NULL);
+ ASSERT (pLlsReplicationServerServiceAddW != NULL);
+ ASSERT (pLlsReplicationServiceAddW != NULL);
+ ASSERT (pLlsReplicationUserAddW != NULL);
+ ASSERT (pLlsReplicationCertDbAddW != NULL);
+ ASSERT (pLlsReplicationProductSecurityAddW != NULL);
+ ASSERT (pLlsReplicationUserAddExW != NULL);
+ ASSERT (pLlsCapabilityIsSupported != NULL);
+ ASSERT (pLlsConnectW != NULL);
+ ASSERT (pLlsClose != NULL);
+
+ if ((pLlsReplConnect != NULL) && (pLlsReplClose != NULL) &&
+ (pLlsFreeMemory != NULL) && (pLlsReplicationRequestW != NULL) &&
+ (pLlsReplicationServerAddW != NULL) && (pLlsReplicationServiceAddW != NULL) &&
+ (pLlsReplicationServerServiceAddW != NULL) && (pLlsReplicationUserAddW != NULL) &&
+ (pLlsReplicationCertDbAddW != NULL) && (pLlsReplicationProductSecurityAddW != NULL) &&
+ (pLlsReplicationUserAddExW != NULL) && (pLlsCapabilityIsSupported != NULL) &&
+ (pLlsConnectW != NULL) && (pLlsClose != NULL)
+ ) {
+
+ //
+ // Create the Replication Management event
+ //
+ Status = NtCreateEvent(
+ &ReplicationEvent,
+ EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
+ NULL,
+ SynchronizationEvent,
+ FALSE
+ );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Fire off the thread to watch for replication.
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) ReplicationManager,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // ReplicationInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ReplicationDo (
+ LLS_HANDLE LlsHandle,
+ LLS_REPL_HANDLE ReplHandle,
+ DWORD LastReplicated
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PREPL_SERVICE_RECORD Services = NULL;
+ ULONG ServicesTotalRecords = 0;
+ PREPL_SERVER_RECORD Servers = NULL;
+ ULONG ServersTotalRecords = 0;
+ PREPL_SERVER_SERVICE_RECORD ServerServices = NULL;
+ ULONG ServerServicesTotalRecords = 0;
+
+ REPL_CERTIFICATE_DB_0 CertificateDB;
+ REPL_PRODUCT_SECURITY_0 ProductSecurity;
+
+ DWORD UserLevel = 0;
+ REPL_USER_RECORD_CONTAINER UserDB;
+
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationDo\n"));
+#endif
+
+ //
+ // Pack all of our data into linear / self-relative buffers so we
+ // can send them over.
+ //
+ ZeroMemory( &UserDB, sizeof( UserDB ) );
+ ZeroMemory( &CertificateDB, sizeof( CertificateDB ) );
+ ZeroMemory( &ProductSecurity, sizeof( ProductSecurity ) );
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) )
+ {
+ UserLevel = 1;
+ Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 1, &UserDB.Level1.NumUsers, &UserDB.Level1.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+ else
+ {
+ UserLevel = 0;
+ Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 0, &UserDB.Level0.NumUsers, &UserDB.Level0.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) )
+ {
+ Status = CertDbPack( &CertificateDB.StringSize, &CertificateDB.Strings, &CertificateDB.HeaderContainer.Level0.NumHeaders, &CertificateDB.HeaderContainer.Level0.Headers, &CertificateDB.ClaimContainer.Level0.NumClaims, &CertificateDB.ClaimContainer.Level0.Claims );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) )
+ {
+ Status = ProductSecurityPack( &ProductSecurity.StringSize, &ProductSecurity.Strings );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ //
+ // Transmit...
+ //
+
+ Status = (*pLlsReplicationServiceAddW) ( ReplHandle, ServicesTotalRecords, Services );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ Status = (*pLlsReplicationServerAddW) ( ReplHandle, ServersTotalRecords, Servers );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ Status = (*pLlsReplicationServerServiceAddW) ( ReplHandle, ServerServicesTotalRecords, ServerServices );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) )
+ {
+ Status = (*pLlsReplicationUserAddExW)( ReplHandle, UserLevel, &UserDB );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+ else
+ {
+ Status = (*pLlsReplicationUserAddW) ( ReplHandle, UserDB.Level0.NumUsers, UserDB.Level0.Users );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) )
+ {
+ Status = (*pLlsReplicationCertDbAddW)( ReplHandle, 0, &CertificateDB );
+ if (Status != STATUS_SUCCESS)
+ goto ReplicationDoExit;
+ }
+
+ if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) )
+ {
+ Status = (*pLlsReplicationProductSecurityAddW)( ReplHandle, 0, &ProductSecurity );
+ }
+
+ReplicationDoExit:
+ if (Status != STATUS_SUCCESS) {
+#if DBG
+ dprintf(TEXT("LLS Replication ABORT: 0x%lX\n"), Status);
+#endif
+ }
+
+ if (Services != NULL)
+ MIDL_user_free(Services);
+
+ if (Servers != NULL)
+ MIDL_user_free(Servers);
+
+ if ( 0 == UserLevel )
+ {
+ if (UserDB.Level0.Users != NULL)
+ MIDL_user_free(UserDB.Level0.Users);
+ }
+ else
+ {
+ if (UserDB.Level1.Users != NULL)
+ MIDL_user_free(UserDB.Level1.Users);
+ }
+
+ if (CertificateDB.Strings != NULL)
+ MIDL_user_free(CertificateDB.Strings);
+
+ if (CertificateDB.HeaderContainer.Level0.Headers != NULL)
+ MIDL_user_free(CertificateDB.HeaderContainer.Level0.Headers);
+
+ if (CertificateDB.ClaimContainer.Level0.Claims != NULL)
+ MIDL_user_free(CertificateDB.ClaimContainer.Level0.Claims);
+
+ if (ProductSecurity.Strings != NULL)
+ MIDL_user_free(ProductSecurity.Strings);
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT(" LLS Replication Finished\n"));
+#endif
+
+ return Status;
+
+} // ReplicationDo
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ReplicationManager (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ThreadParameter - Not used.
+
+
+Return Value:
+
+ This thread never exits.
+
+--*/
+
+{
+ BOOL DoReplication = FALSE;
+ NTSTATUS Status;
+ LLS_REPL_HANDLE ReplHandle = NULL;
+ LLS_HANDLE LlsHandle = NULL;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+ PREPL_REQUEST pReplInfo;
+ TCHAR ReplicateTo[MAX_COMPUTERNAME_LENGTH + 3];
+ DWORD LastReplicated;
+ LPTSTR pReplicateTo = ReplicateTo;
+ TCHAR LastFailedConnectionDownlevelReplicateTo[MAX_COMPUTERNAME_LENGTH + 3] = TEXT("");
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationManager\n"));
+#endif
+
+ //
+ // Loop forever waiting to be given the opportunity to serve the
+ // greater good.
+ //
+ for ( ; ; ) {
+ //
+ // Wait to be notified that there is work to be done
+ //
+ Status = NtWaitForSingleObject( ReplicationEvent, TRUE, NULL );
+
+ //
+ // So they said, go replicate my son... Yeah, but first we must ask
+ // the master for permission.
+ //
+
+ //
+ // Construct our repl record
+ //
+ pReplInfo = MIDL_user_allocate(sizeof(REPL_REQUEST));
+ ASSERT(pReplInfo != NULL);
+ if (pReplInfo != NULL) {
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ReplicateTo, ConfigInfo.ReplicateTo);
+
+ pReplInfo->EnterpriseServerDate = 0;
+ lstrcpy(pReplInfo->EnterpriseServer, ConfigInfo.EnterpriseServer);
+ pReplInfo->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate;
+
+ pReplInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds;
+ pReplInfo->CurrentTime = LastUsedTime;
+ pReplInfo->NumberServices = 0;
+ pReplInfo->NumberUsers = 0;
+
+ pReplInfo->ReplSize = MAX_REPL_SIZE;
+
+ pReplInfo->Backoff = 0;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+#if DBG
+ if (TraceFlags & TRACE_REPLICATION)
+ dprintf(TEXT("LLS Starting Replication to: %s @ %s\n"), ReplicateTo, TimeToString(pReplInfo->CurrentTime));
+#endif
+
+ Status = (*pLlsReplConnect) ( ReplicateTo, &ReplHandle, 0, (LPBYTE *) &pConnectInfo );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+#if DBG
+ dprintf(TEXT("LLS Error: LlsReplConnect failed: 0x%lX\n"), Status);
+#endif
+ ReplHandle = NULL;
+ }
+ else
+ {
+ Status = (*pLlsConnectW)( ReplicateTo, &LlsHandle );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+#if DBG
+ dprintf(TEXT("LLS Error: LlsConnectW failed: 0x%lX\n"), Status);
+#endif
+ LlsHandle = NULL;
+ }
+ }
+
+ if (Status != STATUS_SUCCESS)
+ {
+ DWORD dwWinError;
+ DWORD dwBuildNumber;
+
+ dwWinError = WinNtBuildNumberGet( ReplicateTo, &dwBuildNumber );
+
+ if ( ( ERROR_SUCCESS == dwWinError ) && ( dwBuildNumber < 1057L ) )
+ {
+ // the ReplicateTo machine does not support the license service
+ if ( lstrcmpi( ReplicateTo, LastFailedConnectionDownlevelReplicateTo ) )
+ {
+ lstrcpy( LastFailedConnectionDownlevelReplicateTo, ReplicateTo );
+
+ LogEvent( LLS_EVENT_REPL_DOWNLEVEL_TARGET, 1, &pReplicateTo, Status );
+ }
+ }
+ else
+ {
+ // the ReplicateTo machine should be running the license service
+ *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
+
+ LogEvent( LLS_EVENT_REPL_NO_CONNECTION, 1, &pReplicateTo, Status );
+ }
+ }
+ else
+ {
+ *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' );
+
+ Status = (*pLlsReplicationRequestW) ( ReplHandle, REPL_VERSION, pReplInfo );
+
+ if (Status != STATUS_SUCCESS)
+ {
+ LogEvent( LLS_EVENT_REPL_REQUEST_FAILED, 1, &pReplicateTo, Status );
+ }
+ else
+ {
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ConfigInfo.EnterpriseServer, pReplInfo->EnterpriseServer);
+ ConfigInfo.EnterpriseServerDate = pReplInfo->EnterpriseServerDate;
+ ConfigInfo.IsReplicating = TRUE;
+ LastReplicated = pReplInfo->LastReplicated;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // And lo, thou may proceed...
+ //
+ if (pReplInfo->Backoff == 0)
+ {
+ if ( ConfigInfo.LogLevel )
+ {
+ LogEvent( LLS_EVENT_REPL_START, 1, &pReplicateTo, ERROR_SUCCESS );
+ }
+
+ Status = ReplicationDo( LlsHandle, ReplHandle, LastReplicated );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ LogEvent( LLS_EVENT_REPL_FAILED, 1, &pReplicateTo, Status );
+ }
+ else if ( ConfigInfo.LogLevel )
+ {
+ LogEvent( LLS_EVENT_REPL_END, 1, &pReplicateTo, ERROR_SUCCESS );
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ //
+ // Need to update when next we should replicate
+ //
+ ConfigInfo.LastReplicatedSeconds = DateSystemGet();
+ GetLocalTime(&ConfigInfo.LastReplicated);
+ ReplicationTimeSet();
+ }
+ else
+ {
+ LogEvent( LLS_EVENT_REPL_BACKOFF, 1, &pReplicateTo, ERROR_SUCCESS );
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ }
+
+ ConfigInfo.IsReplicating = FALSE;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+ }
+ }
+
+ //
+ // Disconnect from Master Server
+ //
+ if ( NULL != LlsHandle )
+ {
+ (*pLlsClose)( LlsHandle );
+ LlsHandle = NULL;
+ }
+
+ if ( NULL != ReplHandle )
+ {
+ Status = (*pLlsReplClose) ( &ReplHandle );
+ try
+ {
+ RpcSmDestroyClientContext( &ReplHandle );
+ }
+ except (TRUE)
+ {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#if DBG
+ dprintf(TEXT("ERROR LLSSRV.EXE (Repl): RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ ReplHandle = NULL;
+ }
+
+ MIDL_user_free( pReplInfo );
+ }
+ }
+
+} // ReplicationManager
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ReplicationTimeSet ( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+--*/
+
+{
+ DWORD CurrTime, ReplTime, Time;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: ReplicationTimeSet\n"));
+#endif
+
+ ReplTime = Time = 0;
+
+ //
+ // Figure out what new time to set it to
+ //
+ if (!ConfigInfo.Replicate)
+ return;
+
+ //
+ // If REPLICATE_DELTA it is easy as we just take the delta and apply it to
+ // the last replication time. Otherwise we have to convert the time from
+ // midnight.
+ //
+
+ //
+ // Figure out how long since we last replicated
+ //
+ ReplTime = ConfigInfo.ReplicationTime;
+
+ if (ConfigInfo.ReplicationType == REPLICATE_DELTA) {
+ Time = DateSystemGet() - ConfigInfo.LastReplicatedSeconds;
+
+ //
+ // If we have already gone past when we should replicate then schedule
+ // one real soon now (10 minutes).
+ //
+ if (Time > ReplTime)
+ Time = 10 * 60;
+ else
+ Time = ReplTime - Time;
+
+ Time += DateLocalGet();
+ } else {
+ //
+ // Need to adjust time to midnight - do this by MOD of seconds
+ // per day.
+ //
+ CurrTime = DateLocalGet();
+ Time = CurrTime - ((CurrTime / (60 * 60 * 24)) * (60 * 60 * 24));
+ CurrTime = CurrTime - Time;
+
+ //
+ // Time = seconds past midnight.
+ // CurrTime = Todays @ 12:00 AM
+ // Figure out if we are past the replication time, if so schedule it
+ // for tomorrow, else today.
+ //
+ if (Time > ReplTime)
+ Time = CurrTime + (60 * 60 * 24) + ReplTime;
+ else
+ Time = CurrTime + ReplTime;
+
+ }
+
+ ConfigInfo.NextReplication = Time;
+
+} // ReplicationTimeSet
diff --git a/private/net/svcdlls/lls/server/repl.h b/private/net/svcdlls/lls/server/repl.h
new file mode 100644
index 000000000..4cfa4f37f
--- /dev/null
+++ b/private/net/svcdlls/lls/server/repl.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Repl.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_REPL_H
+#define _LLS_REPL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// Maximum number of records we will send over to the server at once.
+//
+#define MAX_REPL_SIZE 25
+#define REPL_VERSION 0x0102
+
+extern HANDLE ReplicationEvent;
+
+
+NTSTATUS ReplicationInit();
+VOID ReplicationManager ( IN PVOID ThreadParameter );
+VOID ReplicationTimeSet ( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/rpc.c b/private/net/svcdlls/lls/server/rpc.c
new file mode 100644
index 000000000..dc3d8eb14
--- /dev/null
+++ b/private/net/svcdlls/lls/server/rpc.c
@@ -0,0 +1,6121 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ rpc.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added replication of certificate database and secure service list.
+ o Added Llsr API to support secure certificates.
+ o Added LLS_LICENSE_INFO_1 support to LlsrLicenseEnumW() and
+ LlsrLicenseAddW().
+ o Added LLS_PRODUCT_LICENSE_INFO_1 support to LlsrProductLicenseEnumW().
+ o Added save of all data files after receiving replicated data.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "purchase.h"
+#include "server.h"
+
+#include "llsrpc_s.h"
+#include "lsapi_s.h"
+#include "llsdbg_s.h"
+#include "repl.h"
+#include "pack.h"
+#include "registry.h"
+#include "certdb.h"
+
+
+//
+// BUGBUG: maxpreflength does not currently work for the enum calls as it
+// doesn't take into account the string fields in the records.
+//
+
+
+typedef struct {
+ TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1];
+} CLIENT_CONTEXT_TYPE, *PCLIENT_CONTEXT_TYPE;
+
+typedef struct {
+ TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD ReplicationStart;
+
+ BOOL Active;
+ BOOL Replicated;
+
+ BOOL ServicesSent;
+ ULONG ServiceTableSize;
+ PREPL_SERVICE_RECORD Services;
+
+ BOOL ServersSent;
+ ULONG ServerTableSize;
+ PREPL_SERVER_RECORD Servers;
+
+ BOOL ServerServicesSent;
+ ULONG ServerServiceTableSize;
+ PREPL_SERVER_SERVICE_RECORD ServerServices;
+
+ BOOL UsersSent;
+ ULONG UserLevel;
+ ULONG UserTableSize;
+ LPVOID Users;
+
+ BOOL CertDbSent;
+ ULONG CertDbProductStringSize;
+ WCHAR * CertDbProductStrings;
+ ULONG CertDbNumHeaders;
+ PREPL_CERT_DB_CERTIFICATE_HEADER_0 CertDbHeaders;
+ ULONG CertDbNumClaims;
+ PREPL_CERT_DB_CERTIFICATE_CLAIM_0 CertDbClaims;
+
+ BOOL ProductSecuritySent;
+ ULONG ProductSecurityStringSize;
+ WCHAR * ProductSecurityStrings;
+
+} REPL_CONTEXT_TYPE, *PREPL_CONTEXT_TYPE;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LLSRpcListen (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+
+ Status = RpcServerListen(1, 40, 0);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerListen Failed (0x%lx)\n"), Status);
+#endif
+ }
+
+ return Status;
+
+} // LLSRpcListen
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LLSRpcInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ DWORD Ignore;
+ PSECURITY_DESCRIPTOR pSD;
+
+ //
+ // Initialize a security descriptor.
+ //
+ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC Security Descriptor Alloc Failed\n"));
+#endif
+ ASSERT(FALSE);
+ return;
+ }
+
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC Init Security Descriptor Failed\n"));
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ //
+ // Add a NULL disc. ACL to the security descriptor.
+ //
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)) {
+#if DBG
+ dprintf(TEXT("LLS Error: RPC SetSecurityDescriptorDacl\n"));
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ //
+ // Setup for LPC calls..
+ //
+ Status = RpcServerUseProtseqEp(TEXT("ncalrpc"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_LPC_ENDPOINT), pSD);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerUseProtseq ncalrpc Failed (0x%lx)\n"), Status);
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ // Named pipes as well
+ Status = RpcServerUseProtseqEp(TEXT("ncacn_np"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_NP_ENDPOINT), pSD);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerUseProtseq ncacn_np Failed (0x%lx)\n"), Status);
+#endif
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ ASSERT(FALSE);
+ return;
+ }
+
+ if(pSD != NULL)
+ LocalFree((HLOCAL) pSD);
+
+ // register the interface for the UI RPC's
+ Status = RpcServerRegisterIf(llsrpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+
+ // Now the interface for the Licensing RPC's
+ Status = RpcServerRegisterIf(lsapirpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf2 Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+
+#if DBG
+ //
+ // ... and if DEBUG then the debugging interface
+ //
+ Status = RpcServerRegisterIf(llsdbgrpc_ServerIfHandle, NULL, NULL);
+ if (Status) {
+#if DBG
+ dprintf(TEXT("RpcServerRegisterIf (debug) Failed (0x%lx)\n"), Status);
+#endif
+ return;
+ }
+#endif
+
+ //
+ // Create thread to listen for requests.
+ //
+ CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) LLSRpcListen,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+} // LLSRpcInit
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////
+VOID __RPC_USER LLS_HANDLE_rundown(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LLS_HANDLE_rundown\n"));
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) Handle;
+
+ MIDL_user_free(pClient);
+
+} // LLS_HANDLE_rundown
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrConnect(
+ PLLS_HANDLE Handle,
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsConnect: %s\n"), Name);
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) midl_user_allocate(sizeof(CLIENT_CONTEXT_TYPE));
+
+ if (Name != NULL)
+ lstrcpy(pClient->Name, Name);
+ else
+ lstrcpy(pClient->Name, TEXT(""));
+
+ *Handle = pClient;
+
+ return STATUS_SUCCESS;
+} // LlsrConnect
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrClose(
+ LLS_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CLIENT_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsClose\n"));
+#endif
+
+ pClient = (CLIENT_CONTEXT_TYPE *) Handle;
+
+ return STATUS_SUCCESS;
+} // LlsrClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseEnumW(
+ LLS_HANDLE Handle,
+ PLLS_LICENSE_ENUM_STRUCTW pLicenseInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ DWORD RecordSize;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseEnumW\n"));
+#endif
+
+ Level = pLicenseInfo->Level;
+
+ if ( 0 == Level )
+ {
+ RecordSize = sizeof( LLS_LICENSE_INFO_0W );
+ }
+ else if ( 1 == Level )
+ {
+ RecordSize = sizeof( LLS_LICENSE_INFO_1W );
+ }
+ else
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ //
+ i = *pResumeHandle;
+ while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) )
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ }
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while ( i < PurchaseListSize )
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ TotalEntries++;
+ }
+
+ i++;
+ }
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLicenseEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < PurchaseListSize))
+ {
+ if ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) )
+ {
+ if ( 0 == Level )
+ {
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Product = PurchaseList[i].Service->ServiceName;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ }
+ else
+ {
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Product = ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ ? PurchaseList[i].Service->ServiceName
+ : PurchaseList[i].PerServerService->ServiceName;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Vendor = PurchaseList[i].Vendor;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source;
+ ((PLLS_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate;
+ memcpy( ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) );
+ }
+
+ j++;
+ }
+
+ i++;
+ }
+
+LlsrLicenseEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ if ( 0 == Level )
+ {
+ pLicenseInfo->LlsLicenseInfo.Level0->EntriesRead = EntriesRead;
+ pLicenseInfo->LlsLicenseInfo.Level0->Buffer = (PLLS_LICENSE_INFO_0W) BufPtr;
+ }
+ else
+ {
+ pLicenseInfo->LlsLicenseInfo.Level1->EntriesRead = EntriesRead;
+ pLicenseInfo->LlsLicenseInfo.Level1->Buffer = (PLLS_LICENSE_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+ return Status;
+} // LlsrLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseEnumA(
+ LLS_HANDLE Handle,
+ PLLS_LICENSE_ENUM_STRUCTA LicenseInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LICENSE_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseAddW\n"));
+#endif
+
+ if ( 0 == Level )
+ {
+ if ( ( NULL == BufPtr )
+ || ( NULL == BufPtr->LicenseInfo0.Product )
+ || ( NULL == BufPtr->LicenseInfo0.Admin )
+ || ( NULL == BufPtr->LicenseInfo0.Comment ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ Status = LicenseAdd( BufPtr->LicenseInfo0.Product,
+ TEXT("Microsoft"),
+ BufPtr->LicenseInfo0.Quantity,
+ 0,
+ BufPtr->LicenseInfo0.Admin,
+ BufPtr->LicenseInfo0.Comment,
+ 0,
+ LLS_LICENSE_MODE_ALLOW_PER_SEAT,
+ 0,
+ TEXT("None"),
+ 0,
+ NULL );
+ }
+ }
+ else if ( 1 == Level )
+ {
+ if ( ( NULL == BufPtr )
+ || ( NULL == BufPtr->LicenseInfo1.Product )
+ || ( NULL == BufPtr->LicenseInfo1.Admin )
+ || ( NULL == BufPtr->LicenseInfo1.Comment )
+ || ( 0 == BufPtr->LicenseInfo1.Quantity )
+ || ( 0 == ( BufPtr->LicenseInfo1.AllowedModes
+ & ( LLS_LICENSE_MODE_ALLOW_PER_SERVER
+ | LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ // check to see if this certificate is already maxed out in the enterprise
+ BOOL bIsMaster = TRUE;
+ BOOL bMayInstall = TRUE;
+ HINSTANCE hDll = NULL;
+ PLLS_CONNECT_ENTERPRISE_W pLlsConnectEnterpriseW = NULL;
+ PLLS_CLOSE pLlsClose = NULL;
+ PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL;
+ PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W pLlsCertificateClaimAddCheckW = NULL;
+ PLLS_CERTIFICATE_CLAIM_ADD_W pLlsCertificateClaimAddW = NULL;
+ PLLS_FREE_MEMORY pLlsFreeMemory = NULL;
+ LLS_HANDLE hEnterpriseLls = NULL;
+ TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ];
+
+ RtlEnterCriticalSection( &ConfigInfoLock );
+ bIsMaster = ConfigInfo.IsMaster;
+ lstrcpy( szComputerName, ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection( &ConfigInfoLock );
+
+ if( !bIsMaster && ( 0 != BufPtr->LicenseInfo1.CertificateID ) )
+ {
+ // ask enterprise server if we can install this certfificate
+ hDll = LoadLibraryA( "LLSRPC.DLL" );
+
+ if ( NULL == hDll )
+ {
+ // LLSRPC.DLL should be available!
+ ASSERT( FALSE );
+ }
+ else
+ {
+ pLlsConnectEnterpriseW = (PLLS_CONNECT_ENTERPRISE_W ) GetProcAddress( hDll, "LlsConnectEnterpriseW" );
+ pLlsClose = (PLLS_CLOSE ) GetProcAddress( hDll, "LlsClose" );
+ pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED ) GetProcAddress( hDll, "LlsCapabilityIsSupported" );
+ pLlsCertificateClaimAddCheckW = (PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W ) GetProcAddress( hDll, "LlsCertificateClaimAddCheckW" );
+ pLlsCertificateClaimAddW = (PLLS_CERTIFICATE_CLAIM_ADD_W ) GetProcAddress( hDll, "LlsCertificateClaimAddW" );
+ pLlsFreeMemory = (PLLS_FREE_MEMORY ) GetProcAddress( hDll, "LlsFreeMemory" );
+
+ if ( ( NULL == pLlsConnectEnterpriseW )
+ || ( NULL == pLlsClose )
+ || ( NULL == pLlsCapabilityIsSupported )
+ || ( NULL == pLlsCertificateClaimAddCheckW )
+ || ( NULL == pLlsCertificateClaimAddW )
+ || ( NULL == pLlsFreeMemory ) )
+ {
+ // All of these functions should be exported!
+ ASSERT( FALSE );
+ }
+ else
+ {
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Status = (*pLlsConnectEnterpriseW)( NULL, &hEnterpriseLls, 0, (LPBYTE *)&pConnectInfo );
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ (*pLlsFreeMemory)( pConnectInfo );
+
+ if ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) )
+ {
+ Status = (*pLlsCertificateClaimAddCheckW)( hEnterpriseLls, Level, (LPBYTE) BufPtr, &bMayInstall );
+
+ if ( STATUS_SUCCESS != Status )
+ {
+ bMayInstall = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bMayInstall )
+ {
+ // denied!
+ Status = STATUS_ALREADY_COMMITTED;
+ }
+ else
+ {
+ // approved! (or an error occurred trying to get approval...)
+ Status = LicenseAdd( BufPtr->LicenseInfo1.Product,
+ BufPtr->LicenseInfo1.Vendor,
+ BufPtr->LicenseInfo1.Quantity,
+ BufPtr->LicenseInfo1.MaxQuantity,
+ BufPtr->LicenseInfo1.Admin,
+ BufPtr->LicenseInfo1.Comment,
+ 0,
+ BufPtr->LicenseInfo1.AllowedModes,
+ BufPtr->LicenseInfo1.CertificateID,
+ BufPtr->LicenseInfo1.Source,
+ BufPtr->LicenseInfo1.ExpirationDate,
+ BufPtr->LicenseInfo1.Secrets );
+
+ if ( ( STATUS_SUCCESS == Status )
+ && ( NULL != hEnterpriseLls )
+ && ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ) )
+ {
+ // certificate successfully installed on this machine; register it
+ (*pLlsCertificateClaimAddW)( hEnterpriseLls, szComputerName, Level, (LPBYTE) BufPtr );
+ }
+ }
+
+ if ( NULL != hEnterpriseLls )
+ {
+ (*pLlsClose)( hEnterpriseLls );
+ }
+
+ if ( NULL != hDll )
+ {
+ FreeLibrary( hDll );
+ }
+ }
+ }
+ else
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ Status = LicenseListSave();
+ }
+
+ return Status;
+} // LlsrLicenseAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLicenseAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LICENSE_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLicenseAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLicenseAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductEnumW(
+ LLS_HANDLE Handle,
+ PLLS_PRODUCT_ENUM_STRUCTW pProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductEnumW\n"));
+#endif
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_PRODUCT_INFO_0W);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_PRODUCT_INFO_1W);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < MasterServiceListSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < MasterServiceListSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (MasterServiceListSize - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < MasterServiceListSize)) {
+ if (Level == 0)
+ ((PLLS_PRODUCT_INFO_0) BufPtr)[j].Product = MasterServiceList[i]->Name;
+ else {
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Product = MasterServiceList[i]->Name;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Purchased = MasterServiceList[i]->Licenses;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].InUse = MasterServiceList[i]->LicensesUsed;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].ConcurrentTotal = MasterServiceList[i]->MaxSessionCount;
+ ((PLLS_PRODUCT_INFO_1) BufPtr)[j].HighMark = MasterServiceList[i]->HighMark;
+ }
+
+ i++; j++;
+ }
+
+LlsrProductEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ if (Level == 0) {
+ pProductInfo->LlsProductInfo.Level0->EntriesRead = EntriesRead;
+ pProductInfo->LlsProductInfo.Level0->Buffer = (PLLS_PRODUCT_INFO_0W) BufPtr;
+ } else {
+ pProductInfo->LlsProductInfo.Level1->EntriesRead = EntriesRead;
+ pProductInfo->LlsProductInfo.Level1->Buffer = (PLLS_PRODUCT_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ return Status;
+
+} // LlsrProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductEnumA(
+ LLS_HANDLE Handle,
+ PLLS_PRODUCT_ENUM_STRUCTA ProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ProductFamily,
+ LPWSTR Product,
+ LPWSTR lpVersion
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ PMASTER_SERVICE_RECORD Service;
+ DWORD Version;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductAddW\n"));
+#endif
+
+ if ((ProductFamily == NULL) || (Product == NULL) || (lpVersion == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ Version = VersionToDWORD(lpVersion);
+ Service = MasterServiceListAdd(ProductFamily, Product, Version);
+
+ if (Service == NULL)
+ return STATUS_NO_MEMORY;
+
+ return STATUS_SUCCESS;
+} // LlsrProductAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductAddA(
+ LLS_HANDLE Handle,
+ IN LPSTR ProductFamily,
+ IN LPSTR Product,
+ IN LPSTR Version
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductUserEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ PLLS_PRODUCT_USER_ENUM_STRUCTW pProductUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+ PSVC_RECORD pService;
+ DWORD Flags;
+ ULONG j, AccessCount;
+ DWORD LastAccess;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductUserEnumW\n"));
+#endif
+
+ if (Product == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Need AddEnum lock, but just shared UserListLock (as we just read
+ // the data).
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductUserInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_PRODUCT_USER_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_PRODUCT_USER_INFO_1);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ if (lstrcmpi(Product, BackOfficeStr))
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if ( !(UserRec->Flags & LLS_FLAG_DELETED) ) {
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize );
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+
+ if (pService != NULL) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+ else
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if (UserRec->Mapping != NULL)
+ Flags = UserRec->Mapping->Flags;
+ else
+ Flags = UserRec->Flags;
+
+ if (!(UserRec->Flags & LLS_FLAG_DELETED))
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (UserRec != NULL)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while (UserRec != NULL) {
+ TotalEntries++;
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ if (lstrcmpi(Product, BackOfficeStr))
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize );
+ if (pService != NULL) {
+
+ if (Level == 0)
+ ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ else {
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = pService->Flags;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = pService->LastAccess;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = pService->AccessCount;
+ }
+
+ i++;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+ else
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ if (UserRec->Mapping != NULL)
+ Flags = UserRec->Mapping->Flags;
+ else
+ Flags = UserRec->Flags;
+
+ if (!(UserRec->Flags & LLS_FLAG_DELETED))
+ if (Flags & LLS_FLAG_SUITE_USE) {
+ AccessCount = 0;
+ LastAccess = 0;
+
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ for (j = 0; j < UserRec->ServiceTableSize; j++) {
+ if (UserRec->Services[j].LastAccess > LastAccess)
+ LastAccess = UserRec->Services[j].LastAccess;
+
+ if (UserRec->Services[j].AccessCount > AccessCount)
+ AccessCount = UserRec->Services[j].AccessCount;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ if (Level == 0)
+ ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ else {
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = LastAccess;
+ ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = AccessCount;
+ }
+
+ i++;
+ }
+
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+LlsrProductUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pProductUserInfo->LlsProductUserInfo.Level0->EntriesRead = EntriesRead;
+ pProductUserInfo->LlsProductUserInfo.Level0->Buffer = (PLLS_PRODUCT_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrProductUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_PRODUCT_USER_ENUM_STRUCTA ProductUserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductServerEnumW(
+ LLS_HANDLE Handle,
+ LPTSTR Product,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pProductServerInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j;
+ ULONG RestartKey = 0;
+ PSERVER_SERVICE_RECORD pSvc;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductServerEnumW\n"));
+#endif
+
+ if (Product == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (ULONG) *pResumeHandle;
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pProductServerInfo->Level;
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+ if (Level == 0)
+ RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_1);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrProductServerEnumWExit;
+ }
+
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ for (i = RestartKey; i < ServerListSize; i++) {
+ pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services );
+
+ if (pSvc != NULL) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrProductServerEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ for (i = RestartKey; i < ServerListSize; i++) {
+ pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services );
+
+ if (pSvc != NULL) {
+
+ if (Level == 0)
+ ((PLLS_SERVER_PRODUCT_INFO_0) BufPtr)[j].Name = ServerList[i]->Name;
+ else {
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Name = ServerList[i]->Name;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Flags = pSvc->Flags;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxUses = pSvc->MaxSessionCount;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxSetUses = pSvc->MaxSetSessionCount;
+ ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].HighMark = pSvc->HighMark;
+ }
+
+ j++;
+ }
+
+ }
+
+LlsrProductServerEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pProductServerInfo->LlsServerProductInfo.Level0->EntriesRead = EntriesRead;
+ pProductServerInfo->LlsServerProductInfo.Level0->Buffer = (PLLS_SERVER_PRODUCT_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&ServerListLock);
+ return Status;
+} // LlsrProductServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA ProductServerInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductServerEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductLicenseEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ PLLS_PRODUCT_LICENSE_ENUM_STRUCTW pProductLicenseInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ DWORD RecordSize;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumW\n"));
+#endif
+
+ Level = pProductLicenseInfo->Level;
+
+ if ( 0 == Level )
+ {
+ RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_0W );
+ }
+ else if ( 1 == Level )
+ {
+ RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_1W );
+ }
+ else
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&LicenseListLock, TRUE);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ //
+ i = *pResumeHandle;
+ while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) )
+ {
+ // level 0 enums return only per seat licenses for backwards compatibility
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ }
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while ( i < PurchaseListSize )
+ {
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ TotalEntries++;
+ }
+
+ i++;
+ }
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLicenseEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < PurchaseListSize))
+ {
+ if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT )
+ && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) )
+ || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER )
+ && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) )
+ && ( ( Level > 0 )
+ || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) )
+ {
+ if ( 0 == Level )
+ {
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ }
+ else
+ {
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source;
+ ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate;
+ memcpy( ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) );
+ }
+
+ j++;
+ }
+
+ i++;
+ }
+
+LlsrLicenseEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ if ( 0 == Level )
+ {
+ pProductLicenseInfo->LlsProductLicenseInfo.Level0->EntriesRead = EntriesRead;
+ pProductLicenseInfo->LlsProductLicenseInfo.Level0->Buffer = (PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr;
+ }
+ else
+ {
+ pProductLicenseInfo->LlsProductLicenseInfo.Level1->EntriesRead = EntriesRead;
+ pProductLicenseInfo->LlsProductLicenseInfo.Level1->Buffer = (PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&LicenseListLock);
+ return Status;
+
+} // LlsrProductLicenseEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductLicenseEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ PLLS_PRODUCT_LICENSE_ENUM_STRUCTA ProductLicenseInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductLicenseEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserEnumW(
+ LLS_HANDLE Handle,
+ PLLS_USER_ENUM_STRUCTW pUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ pPrefMaxLen - Supplies the number of bytes of information to return
+ in the buffer. If this value is MAXULONG, all available information
+ will be returned.
+
+ pTotalEntries - Returns the total number of entries available. This value
+ is only valid if the return code is STATUS_SUCCESS or STATUS_MORE_ENTRIES.
+
+ pResumeHandle - Supplies a handle to resume the enumeration from where it
+ left off the last time through. Returns the resume handle if return
+ code is STATUS_MORE_ENTRIES.
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j;
+ PUSER_RECORD UserRec = NULL;
+ PVOID RestartKey = NULL;
+ ULONG StrSize;
+ LPTSTR ProductString = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserEnumW\n"));
+#endif
+
+ //
+ // Need AddEnum lock, but just shared UserListLock (as we just read
+ // the data).
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pUserInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_USER_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_USER_INFO_1);
+ else if (Level == 2)
+ RecSize = sizeof(LLS_USER_INFO_2);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+ BufSize += RecSize;
+ EntriesRead++;
+ }
+
+ // Get next record
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (UserRec != NULL)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ while (UserRec != NULL) {
+ TotalEntries++;
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+ //
+ // Reset Enum to correct place.
+ //
+ RestartKey = (PVOID) *pResumeHandle;
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((i < EntriesRead) && (UserRec != NULL)) {
+ if (!(UserRec->Flags & LLS_FLAG_DELETED)) {
+
+ if (Level == 0)
+ ((PLLS_USER_INFO_0) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+ else if (Level == 1) {
+ ((PLLS_USER_INFO_1) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL)
+ ((PLLS_USER_INFO_1) BufPtr)[i].Group = UserRec->Mapping->Name;
+ else
+ ((PLLS_USER_INFO_1) BufPtr)[i].Group = NULL;
+
+ ((PLLS_USER_INFO_1) BufPtr)[i].Licensed = UserRec->LicensedProducts;
+ ((PLLS_USER_INFO_1) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts;
+
+ ((PLLS_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags;
+ } else {
+ ((PLLS_USER_INFO_2) BufPtr)[i].Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL)
+ ((PLLS_USER_INFO_2) BufPtr)[i].Group = UserRec->Mapping->Name;
+ else
+ ((PLLS_USER_INFO_2) BufPtr)[i].Group = NULL;
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Licensed = UserRec->LicensedProducts;
+ ((PLLS_USER_INFO_2) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts;
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Flags = UserRec->Flags;
+
+ //
+ // Walk product table and build up product string
+ //
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+ StrSize = 0;
+
+ for (j = 0; j < UserRec->ServiceTableSize; j++)
+ StrSize += ((lstrlen(UserRec->Services[j].Service->Name) + 2) * sizeof(TCHAR));
+
+ if (StrSize != 0) {
+ ProductString = MIDL_user_allocate(StrSize);
+ if (ProductString != NULL) {
+ lstrcpy(ProductString, TEXT(""));
+
+ for (j = 0; j < UserRec->ServiceTableSize; j++) {
+ if (j != 0)
+ lstrcat(ProductString, TEXT(", "));
+
+ lstrcat(ProductString, UserRec->Services[j].Service->Name);
+ }
+
+ ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString;
+ }
+ }
+
+ if ((StrSize == 0) || (ProductString == NULL)) {
+ ProductString = MIDL_user_allocate(2 * sizeof(TCHAR));
+ if (ProductString != NULL) {
+ lstrcpy(ProductString, TEXT(""));
+ ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString;
+ }
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ }
+
+ i++;
+ }
+
+ UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey);
+ }
+
+LlsrUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey);
+#endif
+
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) RestartKey;
+ pUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead;
+ pUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListAddEnumLock);
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserEnumA(
+ LLS_HANDLE Handle,
+ PLLS_USER_ENUM_STRUCTA UserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ DWORD Level,
+ PLLS_USER_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFOW pUser = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoGetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((User == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ pUser = MIDL_user_allocate(sizeof(LLS_USER_INFOW));
+ if (pUser != NULL) {
+ pUser->UserInfo1.Name = (LPTSTR) UserRec->UserID;
+
+ if (UserRec->Mapping != NULL) {
+ pUser->UserInfo1.Mapping = UserRec->Mapping->Name;
+ pUser->UserInfo1.Licensed = UserRec->Mapping->Licenses;
+ pUser->UserInfo1.UnLicensed = 0;
+ } else {
+ pUser->UserInfo1.Mapping = NULL;
+ pUser->UserInfo1.Licensed = 1;
+ pUser->UserInfo1.UnLicensed = 0;
+ }
+
+ pUser->UserInfo1.Flags = UserRec->Flags;
+ }
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (pUser == NULL)
+ return STATUS_NO_MEMORY;
+
+ *BufPtr = (PLLS_USER_INFOW) pUser;
+ return STATUS_SUCCESS;
+} // LlsrUserInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ PLLS_USER_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ DWORD Level,
+ PLLS_USER_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFO_1 pUser;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoSetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((User == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ pUser = (PLLS_USER_INFO_1) BufPtr;
+
+ //
+ // If in a mapping can't change SUITE_USE, since it is based on the
+ // License Group
+ //
+ if (UserRec->Mapping != NULL) {
+ RtlReleaseResource(&UserListLock);
+ return STATUS_MEMBER_IN_GROUP;
+ }
+
+ RtlConvertSharedToExclusive(&UserListLock);
+
+ //
+ // Reset SUITE_USE and turn off SUITE_AUTO
+ //
+ pUser->Flags &= LLS_FLAG_SUITE_USE;
+ UserRec->Flags &= ~LLS_FLAG_SUITE_USE;
+ UserRec->Flags |= pUser->Flags;
+ UserRec->Flags &= ~LLS_FLAG_SUITE_AUTO;
+
+ //
+ // Run though and clean up all old licenses
+ //
+ UserLicenseListFree( UserRec );
+
+ //
+ // Now assign new ones
+ //
+ SvcListLicenseUpdate( UserRec );
+
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = LLSDataSave();
+
+ return Status;
+} // LlsrUserInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ DWORD Level,
+ PLLS_USER_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserDeleteW(
+ LLS_HANDLE Handle,
+ LPTSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD UserRec = NULL;
+ PLLS_USER_INFO_1 pUser;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ if (User != NULL)
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteW: %s\n"), User);
+ else
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteW: <NULL>\n"));
+#endif
+
+ if (User == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ UserRec = UserListFind(User);
+
+ if (UserRec != NULL) {
+ RtlConvertSharedToExclusive(&UserListLock);
+
+ UserRec->Flags |= LLS_FLAG_DELETED;
+ UsersDeleted = TRUE;
+ SvcListLicenseFree(UserRec);
+ UserLicenseListFree(UserRec);
+
+ if (UserRec->Services != NULL)
+ LocalFree(UserRec->Services);
+
+ UserRec->Services = NULL;
+ UserRec->ServiceTableSize = 0;
+ }
+
+ RtlReleaseResource(&UserListLock);
+
+ if (UserRec == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = LLSDataSave();
+
+ return Status;
+
+} // LlsrUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR pUser,
+ PLLS_USER_PRODUCT_ENUM_STRUCTW pUserProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ PUSER_RECORD UserRec = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductEnumW\n"));
+#endif
+
+ if ( pUser == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pUserProductInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_USER_PRODUCT_INFO_0);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_USER_PRODUCT_INFO_1);
+ else {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ //
+ // Need to find the user-rec
+ //
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+
+ //
+ // Reset Enum to correct place.
+ //
+ UserRec = UserListFind(pUser);
+ if (UserRec == NULL) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto LlsrUserProductEnumWExit;
+ }
+
+ i = (ULONG) *pResumeHandle;
+ RtlEnterCriticalSection(&UserRec->ServiceTableLock);
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ while ((i < UserRec->ServiceTableSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < UserRec->ServiceTableSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (UserRec->ServiceTableSize - i);
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+ goto LlsrUserProductEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ i = (ULONG) *pResumeHandle;
+ while ((j < EntriesRead) && (i < UserRec->ServiceTableSize)) {
+
+ if (Level == 0)
+ ((PLLS_USER_PRODUCT_INFO_0) BufPtr)[j].Product = UserRec->Services[i].Service->Name;
+ else {
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Product = UserRec->Services[i].Service->Name;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Flags = UserRec->Services[i].Flags;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].LastUsed = UserRec->Services[i].LastAccess;
+ ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].UsageCount = UserRec->Services[i].AccessCount;
+ }
+
+ i++; j++;
+ }
+
+ RtlLeaveCriticalSection(&UserRec->ServiceTableLock);
+
+LlsrUserProductEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+ *pResumeHandle = (ULONG) i;
+ pUserProductInfo->LlsUserProductInfo.Level0->EntriesRead = EntriesRead;
+ pUserProductInfo->LlsUserProductInfo.Level0->Buffer = (PLLS_USER_PRODUCT_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&UserListLock);
+ return Status;
+} // LlsrUserProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ PLLS_USER_PRODUCT_ENUM_STRUCTA UserProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR User,
+ LPWSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductDeleteW\n"));
+#endif
+
+ if ((User == NULL) || (Product == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ Status = SvcListDelete(User, Product);
+ RtlReleaseResource(&UserListLock);
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // save modified data
+ Status = LLSDataSave();
+ }
+
+ return Status;
+} // LlsrUserProductDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrUserProductDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR User,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsUserProductDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrUserProductDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingEnumW(
+ LLS_HANDLE Handle,
+ PLLS_MAPPING_ENUM_STRUCTW pMappingInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ DWORD Level;
+ ULONG RecSize;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingEnumW\n"));
+#endif
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ //
+ // Get size of each record based on info level. Only 0 and 1 supported.
+ //
+ Level = pMappingInfo->Level;
+ if (Level == 0)
+ RecSize = sizeof(LLS_MAPPING_INFO_0W);
+ else if (Level == 1)
+ RecSize = sizeof(LLS_MAPPING_INFO_1W);
+ else {
+ Status = STATUS_INVALID_LEVEL;
+ goto LlsrMappingEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < MappingListSize) && (BufSize < pPrefMaxLen)) {
+ BufSize += RecSize;
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= RecSize;
+ EntriesRead--;
+ }
+
+ if (i < MappingListSize)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (MappingListSize - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrMappingEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ j = 0;
+ while ((j < EntriesRead) && (i < MappingListSize)) {
+ if (Level == 0)
+ ((PLLS_GROUP_INFO_0) BufPtr)[j].Name = MappingList[i]->Name;
+ else {
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Name = MappingList[i]->Name;
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Comment = MappingList[i]->Comment;
+ ((PLLS_GROUP_INFO_1) BufPtr)[j].Licenses = MappingList[i]->Licenses;
+ }
+
+ i++; j++;
+ }
+
+LlsrMappingEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ if (Level == 0) {
+ pMappingInfo->LlsMappingInfo.Level0->EntriesRead = EntriesRead;
+ pMappingInfo->LlsMappingInfo.Level0->Buffer = (PLLS_MAPPING_INFO_0W) BufPtr;
+ } else {
+ pMappingInfo->LlsMappingInfo.Level1->EntriesRead = EntriesRead;
+ pMappingInfo->LlsMappingInfo.Level1->Buffer = (PLLS_MAPPING_INFO_1W) BufPtr;
+ }
+
+ RtlReleaseResource(&MappingListLock);
+ return Status;
+
+} // LlsrMappingEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingEnumA(
+ LLS_HANDLE Handle,
+ PLLS_MAPPING_ENUM_STRUCTA MappingInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PMAPPING_RECORD pMapping = NULL;
+ PLLS_GROUP_INFO_1 pMap = NULL;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoGetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((Mapping == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+
+ if (pMapping != NULL) {
+ pMap = MIDL_user_allocate(sizeof(LLS_GROUP_INFO_1));
+ if (pMap != NULL) {
+ pMap->Name = pMapping->Name;
+ pMap->Comment = pMapping->Comment;
+ pMap->Licenses = pMapping->Licenses;
+ }
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (pMapping == NULL)
+ return STATUS_OBJECT_NAME_NOT_FOUND;
+
+ if (pMap == NULL)
+ return STATUS_NO_MEMORY;
+
+ *BufPtr = (PLLS_MAPPING_INFOW) pMap;
+ return STATUS_SUCCESS;
+
+} // LlsrMappingInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PMAPPING_RECORD pMapping = NULL;
+ PLLS_GROUP_INFO_1 pMap;
+ LPTSTR NewComment;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoSetW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((Mapping == NULL) || (BufPtr == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+
+ if (pMapping != NULL) {
+ pMap = (PLLS_GROUP_INFO_1) BufPtr;
+
+ //
+ // Check if comment has changed
+ //
+ if (pMap->Comment != NULL)
+ if (lstrcmp(pMap->Comment, pMapping->Comment)) {
+ NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(pMap->Comment) + 1) * sizeof(TCHAR));
+ if (NewComment != NULL) {
+ LocalFree(pMapping->Comment);
+ pMapping->Comment = NewComment;
+ lstrcpy(pMapping->Comment, pMap->Comment);
+ }
+ }
+
+ if ( pMapping->Licenses != pMap->Licenses )
+ {
+ MappingLicenseListFree( pMapping );
+ pMapping->Licenses = pMap->Licenses;
+ MappingLicenseUpdate( pMapping, TRUE );
+ }
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (pMapping == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else
+ Status = MappingListSave();
+
+ return Status;
+
+} // LlsrMappingInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ DWORD Level,
+ PLLS_MAPPING_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ PLLS_USER_ENUM_STRUCTW pMappingUserInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PMAPPING_RECORD pMapping;
+ DWORD Level;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserEnumW\n"));
+#endif
+
+ Level = pMappingUserInfo->Level;
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&MappingListLock, TRUE);
+
+ pMapping = MappingListFind(Mapping);
+ if (pMapping == NULL) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ goto LlsrMappingUserEnumWExit;
+ }
+
+ //
+ // Calculate how many records will fit into PrefMaxLen buffer. This is
+ // the record size * # records + space for the string data. If MAX_ULONG
+ // is passed in then we return all records.
+ //
+ i = *pResumeHandle;
+ while ((i < pMapping->NumMembers) && (BufSize < pPrefMaxLen)) {
+ BufSize += sizeof(LLS_USER_INFO_0);
+ EntriesRead++;
+
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ //
+ // If we overflowed the buffer then back up one record.
+ //
+ if (BufSize > pPrefMaxLen) {
+ BufSize -= sizeof(LLS_USER_INFO_0);
+ EntriesRead--;
+ }
+
+ if (i < pMapping->NumMembers)
+ Status = STATUS_MORE_ENTRIES;
+
+ //
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ //
+ TotalEntries += (pMapping->NumMembers - i);
+
+ //
+ // Reset Enum to correct place.
+ //
+ i = *pResumeHandle;
+
+ //
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ //
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL) {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrMappingUserEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ //
+ // Buffers are all setup, so loop through records and copy the data.
+ //
+ while ((j < EntriesRead) && (i < pMapping->NumMembers)) {
+ ((PLLS_USER_INFO_0) BufPtr)[j].Name = pMapping->Members[i];
+ i++; j++;
+ }
+
+LlsrMappingUserEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+ pMappingUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead;
+ pMappingUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&MappingListLock);
+ return Status;
+
+} // LlsrMappingUserEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ PLLS_USER_ENUM_STRUCTA MappingUserInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserAddW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUSER_RECORD pUserRec;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserAddW\n"));
+#endif
+
+ if ((Mapping == NULL) || (User == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ pMap = MappingUserListAdd( Mapping, User );
+
+ if (pMap == NULL)
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ else {
+ RtlAcquireResourceShared(&UserListLock, TRUE);
+ pUserRec = UserListFind(User);
+
+ if (pUserRec != NULL)
+ UserMappingAdd(pMap, pUserRec);
+
+ RtlReleaseResource(&UserListLock);
+ }
+
+ RtlReleaseResource(&MappingListLock);
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = MappingListSave();
+
+ if (Status == STATUS_SUCCESS)
+ Status = LLSDataSave();
+ }
+
+ return Status;
+
+} // LlsrMappingUserAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserAddA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping,
+ LPWSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ PUSER_RECORD pUser;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteW\n"));
+#endif
+
+ if ((Mapping == NULL) || (User == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ Status = MappingUserListDelete(Mapping, User);
+ RtlReleaseResource(&MappingListLock);
+
+ pUser = UserListFind( User );
+
+ if (pUser != NULL) {
+ //
+ // if auto switch to BackOffice then turn BackOffice off
+ //
+ if (pUser->Flags & LLS_FLAG_SUITE_AUTO)
+ pUser->Flags &= ~ LLS_FLAG_SUITE_USE;
+
+ //
+ // Free up any licenses used by the user
+ //
+ SvcListLicenseFree( pUser );
+ pUser->Mapping = NULL;
+
+ //
+ // And claim any needed new-ones
+ //
+ SvcListLicenseUpdate( pUser );
+ }
+
+ if (Status == STATUS_SUCCESS)
+ {
+ Status = MappingListSave();
+
+ if (Status == STATUS_SUCCESS)
+ Status = LLSDataSave();
+ }
+
+ return Status;
+} // LlsrMappingUserDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingUserDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping,
+ LPSTR User
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingUserDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_MAPPING_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PMAPPING_RECORD pMap;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingAddW\n"));
+#endif
+
+ if (Level != 1)
+ return STATUS_INVALID_LEVEL;
+
+ if ((BufPtr == NULL) ||
+ (BufPtr->MappingInfo1.Name == NULL) ||
+ (BufPtr->MappingInfo1.Comment == NULL))
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+
+ pMap = MappingListAdd(BufPtr->MappingInfo1.Name,
+ BufPtr->MappingInfo1.Comment,
+ BufPtr->MappingInfo1.Licenses);
+
+ RtlReleaseResource(&MappingListLock);
+ if (pMap == NULL)
+ Status = STATUS_NO_MEMORY;
+
+ if (Status == STATUS_SUCCESS)
+ Status = MappingListSave();
+
+ return Status;
+} // LlsrMappingAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_MAPPING_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingDeleteW(
+ LLS_HANDLE Handle,
+ LPWSTR Mapping
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingDeleteW\n"));
+#endif
+
+ if (Mapping == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ RtlAcquireResourceExclusive(&MappingListLock, TRUE);
+ Status = MappingListDelete(Mapping);
+ RtlReleaseResource(&MappingListLock);
+
+ if (Status == STATUS_SUCCESS)
+ Status = MappingListSave();
+
+ return Status;
+} // LlsrMappingDeleteW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrMappingDeleteA(
+ LLS_HANDLE Handle,
+ LPSTR Mapping
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsMappingDeleteA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrMappingDeleteA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ PLLS_SERVER_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ PLLS_SERVER_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerProductEnumW(
+ LLS_HANDLE Handle,
+ LPWSTR Server,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerProductEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServerProductEnumA(
+ LLS_HANDLE Handle,
+ LPSTR Server,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServerProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServerProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductEnumW(
+ LLS_HANDLE Handle,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo,
+ DWORD pPrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductEnumW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductEnumA(
+ LLS_HANDLE Handle,
+ PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrLocalProductInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ DWORD Level,
+ PLLS_SERVER_PRODUCT_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrLocalProductInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoGetW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOW *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLLS_SERVICE_INFO_0 pInfo;
+ FILETIME ftTimeStartedLocal;
+ LARGE_INTEGER llTimeStartedLocal;
+ LARGE_INTEGER llTimeStartedSystem;
+
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoGetW\n"));
+#endif
+
+ if (Level != 0)
+ return STATUS_INVALID_LEVEL;
+
+ if (BufPtr == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ pInfo = (PLLS_SERVICE_INFO_0) MIDL_user_allocate(sizeof(LLS_SERVICE_INFO_0));
+ if (pInfo == NULL)
+ return STATUS_NO_MEMORY;
+
+ // make sure the config info is up-to-date
+ ConfigInfoRegistryUpdate();
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+
+ pInfo->Version = ConfigInfo.Version;
+ pInfo->Mode = LLS_MODE_ENTERPRISE_SERVER;
+ pInfo->ReplicateTo = ConfigInfo.ReplicateTo;
+ pInfo->EnterpriseServer = ConfigInfo.EnterpriseServer;
+ pInfo->ReplicationType = ConfigInfo.ReplicationType;
+ pInfo->ReplicationTime = ConfigInfo.ReplicationTime;
+ pInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds;
+ pInfo->UseEnterprise = ConfigInfo.UseEnterprise;
+
+ SystemTimeToFileTime( &ConfigInfo.Started, &ftTimeStartedLocal );
+
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ // convert time started (a local SYSTEMTIME) to a system time DWORD in seconds since 1980
+ llTimeStartedLocal.u.LowPart = ftTimeStartedLocal.dwLowDateTime;
+ llTimeStartedLocal.u.HighPart = ftTimeStartedLocal.dwHighDateTime;
+
+ RtlLocalTimeToSystemTime( &llTimeStartedLocal, &llTimeStartedSystem );
+ RtlTimeToSecondsSince1980( &llTimeStartedSystem, &pInfo->TimeStarted );
+
+ *BufPtr = (PLLS_SERVICE_INFOW) pInfo;
+
+ return STATUS_SUCCESS;
+
+} // LlsrServiceInfoGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoGetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOA *BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoSetW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOW BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoSetW\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoSetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrServiceInfoSetA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_SERVICE_INFOA BufPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsServiceInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrServiceInfoSetA
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Replication Functions
+
+/////////////////////////////////////////////////////////////////////////
+VOID __RPC_USER LLS_REPL_HANDLE_rundown(
+ LLS_REPL_HANDLE Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ REPL_CONTEXT_TYPE *pClient;
+ LLS_REPL_HANDLE xHandle;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LLS_REPL_HANDLE_rundown\n"));
+#endif
+
+ if (Handle == NULL)
+ return;
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ if (pClient != NULL)
+ if (pClient->Active) {
+ xHandle = Handle;
+ LlsrReplClose(&xHandle);
+ }
+
+ MIDL_user_free(pClient);
+
+} // LLS_REPL_HANDLE_rundown
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrReplConnect(
+ PLLS_REPL_HANDLE Handle,
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplConnect: %s\n"), Name);
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) midl_user_allocate(sizeof(REPL_CONTEXT_TYPE));
+ ASSERT(pClient != NULL);
+
+ if (Name != NULL)
+ lstrcpy(pClient->Name, Name);
+ else
+ lstrcpy(pClient->Name, TEXT(""));
+
+ pClient->Active = TRUE;
+ pClient->Replicated = FALSE;
+
+ pClient->ServicesSent = FALSE;
+ pClient->ServiceTableSize = 0;
+ pClient->Services = NULL;
+
+ pClient->ServersSent = FALSE;
+ pClient->ServerTableSize = 0;
+ pClient->Servers = NULL;
+
+ pClient->ServerServicesSent = FALSE;
+ pClient->ServerServiceTableSize = 0;
+ pClient->ServerServices = NULL;
+
+ pClient->UsersSent = FALSE;
+ pClient->UserLevel = 0;
+ pClient->UserTableSize = 0;
+ pClient->Users = NULL;
+
+ pClient->CertDbSent = FALSE;
+ pClient->CertDbProductStringSize = 0;
+ pClient->CertDbProductStrings = NULL;
+ pClient->CertDbNumHeaders = 0;
+ pClient->CertDbHeaders = NULL;
+ pClient->CertDbNumClaims = 0;
+ pClient->CertDbClaims = NULL;
+
+ pClient->ProductSecuritySent = FALSE;
+ pClient->ProductSecurityStringSize = 0;
+ pClient->ProductSecurityStrings = NULL;
+
+ *Handle = pClient;
+
+ return STATUS_SUCCESS;
+} // LlsrReplConnect
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrReplClose(
+ LLS_REPL_HANDLE *pHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOL Replicated = TRUE;
+ LLS_REPL_HANDLE Handle = NULL;
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+ REPL_CONTEXT_TYPE *pClient;
+ PSERVER_RECORD Server;
+ ULONG i;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplClose\n"));
+#endif
+
+ ASSERT (pHandle != NULL);
+
+ Handle = *pHandle;
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+ pClient->Active = FALSE;
+
+ //
+ // Check to see if we have all the information from the client - if we do
+ // then munge this information into our internal tables.
+ //
+ if (pClient->ServersSent && pClient->UsersSent && pClient->ServicesSent && pClient->ServerServicesSent) {
+#if DBG
+ if (TraceFlags & TRACE_RPC)
+ dprintf(TEXT("LLS Replication - Munging Data\n"));
+#endif
+
+ UnpackAll (
+ pClient->ServiceTableSize,
+ pClient->Services,
+ pClient->ServerTableSize,
+ pClient->Servers,
+ pClient->ServerServiceTableSize,
+ pClient->ServerServices,
+ pClient->UserLevel,
+ pClient->UserTableSize,
+ pClient->Users
+ );
+
+ if ( pClient->CertDbSent )
+ {
+ CertDbUnpack(
+ pClient->CertDbProductStringSize,
+ pClient->CertDbProductStrings,
+ pClient->CertDbNumHeaders,
+ pClient->CertDbHeaders,
+ pClient->CertDbNumClaims,
+ pClient->CertDbClaims,
+ TRUE );
+ }
+
+ if ( pClient->ProductSecuritySent )
+ {
+ ProductSecurityUnpack(
+ pClient->ProductSecurityStringSize,
+ pClient->ProductSecurityStrings );
+ }
+ } else
+ Replicated = FALSE;
+
+ //////////////////////////////////////////////////////////////////
+ //
+ // Replication Finished - clean up the context data.
+ //
+#if DBG
+ if (TraceFlags & TRACE_RPC)
+ dprintf(TEXT("LLS Replication - Munging Finished\n"));
+#endif
+
+ if (pClient->Servers != NULL) {
+ for (i = 0; i < pClient->ServerTableSize; i++)
+ MIDL_user_free(pClient->Servers[i].Name);
+
+ MIDL_user_free(pClient->Servers);
+ }
+
+ if (pClient->Services != NULL) {
+ for (i = 0; i < pClient->ServiceTableSize; i++) {
+ MIDL_user_free(pClient->Services[i].Name);
+ MIDL_user_free(pClient->Services[i].FamilyName);
+ }
+
+ MIDL_user_free(pClient->Services);
+ }
+
+ if (pClient->ServerServices != NULL)
+ MIDL_user_free(pClient->ServerServices);
+
+ if (pClient->Users != NULL) {
+ for (i = 0; i < pClient->UserTableSize; i++)
+ {
+ if ( 0 == pClient->UserLevel )
+ {
+ MIDL_user_free( ((PREPL_USER_RECORD_0) (pClient->Users))[i].Name );
+ }
+ else
+ {
+ MIDL_user_free( ((PREPL_USER_RECORD_1) (pClient->Users))[i].Name );
+ }
+ }
+
+ MIDL_user_free(pClient->Users);
+ }
+
+ if (pClient->CertDbProductStrings != NULL)
+ {
+ MIDL_user_free(pClient->CertDbProductStrings);
+ }
+
+ if (pClient->CertDbHeaders != NULL)
+ {
+ MIDL_user_free(pClient->CertDbHeaders);
+ }
+
+ if (pClient->CertDbClaims != NULL)
+ {
+ MIDL_user_free(pClient->CertDbClaims);
+ }
+
+ if (pClient->ProductSecurityStrings != NULL)
+ {
+ MIDL_user_free(pClient->ProductSecurityStrings);
+ }
+
+ if (pClient->Replicated) {
+ if (Replicated) {
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+ Server = ServerListFind(pClient->Name);
+ RtlReleaseResource(&ServerListLock);
+
+ ASSERT(Server != NULL);
+ if (Server != NULL)
+ Server->LastReplicated = pClient->ReplicationStart;
+ }
+
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ i = --ConfigInfo.NumReplicating;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ if ( !i )
+ {
+ // no one's replicating; save all our data files
+ SaveAll();
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrReplClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationRequestW(
+ LLS_HANDLE Handle,
+ DWORD Version,
+ PREPL_REQUEST pRequest
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+ REPL_CONTEXT_TYPE *pClient;
+ PSERVER_RECORD Server;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC | TRACE_REPLICATION))
+ dprintf(TEXT("LLS TRACE: LlsReplicationRequestW: %s\n"), ((PCLIENT_CONTEXT_TYPE) Handle)->Name);
+#endif
+
+ if (Version != REPL_VERSION) {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ if (pRequest == NULL)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // Check Enterprise server date from client to see if we need to update
+ // ours. Also, send back new one for the client.
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ lstrcpy(ComputerName, ConfigInfo.ComputerName);
+
+ if (ConfigInfo.EnterpriseServerDate < pRequest->EnterpriseServerDate) {
+ if (lstrlen(pRequest->EnterpriseServer) != 0) {
+ lstrcpy(ConfigInfo.EnterpriseServer, pRequest->EnterpriseServer);
+ ConfigInfo.EnterpriseServerDate = pRequest->EnterpriseServerDate;
+ }
+ }
+
+ lstrcpy(pRequest->EnterpriseServer, ConfigInfo.EnterpriseServer);
+ pRequest->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate;
+
+ //
+ // Increment Repl Count
+ //
+ ConfigInfo.NumReplicating++;
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // Find this server in our server list (add it if not there)
+ //
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+ pClient->Replicated = TRUE;
+ RtlAcquireResourceExclusive(&ServerListLock, TRUE);
+ Server = ServerListAdd(pClient->Name, ComputerName);
+ RtlReleaseResource(&ServerListLock);
+
+ if (Server == NULL) {
+ ASSERT(FALSE);
+ return STATUS_NO_MEMORY;
+ }
+
+ pClient->ReplicationStart = pRequest->CurrentTime;
+ pRequest->LastReplicated = Server->LastReplicated;
+ return STATUS_SUCCESS;
+} // LlsrReplicationRequestW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServerAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVER_RECORD Servers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServerAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServersSent = TRUE;
+ pClient->ServerTableSize = NumRecords;
+ pClient->Servers = Servers;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServerAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServerServiceAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVER_SERVICE_RECORD ServerServices
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServerServiceAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServerServicesSent = TRUE;
+ pClient->ServerServiceTableSize = NumRecords;
+ pClient->ServerServices = ServerServices;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServerServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationServiceAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_SERVICE_RECORD Services
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationServiceAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ServicesSent = TRUE;
+ pClient->ServiceTableSize = NumRecords;
+ pClient->Services = Services;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationServiceAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationUserAddW(
+ LLS_HANDLE Handle,
+ ULONG NumRecords,
+ PREPL_USER_RECORD_0 Users
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ REPL_CONTEXT_TYPE *pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsReplicationUserAddW\n"));
+#endif
+
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->UsersSent = TRUE;
+ pClient->UserLevel = 0;
+ pClient->UserTableSize = NumRecords;
+ pClient->Users = Users;
+
+ return STATUS_SUCCESS;
+} // LlsrReplicationUserAddW
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Licensing Functions
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrLicenseRequestW(
+ LPDWORD LicenseHandle,
+ LPWSTR ProductID,
+ ULONG VersionIndex,
+ BOOLEAN IsAdmin,
+ ULONG DataType,
+ ULONG DataSize,
+ PBYTE Data
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Handle = 0xFFFFFFFFL;
+
+#if DBG
+ if ( TraceFlags & (TRACE_FUNCTION_TRACE) )
+ dprintf(TEXT("LLS TRACE: LlsrLicenseRequestW\n"));
+#endif
+
+ Status = DispatchRequestLicense(DataType, Data, ProductID, VersionIndex, IsAdmin, &Handle);
+ *LicenseHandle = Handle;
+
+ return Status;
+} // LlsrLicenseRequestW
+
+
+NTSTATUS
+LlsrLicenseFree(
+ DWORD LicenseHandle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+#if DBG
+ if ( TraceFlags & (TRACE_FUNCTION_TRACE) )
+ dprintf(TEXT("LLS TRACE: LlsrLicenseFree\n"));
+#endif
+
+
+ DispatchFreeLicense(LicenseHandle);
+ return STATUS_SUCCESS;
+} // LlsrLicenseFree
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Debugging API's
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableDump(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ //
+ // FreeHandle is actually TableID
+ //
+ switch(Table) {
+ case SERVICE_TABLE_NUM:
+ ServiceListDebugDump();
+ break;
+
+ case USER_TABLE_NUM:
+ UserListDebugDump();
+ break;
+
+ case SID_TABLE_NUM:
+ SidListDebugDump();
+ break;
+
+ case LICENSE_TABLE_NUM:
+ LicenseListDebugDump();
+ break;
+
+ case ADD_CACHE_TABLE_NUM:
+ AddCacheDebugDump();
+ break;
+
+ case MASTER_SERVICE_TABLE_NUM:
+ MasterServiceListDebugDump();
+ break;
+
+ case SERVICE_FAMILY_TABLE_NUM:
+ MasterServiceRootDebugDump();
+ break;
+
+ case MAPPING_TABLE_NUM:
+ MappingListDebugDump();
+ break;
+
+ case SERVER_TABLE_NUM:
+ ServerListDebugDump();
+ break;
+
+ case SECURE_PRODUCT_TABLE_NUM:
+ ProductSecurityListDebugDump();
+ break;
+
+ case CERTIFICATE_TABLE_NUM:
+ CertDbDebugDump();
+ break;
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrDbgTableDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableInfoDump(
+ DWORD Table,
+ LPTSTR Item
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ switch(Table) {
+ case SERVICE_TABLE_NUM:
+ ServiceListDebugInfoDump((PVOID) Item);
+ break;
+
+ case USER_TABLE_NUM:
+ UserListDebugInfoDump((PVOID) Item);
+ break;
+
+// case SID_TABLE_NUM:
+// SidListDebugInfoDump((PVOID) Item);
+// break;
+
+// case LICENSE_TABLE_NUM:
+// LicenseListInfoDebugDump((PVOID) Item);
+// break;
+
+// case ADD_CACHE_TABLE_NUM:
+// AddCacheDebugDump((PVOID) Item);
+// break;
+
+ case MASTER_SERVICE_TABLE_NUM:
+ MasterServiceListDebugInfoDump((PVOID) Item);
+ break;
+
+ case SERVICE_FAMILY_TABLE_NUM:
+ MasterServiceRootDebugInfoDump((PVOID) Item);
+ break;
+
+ case MAPPING_TABLE_NUM:
+ MappingListDebugInfoDump((PVOID) Item);
+ break;
+
+ case SERVER_TABLE_NUM:
+ ServerListDebugInfoDump((PVOID) Item);
+ break;
+ }
+
+ return STATUS_SUCCESS;
+} // LlsrDbgTableInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTableFlush(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ return STATUS_SUCCESS;
+} // LlsrDbgTableFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgTraceSet(
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ TraceFlags = Flags;
+ return STATUS_SUCCESS;
+} // LlsrDbgTraceSet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgConfigDump(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ ConfigInfoDebugDump();
+ return STATUS_SUCCESS;
+} // LlsrDbgConfigDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgReplicationForce(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ NtSetEvent( ReplicationEvent, NULL );
+ return STATUS_SUCCESS;
+} // LlsrDbgReplicationForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgReplicationDeny(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ return STATUS_SUCCESS;
+} // LlsrDbgReplicationDeny
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgRegistryUpdateForce(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ ServiceListResynch();
+ return STATUS_SUCCESS;
+} // LlsrDbgRegistryUpdateForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrDbgDatabaseFlush(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ LLSDataSave();
+ return STATUS_SUCCESS;
+} // LlsrDbgDatabaseFlush
+
+
+
+#endif // #if DBG
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+// Extended RPC
+
+NTSTATUS LlsrProductSecurityGetA(
+ LLS_HANDLE Handle,
+ LPSTR Product,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ NOTE: Not yet implemented. Use LlsrProductSecurityGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductSecurityGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecurityGetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName,
+ LPBOOL pIsSecure
+ )
+
+/*++
+
+Routine Description:
+
+ Retrieve the "security" of a product. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to receive the
+ security.
+ pIsSecure (LPBOOL)
+ On return, and if successful, indicates whether the product is
+ secure.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ DWORD i;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetW\n"));
+#endif
+
+ RtlAcquireResourceShared( &LocalServiceListLock, TRUE );
+
+ *pIsSecure = ServiceIsSecure( DisplayName );
+
+ RtlReleaseResource( &LocalServiceListLock );
+
+ return STATUS_SUCCESS;
+} // LlsrProductSecurityGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecuritySetA(
+ LLS_HANDLE Handle,
+ LPSTR Product
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+ NOTE: Not yet implemented. Use LlsrProductSecuritySetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrProductSecuritySetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsrProductSecuritySetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName
+ )
+
+/*++
+
+Routine Description:
+
+ Flags the given product as secure. A product is deemed secure iff
+ it requires a secure certificate. In such a case, licenses for the
+ product may not be entered via the Honesty ("enter the number of
+ licenses you purchased") method.
+
+ This designation is not reversible and is propagated up the
+ replication tree.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product ("DisplayName") for which to activate
+ security.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetW\n"));
+#endif
+
+ nt = ServiceSecuritySet( DisplayName );
+
+ return nt;
+} // LlsrProductSecuritySetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrProductLicensesGetA(
+ LLS_HANDLE Handle,
+ LPSTR DisplayName,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed for use in the given mode.
+
+ NOTE: Not yet implemented. Use LlsrProductLicensesGetW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Mode for which to tally licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicensesGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsProductLicensesGetA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrProductLicensesGetW(
+ LLS_HANDLE Handle,
+ LPWSTR DisplayName,
+ DWORD Mode,
+ LPDWORD pQuantity )
+
+/*++
+
+Routine Description:
+
+ Returns the number of licenses installed for use in the given mode.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ Product (LPWSTR)
+ The name of the product for which to tally licenses.
+ Mode (DWORD)
+ Mode for which to tally licenses.
+ pQuantity (LPDWORD)
+ On return (and if successful), holds the total number of licenses
+ for use by the given product in the given license mode.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsProductLicensesGetW\n"));
+#endif
+
+ *pQuantity = 0;
+
+ if ( !Mode || ServiceIsSecure( DisplayName ) )
+ {
+ // get limit from purchase list
+ *pQuantity = ProductLicensesGet( DisplayName, Mode );
+ }
+ else
+ {
+ DWORD i;
+
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+
+ RtlAcquireResourceShared( &LocalServiceListLock, TRUE );
+
+ // get limit from concurrent limit setting from the registry
+ for ( i=0; i < LocalServiceListSize; i++ )
+ {
+ if ( !lstrcmpi( LocalServiceList[i]->DisplayName, DisplayName ) )
+ {
+ // get concurrent limit straight from the registry, not from LocalServiceList!
+ // (if the mode is set to per seat, the per server licenses in the
+ // LocalServiceList will always be 0!)
+ TCHAR szKeyName[ 512 ];
+ HKEY hKeyService;
+ DWORD dwSize;
+ DWORD dwType;
+
+ wsprintf( szKeyName, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\%s"), LocalServiceList[i]->Name );
+
+ // if error encountered, return STATUS_SUCCESS with *pQuantity = 0
+ if ( STATUS_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hKeyService ) )
+ {
+ dwSize = sizeof( *pQuantity );
+ RegQueryValueEx( hKeyService, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) pQuantity, &dwSize );
+
+ RegCloseKey( hKeyService );
+ }
+
+ break;
+ }
+ }
+
+ RtlReleaseResource( &LocalServiceListLock );
+ }
+
+ return STATUS_SUCCESS;
+} // LlsProductLicensesGetW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimEnumA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr,
+ PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA TargetInfo )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+ NOTE: Not yet implemented. Use LlsrCertificateClaimEnumW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes a license for which the certificate targets are requested.
+ TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA)
+ Container in which to return the target information.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimEnumA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimEnumW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr,
+ PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW TargetInfo )
+
+/*++
+
+Routine Description:
+
+ Enumerates the servers on which a given secure certificate is installed.
+ This function is normally invoked when an attempt to add licenses from
+ a certificate is denied.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes a license for which the certificate targets are requested.
+ TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA)
+ Container in which to return the target information.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumW\n"));
+#endif
+
+ if ( ( 1 != LicenseLevel ) || ( 0 != TargetInfo->Level ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ nt = CertDbClaimsGet( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1,
+ &TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead,
+ (PLLS_CERTIFICATE_CLAIM_INFO_0 *) &TargetInfo->LlsCertificateClaimInfo.Level0->Buffer );
+
+ if ( STATUS_SUCCESS != nt )
+ {
+ TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead = 0;
+ TargetInfo->LlsCertificateClaimInfo.Level0->Buffer = NULL;
+ }
+ }
+
+ return nt;
+} // LlsrCertificateClaimEnumW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddCheckA(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+ NOTE: Not yet implemented. Use LlsrCertificateClaimAddCheckW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes a license for which permission is requested.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimAddCheckA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddCheckW(
+ LLS_HANDLE Handle,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr,
+ LPBOOL pbMayInstall )
+
+/*++
+
+Routine Description:
+
+ Verify that no more licenses from a given certificate are installed in
+ a licensing enterprise than are allowed by the certificate.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes a license for which permission is requested.
+ pbMayInstall (LPBOOL)
+ On return (and if successful), indicates whether the certificate
+ may be legally installed.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckW\n"));
+#endif
+
+ if ( 1 != LicenseLevel )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ *pbMayInstall = CertDbClaimApprove( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1 );
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrCertificateClaimAddCheckW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddA(
+ LLS_HANDLE Handle,
+ LPSTR ServerName,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOA LicensePtr )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+ NOTE: Not yet implemented. Use LlsCertificateClaimAddW().
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOA)
+ Describes the installed license.
+
+Return Value:
+
+ STATUS_NOT_SUPPORTED.
+
+--*/
+
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+} // LlsrCertificateClaimAddA
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrCertificateClaimAddW(
+ LLS_HANDLE Handle,
+ LPWSTR ServerName,
+ DWORD LicenseLevel,
+ PLLS_LICENSE_INFOW LicensePtr )
+
+/*++
+
+Routine Description:
+
+ Declare a number of licenses from a given certificate as being installed
+ on the target machine.
+
+Arguments:
+
+ Handle (LLS_HANDLE)
+ An open LLS handle to the target license server.
+ ServerName (LPWSTR)
+ Name of the server on which the licenses are installed.
+ LicenseLevel (DWORD)
+ The level of the license structure pointed to by pLicenseInfo.
+ LicensePtr (PLLS_LICENSE_INFOW)
+ Describes the installed license.
+
+Return Value:
+
+ STATUS_SUCCESS or NTSTATUS error code.
+
+--*/
+
+{
+ NTSTATUS nt;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddW\n"));
+#endif
+
+ if ( 1 != LicenseLevel )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ nt = CertDbClaimEnter( ServerName, (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1, FALSE, 0 );
+
+ if ( STATUS_SUCCESS == nt )
+ {
+ nt = CertDbSave();
+ }
+ }
+
+ return nt;
+} // LlsrCertificateClaimAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationCertDbAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_CERTIFICATES Certificates )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function receives
+ the contents of the remote certificate database.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated certificate information.
+ Certificates (REPL_CERTIFICATES)
+ Replicated certificate information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationCertDbAddW\n"));
+#endif
+
+ if ( ( 0 != Level )
+ || ( ( NULL != Certificates )
+ && ( ( 0 != Certificates->Level0.ClaimLevel )
+ || ( 0 != Certificates->Level0.HeaderLevel ) ) ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->CertDbSent = TRUE;
+
+ if ( NULL != Certificates )
+ {
+ pClient->CertDbProductStringSize = Certificates->Level0.StringSize;
+ pClient->CertDbProductStrings = Certificates->Level0.Strings;
+ pClient->CertDbNumHeaders = Certificates->Level0.HeaderContainer.Level0.NumHeaders;
+ pClient->CertDbHeaders = Certificates->Level0.HeaderContainer.Level0.Headers;
+ pClient->CertDbNumClaims = Certificates->Level0.ClaimContainer.Level0.NumClaims;
+ pClient->CertDbClaims = Certificates->Level0.ClaimContainer.Level0.Claims;
+
+ // free container only
+ MIDL_user_free( Certificates );
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationCertDbAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationProductSecurityAddW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_SECURE_PRODUCTS SecureProducts )
+
+/*++
+
+Routine Description:
+
+ Called as an optional part of replication, this function receives
+ the list of products which require secure certificates.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated secure product information.
+ SecureProducts (REPL_SECURE_PRODUCTS)
+ Replicated secure product information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationProductSecurityAddW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->ProductSecuritySent = TRUE;
+
+ if ( NULL != SecureProducts )
+ {
+ pClient->ProductSecurityStringSize = SecureProducts->Level0.StringSize;
+ pClient->ProductSecurityStrings = SecureProducts->Level0.Strings;
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationProductSecurityAddW
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+LlsrReplicationUserAddExW(
+ LLS_REPL_HANDLE Handle,
+ DWORD Level,
+ REPL_USERS Users )
+
+/*++
+
+Routine Description:
+
+ Replacement for LlsrReplicationUserAddW(). (This function, unlike its
+ counterpart, supports structure levels.) This function replicates the
+ user list.
+
+Arguments:
+
+ Handle (LLS_REPL_HANDLE)
+ An open replication handle.
+ Level (DWORD)
+ Level of replicated user information.
+ Users (REPL_USERS)
+ Replicated user information.
+
+Return Value:
+
+ STATUS_SUCCESS or STATUS_INVALID_LEVEL.
+
+--*/
+
+{
+ NTSTATUS nt;
+ REPL_CONTEXT_TYPE * pClient;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrReplicationUserAddExW\n"));
+#endif
+
+ if ( ( 0 != Level ) && ( 1 != Level ) )
+ {
+ nt = STATUS_INVALID_LEVEL;
+ }
+ else
+ {
+ pClient = (REPL_CONTEXT_TYPE *) Handle;
+
+ pClient->UsersSent = TRUE;
+ pClient->UserLevel = Level;
+
+ if ( NULL != Users )
+ {
+ if ( 0 == Level )
+ {
+ pClient->UserTableSize = Users->Level0.NumUsers;
+ pClient->Users = Users->Level0.Users;
+ }
+ else
+ {
+ pClient->UserTableSize = Users->Level1.NumUsers;
+ pClient->Users = Users->Level1.Users;
+ }
+
+ // free container only
+ MIDL_user_free( Users );
+ }
+
+ nt = STATUS_SUCCESS;
+ }
+
+ return nt;
+} // LlsrReplicationUserAddExW
+
+
+NTSTATUS
+LlsrCapabilityGet(
+ LLS_HANDLE Handle,
+ DWORD cbCapabilities,
+ LPBYTE pbCapabilities )
+{
+ static DWORD adwCapabilitiesSupported[] =
+ {
+ LLS_CAPABILITY_SECURE_CERTIFICATES,
+ LLS_CAPABILITY_REPLICATE_CERT_DB,
+ LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY,
+ LLS_CAPABILITY_REPLICATE_USERS_EX,
+ LLS_CAPABILITY_SERVICE_INFO_GETW,
+ LLS_CAPABILITY_LOCAL_SERVICE_API,
+ (DWORD) -1L
+ };
+
+ DWORD i;
+ DWORD dwCapByte;
+ DWORD dwCapBit;
+
+ ZeroMemory( pbCapabilities, cbCapabilities );
+
+ for ( i=0; (DWORD) -1L != adwCapabilitiesSupported[ i ]; i++ )
+ {
+ dwCapByte = adwCapabilitiesSupported[ i ] / 8;
+ dwCapBit = adwCapabilitiesSupported[ i ] - 8 * dwCapByte;
+
+ if ( dwCapByte < cbCapabilities )
+ {
+ pbCapabilities[ dwCapByte ] |= ( 1 << dwCapBit );
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LlsrLocalServiceEnumW(
+ LLS_HANDLE Handle,
+ PLLS_LOCAL_SERVICE_ENUM_STRUCTW LocalServiceInfo,
+ DWORD PrefMaxLen,
+ LPDWORD pTotalEntries,
+ LPDWORD pResumeHandle )
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVOID BufPtr = NULL;
+ ULONG BufSize = 0;
+ ULONG EntriesRead = 0;
+ ULONG TotalEntries = 0;
+ ULONG i = 0;
+ ULONG j = 0;
+ const DWORD RecordSize = sizeof( LLS_LOCAL_SERVICE_INFO_0W );
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceEnumW\n"));
+#endif
+
+ if ( 0 != LocalServiceInfo->Level )
+ {
+ return STATUS_INVALID_LEVEL;
+ }
+
+ // Need to scan list so get read access.
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+
+ // Calculate how many records will fit into PrefMaxLen buffer.
+ i = *pResumeHandle;
+ while ( ( i < LocalServiceListSize ) && ( BufSize < PrefMaxLen ) )
+ {
+ BufSize += RecordSize;
+ EntriesRead++;
+ i++;
+ }
+
+ TotalEntries = EntriesRead;
+
+ // If we overflowed the buffer then back up one record.
+ if (BufSize > PrefMaxLen)
+ {
+ BufSize -= RecordSize;
+ EntriesRead--;
+ }
+
+ // Now walk to the end of the list to see how many more records are still
+ // available.
+ TotalEntries += LocalServiceListSize - i;
+
+ if (TotalEntries > EntriesRead)
+ Status = STATUS_MORE_ENTRIES;
+
+ // Reset Enum to correct place.
+ i = *pResumeHandle;
+
+ // We now know how many records will fit into the buffer, so allocate space
+ // and fix up pointers so we can copy the information.
+ BufPtr = MIDL_user_allocate(BufSize);
+ if (BufPtr == NULL)
+ {
+ Status = STATUS_NO_MEMORY;
+ goto LlsrLocalServiceEnumWExit;
+ }
+
+ RtlZeroMemory((PVOID) BufPtr, BufSize);
+
+ // Buffers are all setup, so loop through records and copy the data.
+ while ((j < EntriesRead) && (i < LocalServiceListSize))
+ {
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].KeyName = LocalServiceList[i]->Name;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].DisplayName = LocalServiceList[i]->DisplayName;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FamilyDisplayName = LocalServiceList[i]->FamilyDisplayName;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].Mode = LocalServiceList[i]->Mode;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FlipAllow = LocalServiceList[i]->FlipAllow;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].ConcurrentLimit = LocalServiceList[i]->ConcurrentLimit;
+ ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].HighMark = LocalServiceList[i]->HighMark;
+
+ j++;
+ i++;
+ }
+
+LlsrLocalServiceEnumWExit:
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i);
+#endif
+ *pTotalEntries = TotalEntries;
+
+ *pResumeHandle = (ULONG) i;
+
+ LocalServiceInfo->LlsLocalServiceInfo.Level0->EntriesRead = EntriesRead;
+ LocalServiceInfo->LlsLocalServiceInfo.Level0->Buffer = (PLLS_LOCAL_SERVICE_INFO_0W) BufPtr;
+
+ RtlReleaseResource(&LocalServiceListLock);
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceEnumA(
+ LLS_HANDLE Handle,
+ PLLS_LOCAL_SERVICE_ENUM_STRUCTA LocalServiceInfo,
+ DWORD PrefMaxLen,
+ LPDWORD TotalEntries,
+ LPDWORD ResumeHandle )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceEnumA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceAddW(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceAddW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( ( NULL == LocalServiceInfo->LocalServiceInfo0.KeyName )
+ || ( NULL == LocalServiceInfo->LocalServiceInfo0.DisplayName )
+ || ( NULL == LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLicenseInfo;
+ HKEY hKeyService;
+ DWORD dwDisposition;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // create key
+ lError = RegCreateKeyEx( hKeyLicenseInfo, LocalServiceInfo->LocalServiceInfo0.KeyName, 0, NULL, 0, KEY_WRITE, NULL, &hKeyService, &dwDisposition );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set DisplayName
+ lError = RegSetValueEx( hKeyService,
+ REG_VALUE_NAME,
+ 0,
+ REG_SZ,
+ (LPBYTE) LocalServiceInfo->LocalServiceInfo0.DisplayName,
+ ( sizeof( *LocalServiceInfo->LocalServiceInfo0.DisplayName )
+ * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.DisplayName ) ) ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FamilyDisplayName
+ lError = RegSetValueEx( hKeyService,
+ REG_VALUE_FAMILY,
+ 0,
+ REG_SZ,
+ (LPBYTE) LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName,
+ ( sizeof( *LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName )
+ * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) ) ) );
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ // set remaining items and update LocalServiceList
+ Status = LlsrLocalServiceInfoSetW( Handle, LocalServiceInfo->LocalServiceInfo0.KeyName, Level, LocalServiceInfo );
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceAddA(
+ LLS_HANDLE Handle,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceAddA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoSetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ LONG lError;
+ HKEY hKeyLicenseInfo;
+ HKEY hKeyService;
+
+ lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_WRITE, &hKeyService );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set Mode
+ lError = RegSetValueEx( hKeyService, REG_VALUE_MODE, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.Mode, sizeof( LocalServiceInfo->LocalServiceInfo0.Mode ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set FlipAllow
+ lError = RegSetValueEx( hKeyService, REG_VALUE_FLIP, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.FlipAllow, sizeof( LocalServiceInfo->LocalServiceInfo0.FlipAllow ) );
+
+ if ( ERROR_SUCCESS == lError )
+ {
+ // set ConcurrentLimit
+ lError = RegSetValueEx( hKeyService, REG_VALUE_LIMIT, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit, sizeof( LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit ) );
+ }
+ }
+
+ RegCloseKey( hKeyService );
+ }
+
+ RegCloseKey( hKeyLicenseInfo );
+ }
+
+ switch ( lError )
+ {
+ case ERROR_SUCCESS:
+ Status = STATUS_SUCCESS;
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ default:
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ if ( STATUS_SUCCESS == Status )
+ {
+ LocalServiceListUpdate();
+ LocalServerServiceListUpdate();
+ ServiceListResynch();
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoSetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoGetW(
+ LLS_HANDLE Handle,
+ LPWSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOW * pLocalServiceInfo )
+{
+ NTSTATUS Status;
+
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsrLocalServiceInfoGetW\n"));
+#endif
+
+ if ( 0 != Level )
+ {
+ Status = STATUS_INVALID_LEVEL;
+ }
+ else if ( NULL == KeyName )
+ {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else
+ {
+ PLOCAL_SERVICE_RECORD pRecord;
+
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+
+ pRecord = LocalServiceListFind( KeyName );
+
+ if ( NULL == pRecord )
+ {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ else
+ {
+ *pLocalServiceInfo = MIDL_user_allocate( sizeof( **pLocalServiceInfo ) );
+
+ if ( NULL == *pLocalServiceInfo )
+ {
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ {
+ (*pLocalServiceInfo)->LocalServiceInfo0.KeyName = pRecord->Name;
+ (*pLocalServiceInfo)->LocalServiceInfo0.DisplayName = pRecord->DisplayName;
+ (*pLocalServiceInfo)->LocalServiceInfo0.FamilyDisplayName = pRecord->FamilyDisplayName;
+ (*pLocalServiceInfo)->LocalServiceInfo0.Mode = pRecord->Mode;
+ (*pLocalServiceInfo)->LocalServiceInfo0.FlipAllow = pRecord->FlipAllow;
+ (*pLocalServiceInfo)->LocalServiceInfo0.ConcurrentLimit = pRecord->ConcurrentLimit;
+ (*pLocalServiceInfo)->LocalServiceInfo0.HighMark = pRecord->HighMark;
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ RtlReleaseResource(&LocalServiceListLock);
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+LlsrLocalServiceInfoGetA(
+ LLS_HANDLE Handle,
+ LPSTR KeyName,
+ DWORD Level,
+ PLLS_LOCAL_SERVICE_INFOA * pLocalServiceInfo )
+{
+#if DBG
+ if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC))
+ dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoGetA\n"));
+#endif
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
diff --git a/private/net/svcdlls/lls/server/scaven.c b/private/net/svcdlls/lls/server/scaven.c
new file mode 100644
index 000000000..4c7e7c3b7
--- /dev/null
+++ b/private/net/svcdlls/lls/server/scaven.c
@@ -0,0 +1,198 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ scaven.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) 06-Jan-1995
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Added periodic logging of certificate agreement violations.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+#include "repl.h"
+#include "llsevent.h"
+#include "llsrpc_s.h"
+#include "certdb.h"
+
+NTSTATUS LLSDataSave();
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ScavengerThread (
+ IN PVOID ThreadParameter
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ ThreadParameter - Indicates how many active threads there currently
+ are.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG i;
+ ULONG Count = 0;
+
+ //
+ // Just wait around forver waiting to service things.
+ //
+ while (TRUE) {
+ //
+ // Wait 15 minutes before checking things out.
+ //
+ Sleep(900000L);
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ScavengerThread waking up\n"));
+#endif
+ //
+ // Update HighMark for local table
+ //
+ LocalServerServiceListHighMarkUpdate();
+
+ //
+ // Hmm, lets check replication...
+ //
+ ConfigInfoUpdate();
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ if (ConfigInfo.Replicate) {
+ //
+ // If we are past replication time then do it
+ //
+ if (DateLocalGet() > ConfigInfo.NextReplication)
+ NtSetEvent( ReplicationEvent, NULL );
+
+ }
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ //
+ // Now update our last used time
+ //
+ RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE);
+ RtlAcquireResourceExclusive(&UserListLock, TRUE);
+ LastUsedTime = DateSystemGet();
+ RtlReleaseResource(&UserListLock);
+ RtlReleaseResource(&UserListAddEnumLock);
+
+ //
+ // Check stuff every 6 hours (4 * 15 minutes)
+ //
+ Count++;
+ if (Count > (6 * 4)) {
+ // Reset counter
+ Count = 0;
+
+ //
+ // Save out the data
+ //
+ LLSDataSave();
+
+ //
+ // Save HighMark to registry
+ //
+ LocalServiceListHighMarkSet();
+
+ if (IsMaster) {
+ //
+ // Check for license compliance
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < MasterServiceListSize; i++) {
+ if (MasterServiceList[i]->LicensesUsed > MasterServiceList[i]->Licenses) {
+ LPWSTR SubString[1];
+
+ //
+ // Notify the system
+ //
+ SubString[0] = (LPWSTR) MasterServiceList[i]->Name;
+
+ LogEvent(LLS_EVENT_PRODUCT_NO_LICENSE, 1, SubString, ERROR_SUCCESS);
+ }
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+
+ // log certificate violations
+ CertDbLogViolations();
+ }
+ }
+ }
+
+} // ScavengerThread
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ScavengerInit( )
+
+/*++
+
+Routine Description:
+
+ Looks in registry for given service and sets values accordingly.
+
+Arguments:
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HANDLE Thread;
+ DWORD Ignore;
+
+ //
+ // Just dispatch our scavenger thread
+ //
+ Thread = CreateThread(
+ NULL,
+ 0L,
+ (LPTHREAD_START_ROUTINE) ScavengerThread,
+ 0L,
+ 0L,
+ &Ignore
+ );
+
+} // ScavengerInit
+
+
diff --git a/private/net/svcdlls/lls/server/scaven.h b/private/net/svcdlls/lls/server/scaven.h
new file mode 100644
index 000000000..a6a0c14b7
--- /dev/null
+++ b/private/net/svcdlls/lls/server/scaven.h
@@ -0,0 +1,38 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Scaven.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_SCAVEN_H
+#define _LLS_SCAVEN_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+VOID ScavengerInit( );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/server.c b/private/net/svcdlls/lls/server/server.c
new file mode 100644
index 000000000..459a6e984
--- /dev/null
+++ b/private/net/svcdlls/lls/server/server.c
@@ -0,0 +1,754 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ server.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llsutil.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "purchase.h"
+#include "perseat.h"
+#include "server.h"
+
+#define NO_LLS_APIS
+#include "llsapi.h"
+
+
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////
+
+ULONG ServerListSize = 0;
+PSERVER_RECORD *ServerList = NULL;
+PSERVER_RECORD *ServerTable = NULL;
+
+RTL_RESOURCE ServerListLock;
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListInit()
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RtlInitializeResource(&ServerListLock);
+
+ //
+ // Add ourself as the first server (master server)
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ ServerListAdd( ConfigInfo.ComputerName, NULL);
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+ LocalServerServiceListUpdate();
+
+} // ServerListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServerListCompare(const void *arg1, const void *arg2) {
+ PSERVER_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg1);
+ Svc2 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name );
+
+} // ServerListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServerServiceListCompare(const void *arg1, const void *arg2) {
+ PSERVER_SERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg1);
+ Svc2 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg2);
+
+ return lstrcmpi( MasterServiceTable[Svc1->Service]->Name, MasterServiceTable[Svc2->Service]->Name );
+
+} // ServerServiceListCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_SERVICE_RECORD
+ServerServiceListFind(
+ LPTSTR Name,
+ ULONG ServiceTableSize,
+ PSERVER_SERVICE_RECORD *ServiceList
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServerServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ PMASTER_SERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListFind\n"));
+#endif
+
+ if (ServiceTableSize == 0)
+ return NULL;
+
+ end = (LONG) ServiceTableSize - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = MasterServiceTable[ServiceList[cur]->Service];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return ServiceList[cur];
+ }
+
+ return NULL;
+
+} // ServerServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_RECORD
+ServerListFind(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServerList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found server table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) ServerListSize - 1;
+ LONG cur;
+ int match;
+ PSERVER_RECORD Server;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListFind\n"));
+#endif
+
+ if ((ServerListSize == 0) || (Name == NULL))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Server = ServerList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(Name, Server->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Server;
+ }
+
+ return NULL;
+
+} // ServerListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_SERVICE_RECORD
+ServerServiceListAdd(
+ LPTSTR Name,
+ ULONG ServiceIndex,
+ PULONG pServiceTableSize,
+ PSERVER_SERVICE_RECORD **pServiceList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PSERVER_SERVICE_RECORD Service;
+ PSERVER_SERVICE_RECORD *ServiceList;
+ ULONG ServiceListSize;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerServiceListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0')) || (pServiceTableSize == NULL) || (pServiceList == NULL)) {
+#if DBG
+ dprintf(TEXT("Error LLS: ServerServiceListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceListSize = *pServiceTableSize;
+ ServiceList = *pServiceList;
+
+ //
+ // Try to find the name
+ //
+ Service = ServerServiceListFind(Name, ServiceListSize, ServiceList);
+ if (Service != NULL) {
+ Service->Service = ServiceIndex;
+ return Service;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (ServiceList == NULL) {
+ ServiceList = (PSERVER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_SERVICE_RECORD));
+ } else {
+ ServiceList = (PSERVER_SERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVER_SERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if (ServiceList == NULL) {
+ ServiceListSize = 0;
+ ASSERT(FALSE);
+ goto ServerServiceListAddExit;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Service = (PSERVER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVER_SERVICE_RECORD));
+ if (Service == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceList[ServiceListSize] = Service;
+
+ //
+ // Initialize other stuff
+ //
+ Service->Service = ServiceIndex;
+ Service->MaxSessionCount = 0;
+ Service->MaxSetSessionCount = 0;
+ Service->HighMark = 0;
+ Service->Flags = 0;
+
+ ServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVER_SERVICE_RECORD), ServerServiceListCompare);
+
+ServerServiceListAddExit:
+ *pServiceTableSize = ServiceListSize;
+ *pServiceList = ServiceList;
+ return Service;
+
+} // ServerServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVER_RECORD
+ServerListAdd(
+ LPTSTR Name,
+ LPTSTR Master
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ LPTSTR NewName;
+ PSERVER_RECORD Server;
+ PSERVER_RECORD pMaster;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServerListAdd\n"));
+#endif
+
+ if ((Name == NULL) || (*Name == TEXT('\0'))) {
+#if DBG
+ dprintf(TEXT("Error LLS: ServerListAdd Bad Parms\n"));
+#endif
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ //
+ // Try to find the name
+ //
+ Server = ServerListFind(Name);
+ if (Server != NULL) {
+ return Server;
+ }
+
+ //
+ // No record - so create a new one
+ //
+ if (ServerList == NULL) {
+ ServerList = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD));
+ ServerTable = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD));
+ } else {
+ ServerList = (PSERVER_RECORD *) LocalReAlloc(ServerList, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND);
+ ServerTable = (PSERVER_RECORD *) LocalReAlloc(ServerTable, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate server table
+ //
+ if ((ServerList == NULL) || (ServerTable == NULL)) {
+ ASSERT(FALSE);
+ ServerList = NULL;
+ ServerTable = NULL;
+ ServerListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for Record.
+ //
+ Server = (PSERVER_RECORD) LocalAlloc(LPTR, sizeof(SERVER_RECORD));
+ if (Server == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServerList[ServerListSize] = Server;
+ ServerTable[ServerListSize] = Server;
+
+ NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR));
+ if (NewName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(Server);
+ return NULL;
+ }
+
+ // now copy it over...
+ Server->Name = NewName;
+ lstrcpy(NewName, Name);
+
+ //
+ // Initialize other stuff
+ //
+ Server->Index = ServerListSize + 1;
+ Server->LastReplicated = 0;
+ Server->IsReplicating = FALSE;
+
+ //
+ // Fixup slave/master chain
+ //
+ Server->MasterServer = 0;
+ Server->NextServer = 0;
+ if (Master != NULL) {
+ pMaster = ServerListFind(Master);
+
+ if (pMaster != NULL) {
+ Server->MasterServer = pMaster->Index;
+ Server->NextServer = pMaster->SlaveServer;
+ pMaster->SlaveServer = Server->Index;
+ } else {
+ ASSERT(FALSE);
+ }
+ }
+
+ Server->SlaveServer = 0;
+
+ Server->ServiceTableSize = 0;
+ Server->Services = NULL;
+
+ ServerListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServerList, (size_t) ServerListSize, sizeof(PSERVER_RECORD), ServerListCompare);
+
+ return Server;
+
+} // ServerListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServerServiceListUpdate(
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVER_RECORD Server;
+ PMASTER_SERVICE_RECORD Service;
+ PSERVER_SERVICE_RECORD ServerService;
+ ULONG i, Index;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: LocalServerServiceListUpdate\n"));
+#endif
+
+ //
+ // Find our local server in the Server table
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ Server = ServerListFind( ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ ASSERT(Server != NULL);
+ if (Server == NULL)
+ return;
+
+ RtlAcquireResourceShared(&LocalServiceListLock, TRUE);
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+
+ for (i = 0; i < LocalServiceListSize; i++) {
+ Service = MasterServiceListFind(LocalServiceList[i]->DisplayName);
+ if (Service == NULL) {
+ RtlConvertSharedToExclusive(&MasterServiceListLock);
+ Service = MasterServiceListAdd(LocalServiceList[i]->FamilyDisplayName, LocalServiceList[i]->DisplayName, 0);
+ RtlConvertExclusiveToShared(&MasterServiceListLock);
+ }
+
+ if (Service != NULL) {
+ ServerService = ServerServiceListAdd( Service->Name, Service->Index, &Server->ServiceTableSize, &Server->Services );
+
+ ASSERT(ServerService != NULL);
+ if (ServerService != NULL) {
+ //
+ // Update high mark if needed
+ //
+ if ( LocalServiceList[i]->HighMark > ServerService->HighMark )
+ {
+ ServerService->HighMark = LocalServiceList[i]->HighMark;
+ }
+
+ //
+ // Subtract any old licenses we might have
+ //
+ Service->MaxSessionCount -= ServerService->MaxSessionCount;
+
+ //
+ // Now update to current Licenses
+ //
+ ServerService->MaxSessionCount = LocalServiceList[i]->ConcurrentLimit;
+ if (LocalServiceList[i]->ConcurrentLimit > ServerService->MaxSetSessionCount)
+ ServerService->MaxSetSessionCount = LocalServiceList[i]->ConcurrentLimit;
+
+ Service->MaxSessionCount += ServerService->MaxSessionCount;
+ ServerService->Flags &= ~LLS_FLAG_PRODUCT_PERSEAT;
+
+ if (LocalServiceList[i]->Mode == 0)
+ ServerService->Flags |= LLS_FLAG_PRODUCT_PERSEAT;
+
+ }
+
+ }
+
+ }
+
+ RtlReleaseResource(&MasterServiceListLock);
+ RtlReleaseResource(&LocalServiceListLock);
+
+} // LocalServerServiceListUpdate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+LocalServerServiceListHighMarkUpdate(
+ )
+
+/*++
+
+Routine Description:
+
+ We've got to do this separatly because it locks the Service Table
+ and it needs to be done in reverse. I.E. We need to run through
+ the Service Table to get the display names and then look it up in
+ the ServerServicesList instead of running through the
+ ServerServicesList.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVER_RECORD Server;
+ PSERVER_SERVICE_RECORD ServerService;
+ PMASTER_SERVICE_RECORD Service;
+ ULONG i;
+
+ //
+ // Find our local server in the Server table
+ //
+ RtlEnterCriticalSection(&ConfigInfoLock);
+ Server = ServerListFind( ConfigInfo.ComputerName );
+ RtlLeaveCriticalSection(&ConfigInfoLock);
+
+ ASSERT(Server != NULL);
+ if (Server == NULL)
+ return;
+
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceListSize; i++) {
+
+ ServerService = ServerServiceListFind( ServiceList[i]->DisplayName, Server->ServiceTableSize, Server->Services );
+
+ if (ServerService != NULL) {
+ Service = MasterServiceListFind(ServiceList[i]->DisplayName);
+ ASSERT(Service != NULL);
+
+ if (Service != NULL) {
+ //
+ // Subtract any old info we might have
+ //
+ if (Service->HighMark != 0)
+ {
+ Service->HighMark -= ServerService->HighMark;
+ }
+
+ //
+ // Now update to current Licenses
+ //
+ ServerService->HighMark = ServiceList[i]->HighMark;
+ Service->HighMark += ServerService->HighMark;
+ }
+ }
+
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+ RtlReleaseResource(&MasterServiceListLock);
+
+} // LocalServerServiceListHighMarkUpdate
+
+
+
+#if DBG
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize);
+ if (ServerList == NULL)
+ goto ServerListDebugDumpExit;
+
+ for (i = 0; i < ServerListSize; i++) {
+ dprintf(TEXT("%3lu) [%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"),
+ i + 1, ServerList[i]->Index, TimeToString(ServerList[i]->LastReplicated), ServerList[i]->ServiceTableSize,
+ ServerList[i]->MasterServer, ServerList[i]->SlaveServer, ServerList[i]->NextServer, ServerList[i]->Name);
+ }
+
+ServerListDebugDumpExit:
+ RtlReleaseResource(&ServerListLock);
+
+ return;
+} // ServerListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServerListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ PSERVER_RECORD Server = NULL;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServerListLock, TRUE);
+
+ dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize);
+ if (ServerList == NULL)
+ goto ServerListDebugInfoDumpExit;
+
+ if (Data == NULL)
+ goto ServerListDebugInfoDumpExit;
+
+ Server = ServerListFind( (LPTSTR) Data );
+ if (Server == NULL) {
+ dprintf(TEXT("Server not found: %s\n"), (LPTSTR) Data );
+ goto ServerListDebugInfoDumpExit;
+ }
+
+ //
+ // Show server
+ //
+ dprintf(TEXT("[%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"),
+ Server->Index, TimeToString(Server->LastReplicated), Server->ServiceTableSize,
+ Server->MasterServer, Server->SlaveServer, Server->NextServer, Server->Name);
+
+ //
+ // Now all the services for this server
+ //
+ RtlAcquireResourceShared(&MasterServiceListLock, TRUE);
+ for (i = 0; i < Server->ServiceTableSize; i++) {
+ dprintf(TEXT(" %3lu) Flags: 0x%4lX MS: %3lu HM: %3lu SHM: %3lu Service: %s\n"),
+ i + 1, Server->Services[i]->Flags, Server->Services[i]->MaxSessionCount, Server->Services[i]->HighMark,
+ Server->Services[i]->MaxSetSessionCount, MasterServiceTable[Server->Services[i]->Service]->Name);
+
+ }
+ RtlReleaseResource(&MasterServiceListLock);
+
+ServerListDebugInfoDumpExit:
+ RtlReleaseResource(&ServerListLock);
+
+ return;
+} // ServerListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/server.h b/private/net/svcdlls/lls/server/server.h
new file mode 100644
index 000000000..15dafe202
--- /dev/null
+++ b/private/net/svcdlls/lls/server/server.h
@@ -0,0 +1,84 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Server.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_SERVERTBL_H
+#define _LLS_SERVERTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _SERVER_SERVICE_RECORD {
+ ULONG Service;
+ DWORD Flags;
+
+ ULONG MaxSessionCount; // Max # simultaneous sessions
+ ULONG MaxSetSessionCount; // Max # simultaneous sessions ever set
+ ULONG HighMark; // Max # simultaneous sessions ever attempted
+} SERVER_SERVICE_RECORD, *PSERVER_SERVICE_RECORD;
+
+
+typedef struct _SERVER_RECORD {
+ ULONG Index;
+ LPTSTR Name;
+
+ DWORD LastReplicated;
+ BOOL IsReplicating;
+
+ ULONG MasterServer;
+ ULONG SlaveServer;
+ ULONG NextServer;
+ ULONG ServiceTableSize;
+ PSERVER_SERVICE_RECORD *Services;
+} SERVER_RECORD, *PSERVER_RECORD;
+
+
+extern ULONG ServerListSize;
+extern PSERVER_RECORD *ServerList;
+extern PSERVER_RECORD *ServerTable;
+
+extern RTL_RESOURCE ServerListLock;
+
+
+VOID ServerListInit();
+PSERVER_RECORD ServerListFind( LPTSTR Name );
+PSERVER_RECORD ServerListAdd( LPTSTR Name, LPTSTR Master );
+
+PSERVER_SERVICE_RECORD ServerServiceListFind( LPTSTR Name, ULONG ServiceTableSize, PSERVER_SERVICE_RECORD *ServiceList );
+PSERVER_SERVICE_RECORD ServerServiceListAdd( LPTSTR Name, ULONG ServiceIndex, PULONG pServiceTableSize, PSERVER_SERVICE_RECORD **pServiceList );
+VOID LocalServerServiceListUpdate();
+VOID LocalServerServiceListHighMarkUpdate();
+
+
+#if DBG
+
+VOID ServerListDebugDump( );
+VOID ServerListDebugInfoDump( PVOID Data );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/service.c b/private/net/svcdlls/lls/server/service.c
new file mode 100644
index 000000000..53c4050e1
--- /dev/null
+++ b/private/net/svcdlls/lls/server/service.c
@@ -0,0 +1,625 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.c
+
+Abstract:
+
+ License Logging Service - Common routines for all service.
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <tchar.h>
+#include <shellapi.h>
+
+#include "service.h"
+
+
+
+// internal variables
+SERVICE_STATUS ssStatus; // current status of the service
+SERVICE_STATUS_HANDLE sshStatusHandle;
+DWORD dwErr = 0;
+BOOL bDebug = FALSE;
+TCHAR szErr[256];
+
+// internal function prototypes
+VOID WINAPI ServiceCtrl(DWORD dwCtrlCode);
+VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv);
+VOID CmdInstallService();
+VOID CmdRemoveService();
+VOID CmdDebugService(int argc, char **argv);
+BOOL WINAPI ControlHandler ( DWORD dwCtrlType );
+LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize );
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID _CRTAPI1
+main(
+ int argc,
+ char **argv
+ )
+/*++
+
+Routine Description:
+
+ Main routine to setup the exception handlers and initialize everything
+ before spawning threads to listen to LPC and RPC port requests.
+
+ main() either performs the command line task, or calls
+ StartServiceCtrlDispatcher to register the main service thread. When the
+ this call returns, the service has stopped, so exit.
+
+Arguments:
+
+ argc - number of command line arguments
+ argv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION) ServiceMain },
+ { NULL, NULL }
+ };
+
+ if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) {
+ if ( _stricmp( "install", argv[1]+1 ) == 0 ) {
+ CmdInstallService();
+ } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) {
+ CmdRemoveService();
+ } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) {
+ bDebug = TRUE;
+ CmdDebugService(argc, argv);
+ } else {
+ goto dispatch;
+ }
+
+ exit(0);
+ }
+
+ // if it doesn't match any of the above parameters
+ // the service control manager may be starting the service
+ // so we must call StartServiceCtrlDispatcher
+ dispatch:
+#ifdef DEBUG
+ // this is just to be friendly
+ printf( "%s -install to install the service\n", SZAPPNAME );
+ printf( "%s -remove to remove the service\n", SZAPPNAME );
+ printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME );
+ printf( "\nStartServiceCtrlDispatcher being called.\n" );
+ printf( "This may take several seconds. Please wait.\n" );
+#endif
+
+ if (!StartServiceCtrlDispatcher(dispatchTable))
+ MessageLogWrite(TEXT("StartServiceCtrlDispatcher failed."));
+
+} // main
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID WINAPI
+ServiceMain(
+ DWORD dwArgc,
+ LPTSTR *lpszArgv
+ )
+/*++
+
+Routine Description:
+
+ Performs the service initialization and then calls the ServiceStart()
+ routine to perform majority of the work.
+
+Arguments:
+
+ dwArgc - number of command line arguments
+ lpszArgv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+
+ // register our service control handler:
+ //
+ sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), ServiceCtrl);
+
+ if (!sshStatusHandle)
+ goto cleanup;
+
+ // SERVICE_STATUS members that don't change in example
+ //
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwServiceSpecificExitCode = 0;
+
+
+ // report the status to the service control manager.
+ //
+ if (!ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000)) // wait hint
+ goto cleanup;
+
+
+ ServiceStart( dwArgc, lpszArgv );
+
+cleanup:
+
+ // try to report the stopped status to the service control manager.
+ //
+ if (sshStatusHandle)
+ (VOID)ReportStatusToSCMgr(
+ SERVICE_STOPPED,
+ dwErr,
+ 0);
+
+ return;
+} // ServiceMain
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID WINAPI
+ServiceCtrl(
+ DWORD dwCtrlCode
+ )
+/*++
+
+Routine Description:
+
+ Called by the SCM whenever ControlService() is called on this service.
+
+Arguments:
+
+ dwCtrlCode - type of control requested
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwState = SERVICE_RUNNING;
+
+ // Handle the requested control code.
+ //
+ switch(dwCtrlCode) {
+ // Stop the service.
+ //
+ case SERVICE_CONTROL_STOP:
+ dwState = SERVICE_STOP_PENDING;
+ ssStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ break;
+
+ // Update the service status.
+ //
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ // invalid control code
+ //
+ default:
+ break;
+
+ }
+
+ ReportStatusToSCMgr(dwState, NO_ERROR, 0);
+
+ if ( SERVICE_CONTROL_STOP == dwCtrlCode )
+ {
+ ServiceStop();
+ }
+} // ServiceCtrl
+
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+ReportStatusToSCMgr(
+ DWORD dwCurrentState,
+ DWORD dwWin32ExitCode,
+ DWORD dwWaitHint
+ )
+/*++
+
+Routine Description:
+
+ Sets the current status of the service and reports it to the SCM.
+
+Arguments:
+
+ dwCurrentState - the state of the service
+ dwWin32ExitCode - error code to report
+ dwWaitHint - worst case estimate to next checkpoint
+
+Return Values:
+
+ None.
+
+--*/
+{
+ static DWORD dwCheckPoint = 1;
+ BOOL fResult = TRUE;
+
+ ssStatus.dwControlsAccepted = 0;
+ if ( !bDebug ) { // when debugging we don't report to the SCM
+ if (dwCurrentState == SERVICE_START_PENDING)
+ ssStatus.dwControlsAccepted = 0;
+ else
+ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ ssStatus.dwCurrentState = dwCurrentState;
+ ssStatus.dwWin32ExitCode = dwWin32ExitCode;
+ ssStatus.dwWaitHint = dwWaitHint;
+
+ if ( ( dwCurrentState == SERVICE_RUNNING ) ||
+ ( dwCurrentState == SERVICE_STOPPED ) )
+ ssStatus.dwCheckPoint = 0;
+ else
+ ssStatus.dwCheckPoint = dwCheckPoint++;
+
+
+ // Report the status of the service to the service control manager.
+ //
+ if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) {
+ MessageLogWrite(TEXT("SetServiceStatus"));
+ }
+ }
+ return fResult;
+} // ReportStatusToSCMgr
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MessageLogWrite(
+ LPTSTR lpszMsg
+ )
+/*++
+
+Routine Description:
+
+ Logs an error message.
+
+Arguments:
+
+ lpszMsg - text for message
+
+Return Values:
+
+ None.
+
+--*/
+{
+ TCHAR szMsg[256];
+ HANDLE hEventSource;
+ LPTSTR lpszStrings[2];
+
+
+ if ( !bDebug ) {
+ dwErr = GetLastError();
+
+ // Use event logging to log the error.
+ //
+ hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME));
+
+ _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr);
+ lpszStrings[0] = szMsg;
+ lpszStrings[1] = lpszMsg;
+
+ if (hEventSource != NULL) {
+ ReportEvent(hEventSource, // handle of event source
+ EVENTLOG_ERROR_TYPE, // event type
+ 0, // event category
+ 0, // event ID
+ NULL, // current user's SID
+ 2, // strings in lpszStrings
+ 0, // no bytes of raw data
+ lpszStrings, // array of error strings
+ NULL); // no raw data
+
+ (VOID) DeregisterEventSource(hEventSource);
+ }
+ }
+} // MessageLogWrite
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The following code handles service installation and removal
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CmdInstallService()
+/*++
+
+Routine Description:
+
+ Installs the service.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ TCHAR szPath[512];
+
+ if ( GetModuleFileName( NULL, szPath, 512 ) == 0 ) {
+ _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256));
+ return;
+ }
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if ( schSCManager ) {
+ schService = CreateService(
+ schSCManager, // SCManager database
+ TEXT(SZSERVICENAME), // name of service
+ TEXT(SZSERVICEDISPLAYNAME), // name to display
+ SERVICE_ALL_ACCESS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_DEMAND_START, // start type
+ SERVICE_ERROR_NORMAL, // error control type
+ szPath, // service's binary
+ NULL, // no load ordering group
+ NULL, // no tag identifier
+ TEXT(SZDEPENDENCIES), // dependencies
+ NULL, // LocalSystem account
+ NULL); // no password
+
+ if ( schService ) {
+ _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ CloseServiceHandle(schService);
+ } else {
+ _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256));
+ }
+
+ CloseServiceHandle(schSCManager);
+ } else
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+} // CmdInstallService
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CmdRemoveService()
+/*++
+
+Routine Description:
+
+ Stops and removes the service.
+
+Arguments:
+
+ None.
+
+Return Values:
+
+ None.
+
+--*/
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if ( schSCManager ) {
+ schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS);
+
+ if (schService) {
+ // try to stop the service
+ if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) {
+ _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME));
+ Sleep( 1000 );
+
+ while( QueryServiceStatus( schService, &ssStatus ) ) {
+ if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
+ _tprintf(TEXT("."));
+ Sleep( 1000 );
+ } else
+ break;
+ }
+
+ if ( ssStatus.dwCurrentState == SERVICE_STOPPED )
+ _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+
+ }
+
+ // now remove the service
+ if( DeleteService(schService) )
+ _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) );
+ else
+ _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256));
+
+
+ CloseServiceHandle(schService);
+ } else
+ _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256));
+
+ CloseServiceHandle(schSCManager);
+ } else
+ _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256));
+
+} // CmdRemoveService
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Routines for running the service as a console app
+//
+/////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID CmdDebugService(
+ int argc,
+ char ** argv
+ )
+/*++
+
+Routine Description:
+
+ Runs the service as a console application
+
+Arguments:
+
+ argc - number of command line arguments
+ argv - array of command line arguments
+
+Return Values:
+
+ None.
+
+--*/
+{
+ DWORD dwArgc;
+ LPTSTR *lpszArgv;
+
+#ifdef UNICODE
+ lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) );
+#else
+ dwArgc = (DWORD) argc;
+ lpszArgv = argv;
+#endif
+
+ _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+
+ SetConsoleCtrlHandler( ControlHandler, TRUE );
+
+ ServiceStart( dwArgc, lpszArgv );
+} // CmdDebugService
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL WINAPI
+ControlHandler (
+ DWORD dwCtrlType
+ )
+/*++
+
+Routine Description:
+
+ Handle console control events.
+
+Arguments:
+
+ dwCtrlType - type of control event
+ lpszMsg - text for message
+
+Return Values:
+
+ True - handled
+ False - unhandled
+
+--*/
+{
+
+ switch( dwCtrlType ) {
+ case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate
+ case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode
+ _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME));
+ ServiceStop();
+ return TRUE;
+ break;
+
+ }
+
+ return FALSE;
+
+} // ControlHandler
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+GetLastErrorText(
+ LPTSTR lpszBuf,
+ DWORD dwSize
+ )
+/*++
+
+Routine Description:
+
+ Copies last error message text to string.
+
+Arguments:
+
+ lpszBuf - destination buffer
+ dwSize - size of buffer
+
+Return Values:
+
+ destination buffer
+
+--*/
+{
+ DWORD dwRet;
+ LPTSTR lpszTemp = NULL;
+
+ dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL,
+ GetLastError(),
+ LANG_NEUTRAL,
+ (LPTSTR)&lpszTemp,
+ 0,
+ NULL );
+
+ // supplied buffer is not long enough
+ if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) )
+ lpszBuf[0] = TEXT('\0');
+ else {
+ lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character
+ _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() );
+ }
+
+ if ( lpszTemp )
+ LocalFree((HLOCAL) lpszTemp );
+
+ return lpszBuf;
+} // GetLastErrorText
diff --git a/private/net/svcdlls/lls/server/service.h b/private/net/svcdlls/lls/server/service.h
new file mode 100644
index 000000000..c7a92147a
--- /dev/null
+++ b/private/net/svcdlls/lls/server/service.h
@@ -0,0 +1,59 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Service.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _SERVICE_H
+#define _SERVICE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// name of the executable
+#define SZAPPNAME "LLSrv"
+
+// internal name of the service
+#define SZSERVICENAME "LicenseLoggingService"
+
+// displayed name of the service
+#define SZSERVICEDISPLAYNAME "License Logging Service"
+
+// list of service dependencies - "dep1\0dep2\0\0"
+#define SZDEPENDENCIES ""
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv);
+VOID ServiceStop();
+
+
+
+BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
+VOID MessageLogWrite(LPTSTR lpszMsg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/server/sources b/private/net/svcdlls/lls/server/sources
new file mode 100644
index 000000000..f7128e073
--- /dev/null
+++ b/private/net/svcdlls/lls/server/sources
@@ -0,0 +1,84 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=server
+
+TARGETNAME=llssrv
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc;$(SOURCES_PATH)..\inc
+
+SOURCES= \
+ $(SOURCES_PATH)certdb.c \
+ $(SOURCES_PATH)llsutil.c \
+ $(SOURCES_PATH)service.c \
+ $(SOURCES_PATH)msvctbl.c \
+ $(SOURCES_PATH)registry.c \
+ $(SOURCES_PATH)mapping.c \
+ $(SOURCES_PATH)purchase.c \
+ $(SOURCES_PATH)perseat.c \
+ $(SOURCES_PATH)server.c \
+ $(SOURCES_PATH)repl.c \
+ $(SOURCES_PATH)rpc.c \
+ $(SOURCES_PATH)pack.c \
+ $(SOURCES_PATH)scaven.c \
+ $(SOURCES_PATH)llsrpc_s.c \
+ $(SOURCES_PATH)lsapi_s.c \
+ $(SOURCES_PATH)llsdbg_s.c \
+ $(SOURCES_PATH)svctbl.c \
+ $(SOURCES_PATH)llssrv.c \
+ $(SOURCES_PATH)llssrv.rc
+
+UMTYPE=windows
+UMAPPL=llssrv
+UMRES=$(@R).res
+NTTARGETFILE1=obj\*\llssrv.res
+UMLIBS= \
+ $(SOURCES_PATH)..\common\obj\*\llscomm.lib \
+ obj\*\llssrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib
+
+
+TARGETLIBS= \
+ $(SOURCES_PATH)..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\nlrepl.lib
+
+C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+
+USE_CRTDLL=1
diff --git a/private/net/svcdlls/lls/server/svctbl.c b/private/net/svcdlls/lls/server/svctbl.c
new file mode 100644
index 000000000..a4e8e4307
--- /dev/null
+++ b/private/net/svcdlls/lls/server/svctbl.c
@@ -0,0 +1,902 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ svctbl.c
+
+Abstract:
+
+ Service Table routines. Handles all access to service table
+ for keeping track of running services and session counts to those
+ services.
+
+
+Author:
+
+ Arthur Hanson (arth) 07-Dec-1994
+
+Revision History:
+
+ Jeff Parham (jeffparh) 05-Dec-1995
+ o Integrated per seat and per server purchase models for secure
+ certificates.
+ o Added logging of per server license rejections.
+
+--*/
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "llsapi.h"
+#include "debug.h"
+#include "llssrv.h"
+#include "registry.h"
+#include "ntlsapi.h"
+#include "mapping.h"
+#include "msvctbl.h"
+#include "svctbl.h"
+#include "perseat.h"
+#include "llsevent.h"
+#include "llsutil.h"
+#include "purchase.h"
+
+
+//
+// Must have ending space for version number placeholder!
+//
+#define FILE_PRINT "FilePrint "
+#define FILE_PRINT_BASE "FilePrint"
+#define FILE_PRINT_VERSION_NDX ( 9 )
+
+#define REMOTE_ACCESS "REMOTE_ACCESS "
+#define REMOTE_ACCESS_BASE "REMOTE_ACCESS"
+
+extern ULONG NumFilePrintEntries;
+extern LPTSTR *FilePrintTable;
+
+
+ULONG ServiceListSize = 0;
+PSERVICE_RECORD *ServiceList = NULL;
+PSERVICE_RECORD *ServiceFreeList = NULL;
+
+
+RTL_RESOURCE ServiceListLock;
+
+int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2);
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListInit()
+
+/*++
+
+Routine Description:
+
+ Creates the service table, used for tracking the services and session
+ count. This will pull the initial services from the registry.
+
+ The table is linear so a binary search can be used on the table, so
+ some extra records are initialized so that each time we add a new
+ service we don't have to do a realloc. We also assume that adding
+ new services is a relatively rare occurance, since we need to sort
+ it each time.
+
+ The service table is guarded by a read and write semaphore. Multiple
+ reads can occur, but a write blocks everything.
+
+ The service table has two default entries for FilePrint and REMOTE_ACCESS.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL PerSeatLicensing;
+ ULONG SessionLimit;
+ PSERVICE_RECORD Service;
+
+ RtlInitializeResource(&ServiceListLock);
+
+ //
+ // Just need to init FilePrint values...
+ //
+ Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX );
+ RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit);
+
+ //
+ // Need to init RAS separatly as it uses File/Print Licenses.
+ //
+ Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1);
+ if (Service != NULL) {
+ Service->MaxSessionCount = SessionLimit;
+ Service->PerSeatLicensing = PerSeatLicensing;
+ }
+
+} // ServiceListInit
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl ServiceListCompare(const void *arg1, const void *arg2) {
+ PSERVICE_RECORD Svc1, Svc2;
+
+ Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1);
+ Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2);
+
+ return lstrcmpi( Svc1->Name, Svc2->Name);
+
+} // ServiceListCompare
+
+
+PSERVICE_RECORD
+ServiceListFind(
+ LPTSTR ServiceName
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine to actually do binary search on ServiceList, this
+ does not do any locking as we expect the wrapper routine to do this.
+ The search is a simple binary search.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to found service table entry or NULL if not found.
+
+--*/
+
+{
+ LONG begin = 0;
+ LONG end = (LONG) ServiceListSize - 1;
+ LONG cur;
+ int match;
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListFind\n"));
+#endif
+ if ((ServiceName == NULL) || (ServiceListSize == 0))
+ return NULL;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+ Service = ServiceList[cur];
+
+ // compare the two result into match
+ match = lstrcmpi(ServiceName, Service->Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return Service;
+ }
+
+ return NULL;
+
+} // ServiceListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+VersionToDWORD(LPTSTR Version)
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSTR pVer;
+ DWORD Ver = 0;
+ char tmpStr[10];
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: VersionToDWORD\n"));
+#endif
+
+ if ((Version == NULL) || (*Version == TEXT('\0')))
+ return Ver;
+
+ //
+ // Do major version number
+ //
+ tmpStr[0] = '\0';
+ WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL);
+ Ver = (ULONG) atoi(tmpStr);
+ Ver *= 0x10000;
+
+ //
+ // Now minor - look for period
+ //
+ pVer = tmpStr;
+ while ((*pVer != '\0') && (*pVer != '.'))
+ pVer++;
+
+ if (*pVer == '.') {
+ pVer++;
+ Ver += atoi(pVer);
+ }
+
+ return Ver;
+
+} // VersionToDWORD
+
+
+/////////////////////////////////////////////////////////////////////////
+PSERVICE_RECORD
+ServiceListAdd(
+ LPTSTR ServiceName,
+ ULONG VersionIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Adds a service to the service table. This will also cause a poll of
+ the registry to get the initial values for session limits and the
+ type of licensing being used.
+
+Arguments:
+
+ ServiceName -
+
+Return Value:
+
+ Pointer to added service table entry, or NULL if failed.
+
+--*/
+
+{
+ ULONG i;
+ ULONG SessionLimit = 0;
+ BOOL PerSeatLicensing = FALSE;
+ PSERVICE_RECORD NewService;
+ LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName;
+ PSERVICE_RECORD CurrentRecord = NULL;
+ PMASTER_SERVICE_RECORD mService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListAdd\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.
+ //
+ CurrentRecord = ServiceListFind(ServiceName);
+ if (CurrentRecord != NULL) {
+ return CurrentRecord;
+ }
+
+ //
+ // Allocate space for table (zero init it).
+ //
+ if (ServiceList == NULL) {
+ ServiceList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
+ ServiceFreeList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) );
+ } else {
+ ServiceList = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ ServiceFreeList = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND);
+ }
+
+ //
+ // Make sure we could allocate service table
+ //
+ if ((ServiceList == NULL) || (ServiceFreeList == NULL)) {
+ ASSERT(FALSE);
+ ServiceFreeList = NULL;
+ ServiceList = NULL;
+ ServiceListSize = 0;
+ return NULL;
+ }
+
+ //
+ // Allocate space for saving off Service Name - we will take a space, then
+ // the version string onto the end of the Product Name. Therefore the
+ // product name will be something like "Microsoft SQL 4.2a". We maintain
+ // a pointer to the version, so that we can convert the space to a NULL
+ // and then get the product and version string separatly. Keeping them
+ // together simplifies the qsort and binary search routines.
+ //
+ NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD));
+ if (NewService == NULL) {
+ ASSERT(FALSE);
+ return NULL;
+ }
+
+ ServiceList[ServiceListSize] = NewService;
+ ServiceFreeList[ServiceListSize] = NewService;
+
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR));
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // now copy it over...
+ NewService->Name = NewServiceName;
+ lstrcpy(NewService->Name, ServiceName);
+
+ //
+ // Allocate space for Root Name
+ //
+ NewService->Name[VersionIndex] = TEXT('\0');
+ NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(NewService->Name) + 1) * sizeof(TCHAR));
+
+ if (NewServiceName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ lstrcpy(NewServiceName, NewService->Name);
+ NewService->Name[VersionIndex] = TEXT(' ');
+
+ // point service structure to it...
+ NewService->FamilyName = NewServiceName;
+
+ //
+ // Allocate space for Display Name
+ //
+ RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName);
+
+ if (pDisplayName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService->FamilyName);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // point service structure to it...
+ NewService->DisplayName = pDisplayName;
+
+ RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName);
+
+ if (pFamilyDisplayName == NULL) {
+ ASSERT(FALSE);
+ LocalFree(NewService->Name);
+ LocalFree(NewService->FamilyName);
+ LocalFree(NewService->DisplayName);
+ LocalFree(NewService);
+ return NULL;
+ }
+
+ // point service structure to it...
+ NewService->FamilyDisplayName = pFamilyDisplayName;
+
+ //
+ // Update table size and init entry, including reading init values
+ // from registry.
+ //
+ NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]);
+
+ // Init values from registry...
+ RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit);
+
+ if ( PerSeatLicensing )
+ {
+ // per seat mode
+ NewService->MaxSessionCount = 0;
+ }
+ else if ( ServiceIsSecure( NewService->DisplayName ) )
+ {
+ // per server mode with a secure product; requires certificate
+ NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE );
+ }
+ else
+ {
+ // per server mode with an unsecure product; use limit from registry
+ NewService->MaxSessionCount = SessionLimit;
+ }
+
+ NewService->PerSeatLicensing = PerSeatLicensing;
+ NewService->SessionCount = 0;
+ NewService->Index = ServiceListSize;
+ RtlInitializeCriticalSection(&NewService->ServiceLock);
+
+ if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) {
+ RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE);
+ mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version);
+ RtlReleaseResource(&MasterServiceListLock);
+
+ if (mService == NULL) {
+ ASSERT(FALSE);
+ } else {
+ NewService->MasterService = mService;
+
+ //
+ // In case this got added from the local service list table and we
+ // didn't have a version # yet.
+ //
+ if (mService->Version == 0) {
+ PMASTER_SERVICE_ROOT ServiceRoot = NULL;
+
+ //
+ // Fixup next pointer chain
+ //
+ ServiceRoot = mService->Family;
+ i = 0;
+ while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version))
+ i++;
+
+ mService->next = 0;
+ mService->Version = NewService->Version;
+ if (i > 0) {
+ if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1)
+ mService->next = 0;
+ else
+ mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next;
+
+ if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService)
+ MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1;
+ }
+
+ // Resort it in order of the versions
+ qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare);
+ }
+ }
+
+ }
+
+ ServiceListSize++;
+
+ // Have added the entry - now need to sort it in order of the service names
+ qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare);
+
+ return NewService;
+} // ServiceListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListResynch( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVICE_RECORD Service;
+ BOOL PerSeatLicensing;
+ ULONG SessionLimit;
+ ULONG i = 0;
+ PSERVICE_RECORD FilePrintService;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: ServiceListReSynch\n"));
+#endif
+ if (ServiceList == NULL)
+ return;
+
+ //
+ // Need to update list so get exclusive access.
+ //
+ RtlAcquireResourceExclusive(&ServiceListLock, TRUE);
+
+ for (i = 0; i < ServiceListSize; i++) {
+ //
+ // Note: We will init REMOTE_ACCESS with bogus values here, but we
+ // reset it to the correct values below. Since we have exclusive access
+ // to the table, this is fine (and faster than always checking for
+ // REMOTE_ACCESS).
+ //
+ RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit);
+
+ if ( PerSeatLicensing )
+ {
+ // per seat mode
+ (ServiceList[i])->MaxSessionCount = 0;
+ }
+ else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) )
+ {
+ // per server mode with a secure product; requires certificate
+ (ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE );
+ }
+ else
+ {
+ // per server mode with an unsecure product; use limit from registry
+ (ServiceList[i])->MaxSessionCount = SessionLimit;
+ }
+
+ (ServiceList[i])->PerSeatLicensing = PerSeatLicensing;
+ }
+
+ //
+ // Need to init RAS separatly as it uses File/Print Licenses.
+ //
+ Service = ServiceListFind(TEXT(REMOTE_ACCESS));
+ FilePrintService = ServiceListFind(TEXT(FILE_PRINT));
+
+ ASSERT( NULL != Service );
+ ASSERT( NULL != FilePrintService );
+
+ if ( ( NULL != Service ) && ( NULL != FilePrintService ) )
+ {
+ Service->MaxSessionCount = FilePrintService->MaxSessionCount;
+ Service->PerSeatLicensing = FilePrintService->PerSeatLicensing;
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+ return;
+} // ServiceListResynch
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+DispatchRequestLicense(
+ ULONG DataType,
+ PVOID Data,
+ LPTSTR ServiceID,
+ ULONG VersionIndex,
+ BOOL IsAdmin,
+ ULONG *Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ ServiceID -
+
+ IsAdmin -
+
+ Handle -
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PSERVICE_RECORD Service;
+ ULONG SessionCount;
+ ULONG TableEntry;
+ LPTSTR pServiceID;
+ BOOL NoLicense = FALSE;
+ BOOL PerSeat;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n"));
+#endif
+
+ *Handle = 0xFFFFFFFF;
+ pServiceID = ServiceID;
+
+ // we only need read access since we aren't adding at this point
+ RtlAcquireResourceShared( &ServiceListLock, TRUE );
+
+ // check if in FilePrint table, if so then we use FilePrint as the name
+ ServiceID[ VersionIndex ] = TEXT('\0');
+ if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) )
+ {
+ pServiceID = TEXT(FILE_PRINT);
+ }
+ ServiceID[ VersionIndex ] = TEXT(' ');
+
+ Service = ServiceListFind( pServiceID );
+
+ if (Service == NULL)
+ {
+ // couldn't find service in list, so add it
+ RtlConvertSharedToExclusive(&ServiceListLock);
+ Service = ServiceListAdd( pServiceID, VersionIndex );
+ RtlConvertExclusiveToShared(&ServiceListLock);
+ }
+
+ if (Service != NULL)
+ {
+ // service found or added successfully
+
+ *Handle = (ULONG) Service->Index;
+
+ RtlEnterCriticalSection(&Service->ServiceLock);
+ SessionCount = Service->SessionCount + 1;
+
+#if DBG
+ if (TraceFlags & TRACE_LICENSE_REQUEST)
+ dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount);
+#endif
+
+ if (SessionCount > Service->HighMark)
+ {
+ Service->HighMark = SessionCount;
+ }
+
+ PerSeat = Service->PerSeatLicensing;
+
+ if ( (SessionCount > Service->MaxSessionCount) && !IsAdmin )
+ {
+ if ( PerSeat )
+ {
+ // for node licenses, allow limit to be exceeded
+ // NOTE: The above comparison doesn't even make sense for per seat
+ // licenses, since MaxSessionCount tracks only per server
+ // licenses, never per seat licenses! -JBP
+ Service->SessionCount++;
+ }
+ else
+ {
+ // zero tolerance for exceeding limits on concurrent licenses
+
+ TCHAR szFullUserName[ MAX_DOMAINNAME_LENGTH + MAX_USERNAME_LENGTH + 3 ] = TEXT("");
+ LPWSTR apszSubString[ 2 ];
+ DWORD dwError = ERROR_SUCCESS;
+
+ if ( NT_LS_USER_NAME == DataType )
+ {
+ apszSubString[ 0 ] = (LPWSTR) Data;
+ }
+ else
+ {
+ TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ];
+ TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ];
+ DWORD cbUserName;
+ DWORD cbDomainName;
+ SID_NAME_USE snu;
+
+ cbUserName = sizeof( szUserName );
+ cbDomainName = sizeof( szDomainName );
+
+ if ( LookupAccountSid( NULL, (PSID) Data, szUserName, &cbUserName, szDomainName, &cbDomainName, &snu ) )
+ {
+ lstrcpy( szFullUserName, szDomainName );
+ lstrcat( szFullUserName, TEXT( "\\" ) );
+ lstrcat( szFullUserName, szUserName );
+ }
+ else
+ {
+ dwError = GetLastError();
+ }
+
+ apszSubString[ 0 ] = szFullUserName;
+ }
+
+ apszSubString[ 1 ] = ServiceID;
+
+ LogEvent( LLS_EVENT_USER_NO_LICENSE, 2, apszSubString, dwError );
+
+ NoLicense = TRUE;
+ }
+ }
+ else
+ {
+ // within valid license limits or user is an admin
+ Service->SessionCount++;
+ }
+
+ RtlLeaveCriticalSection(&Service->ServiceLock);
+ RtlReleaseResource(&ServiceListLock);
+
+ if ( PerSeat )
+ {
+ // per node ("per seat") license
+
+ // translate REMOTE_ACCESS into FILE_PRINT before adding to
+ // per seat license records
+ if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) )
+ {
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+ Service = ServiceListFind(TEXT(FILE_PRINT));
+ RtlReleaseResource(&ServiceListLock);
+
+ ASSERT(Service != NULL);
+ }
+
+ UserListUpdate( DataType, Data, Service );
+ }
+ else
+ {
+ // concurrent use ("per server") license
+ if (NoLicense)
+ {
+ Status = LS_INSUFFICIENT_UNITS;
+ *Handle = 0xFFFFFFFF;
+ }
+ }
+ }
+ else
+ {
+ // could neither find nor create service entry
+
+ RtlReleaseResource(&ServiceListLock);
+#if DBG
+ dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) );
+#endif
+ }
+
+ return Status;
+} // DispatchRequestLicense
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DispatchFreeLicense(
+ ULONG Handle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ Handle -
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSERVICE_RECORD Service;
+
+#if DBG
+ if (TraceFlags & TRACE_FUNCTION_TRACE)
+ dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n"));
+#endif
+ //
+ // We only need read access since we aren't adding at this point.
+ //
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+#if DBG
+ if (TraceFlags & TRACE_LICENSE_FREE)
+ dprintf(TEXT("Free Handle: 0x%lX\n"), Handle);
+#endif
+ if (Handle < ServiceListSize) {
+ Service = ServiceFreeList[Handle];
+ RtlEnterCriticalSection(&Service->ServiceLock);
+ if (Service->SessionCount > 0)
+ Service->SessionCount--;
+ RtlLeaveCriticalSection(&Service->ServiceLock);
+ } else {
+#if DBG
+ dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle);
+#endif
+ }
+
+ RtlReleaseResource(&ServiceListLock);
+
+} // DispatchFreeLicense
+
+
+#if DBG
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListDebugDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+
+ //
+ // Need to scan list so get read access.
+ //
+ RtlAcquireResourceShared(&ServiceListLock, TRUE);
+
+ dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
+ if (ServiceList == NULL)
+ goto ServiceListDebugDumpExit;
+
+ for (i = 0; i < ServiceListSize; i++) {
+ if ((ServiceList[i])->PerSeatLicensing)
+ dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
+ else
+ dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name);
+ }
+
+ServiceListDebugDumpExit:
+ RtlReleaseResource(&ServiceListLock);
+
+ return;
+} // ServiceListDebugDump
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ServiceListDebugInfoDump( PVOID Data )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PSERVICE_RECORD CurrentRecord = NULL;
+
+ dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize);
+
+ if (lstrlen((LPWSTR) Data) > 0) {
+ CurrentRecord = ServiceListFind((LPWSTR) Data);
+ if (CurrentRecord != NULL) {
+ if (CurrentRecord->PerSeatLicensing)
+ dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
+ else
+ dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"),
+ CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name);
+ }
+ }
+
+} // ServiceListDebugInfoDump
+
+#endif
diff --git a/private/net/svcdlls/lls/server/svctbl.h b/private/net/svcdlls/lls/server/svctbl.h
new file mode 100644
index 000000000..e5bbb3759
--- /dev/null
+++ b/private/net/svcdlls/lls/server/svctbl.h
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ SvcTbl.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef _LLS_SVCTBL_H
+#define _LLS_SVCTBL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _SERVICE_RECORD {
+ DWORD Index;
+ LPTSTR Name;
+ LPTSTR DisplayName;
+ DWORD Version;
+ LPTSTR FamilyName;
+ LPTSTR FamilyDisplayName;
+
+ PMASTER_SERVICE_RECORD MasterService;
+
+ BOOL PerSeatLicensing;
+
+ RTL_CRITICAL_SECTION ServiceLock;
+ ULONG SessionCount; // # sessions current active
+ ULONG MaxSessionCount; // Max # simultaneous sessions
+ ULONG HighMark; // Max # simultaneous sessions ever attempted
+} SERVICE_RECORD, *PSERVICE_RECORD;
+
+
+extern ULONG ServiceListSize;
+extern PSERVICE_RECORD *ServiceList;
+extern PSERVICE_RECORD *ServiceFreeList;
+extern RTL_RESOURCE ServiceListLock;
+
+
+VOID ServiceListInit();
+PSERVICE_RECORD ServiceListAdd( LPTSTR ServiceName, ULONG VersionIndex );
+PSERVICE_RECORD ServiceListFind( LPTSTR ServiceName );
+VOID ServiceListResynch( );
+NTSTATUS DispatchRequestLicense( ULONG DataType, PVOID Data, LPTSTR ServiceID, ULONG VersionIndex, BOOL IsAdmin, ULONG *Handle );
+VOID DispatchFreeLicense( ULONG Handle );
+DWORD VersionToDWORD(LPTSTR Version);
+
+#if DBG
+VOID ServiceListDebugDump( );
+VOID ServiceListDebugInfoDump( );
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/common/llsdbg.c b/private/net/svcdlls/lls/test/common/llsdbg.c
new file mode 100644
index 000000000..8713a66e4
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/llsdbg.c
@@ -0,0 +1,441 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llsdbg.c
+
+Abstract:
+
+ Client side debugging RPC wrappers for License Logging Service.
+
+Author:
+
+ Arthur Hanson (arth) 30-Jan-1995
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lm.h>
+
+#include "debug.h"
+#include "llsdbg_c.h"
+
+
+LPTSTR pszStringBinding = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsDebugInit( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+ LPTSTR pszUuid = NULL;
+ LPTSTR pszProtocolSequence = NULL;
+ LPTSTR pszNetworkAddress = NULL;
+ LPTSTR pszEndpoint = NULL;
+ LPTSTR pszOptions = NULL;
+ TCHAR pComputer[MAX_COMPUTERNAME_LENGTH + 1];
+ ULONG Size;
+
+ pszProtocolSequence = TEXT("ncalrpc");
+ pszEndpoint = TEXT(LLS_LPC_ENDPOINT);
+ pszNetworkAddress = NULL;
+
+ // Compose a string binding
+ Status = RpcStringBindingComposeW(pszUuid,
+ pszProtocolSequence,
+ pszNetworkAddress,
+ pszEndpoint,
+ pszOptions,
+ &pszStringBinding);
+ if(Status) {
+#ifdef DEBUG
+ dprintf(TEXT("RpcStringBindingComposeW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ // Bind using the created string binding...
+ Status = RpcBindingFromStringBindingW(pszStringBinding, &llsdbgrpc_handle);
+ if(Status) {
+#ifdef DEBUG
+ dprintf(TEXT("RpcBindingFromStringBindingW Failed: 0x%lX\n"), Status);
+#endif
+ return I_RpcMapWin32Status(Status);
+ }
+
+ return I_RpcMapWin32Status(Status);
+
+} // LlsDebugInit
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+NTAPI
+LlsClose( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RPC_STATUS Status;
+
+ Status = RpcStringFree(&pszStringBinding);
+ if (Status )
+ return(Status);
+
+ Status = RpcBindingFree(&llsdbgrpc_handle);
+ return Status;
+
+} // LlsClose
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableDump(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableDump( Table );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableInfoDump(
+ DWORD Table,
+ LPTSTR Item
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableInfoDump( Table, Item );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableInfoDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTableFlush(
+ DWORD Table
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTableFlush( Table );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTableFlush
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgTraceSet(
+ DWORD Flags
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgTraceSet( Flags );
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgTraceSet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgConfigDump( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgConfigDump();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgConfigDump
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgReplicationForce( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgReplicationForce();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgReplicationForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgReplicationDeny( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgReplicationDeny();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgReplicationDeny
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgRegistryUpdateForce( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgRegistryUpdateForce();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgRegistryUpdateForce
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS LlsDbgDatabaseFlush( )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ try {
+ Status = LlsrDbgDatabaseFlush();
+ }
+ except (TRUE) {
+ Status = I_RpcMapWin32Status(RpcExceptionCode());
+#ifdef DEBUG
+ dprintf(TEXT("ERROR LLSDBG RPC Exception: 0x%lX\n"), Status);
+#endif
+ }
+
+ return Status;
+} // LlsDbgDatabaseFlush
diff --git a/private/net/svcdlls/lls/test/common/llsdbg.h b/private/net/svcdlls/lls/test/common/llsdbg.h
new file mode 100644
index 000000000..99083767d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/llsdbg.h
@@ -0,0 +1,47 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsDbg.h
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#ifndef _LLS_LLSDBG_H
+#define _LLS_LLSDBG_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+NTSTATUS NTAPI LlsDebugInit( );
+NTSTATUS NTAPI LlsClose( );
+
+NTSTATUS LlsDbgTableDump( DWORD Table );
+NTSTATUS LlsDbgTableInfoDump( DWORD Table, LPTSTR Item );
+NTSTATUS LlsDbgTableFlush( DWORD Table );
+NTSTATUS LlsDbgTraceSet( DWORD Flags );
+NTSTATUS LlsDbgConfigDump( );
+NTSTATUS LlsDbgReplicationForce( );
+NTSTATUS LlsDbgReplicationDeny( );
+NTSTATUS LlsDbgRegistryUpdateForce( );
+NTSTATUS LlsDbgDatabaseFlush( );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/common/makefile b/private/net/svcdlls/lls/test/common/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/common/sources b/private/net/svcdlls/lls/test/common/sources
new file mode 100644
index 000000000..8942b20a6
--- /dev/null
+++ b/private/net/svcdlls/lls/test/common/sources
@@ -0,0 +1,45 @@
+
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=common
+
+TARGETNAME=llsdbg
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\..\inc;$(_NTROOT)\public\sdk\inc;$(_NTROOT)\private\inc
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE -DDEBUG
+
+USE_CRTDLL=1
+
+
+
+SOURCES= \
+ llsdbg_c.c \
+ llsdbg.c
+
+UMTYPE=windows
diff --git a/private/net/svcdlls/lls/test/ct/initdata.bat b/private/net/svcdlls/lls/test/ct/initdata.bat
new file mode 100644
index 000000000..f47b754fd
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/initdata.bat
@@ -0,0 +1,70 @@
+@echo off
+REM
+SET domain=TestDomain
+REM
+llscmd rpc llslicenseadd "SQL 4.2" "Mr. Slate" 15 "New employees"
+llscmd rpc llslicenseadd "SQL 4.2" "Mr. Slate" 10 "Authoried by Mr. Slate"
+llscmd rpc llslicenseadd "SNA 3.0" "Fred Flinstone" 1 "Who let me in"
+llscmd rpc llslicenseadd "Exchange 1.0" "Mr. Slate" 30 "On the beta"
+llscmd rpc llslicenseadd "Microsoft BackOffice" "Admin" 3 "xxx"
+
+llscmd rpc llsGroupadd Lab2 2 "Computer Lab 2/2222"
+llscmd rpc llsGroupadd Lab1 4 "Computer Lab 1/1111"
+llscmd rpc llsGroupadd Lab3 5 "Computer Lab 3/3333"
+
+llscmd rpc llsGroupuseradd Lab1 %domain%\Fred
+
+llscmd rpc llsGroupuseradd Lab2 %domain%\Barney
+llscmd rpc llsGroupuseradd Lab2 %domain%\Wilma
+
+llscmd rpc llsGroupuseradd Lab3 %domain%\Pebbles
+llscmd rpc llsGroupuseradd Lab3 %domain%\BamBam
+llscmd rpc llsGroupuseradd Lab3 %domain%\Mr.Slate
+
+llscmd add user %domain%\\"Fred" SQL 4.2
+llscmd add user %domain%\\"Fred" SQL 4.2
+llscmd add user %domain%\\"Fred" SQL 4.2
+
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Barney" SQL 4.2
+llscmd add user %domain%\\"Wilma" SQL 4.2
+llscmd add user %domain%\\"Pebbles" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"BamBam" SQL 4.2
+llscmd add user %domain%\\"Mr.Slate" SNA 3.0
+llscmd add user %domain%\\"GeorgeJ" SNA 3.0
+llscmd add user %domain%\\"AstroJ" SNA 3.0
+
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Fred" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Barney" Exchange 1.0
+llscmd add user %domain%\\"Wilma" Exchange 1.0
+llscmd add user %domain%\\"Wilma" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"Pebbles" Exchange 1.0
+llscmd add user %domain%\\"BamBam" Exchange 1.0
+llscmd add user %domain%\\"Mr.Slate" Exchange 1.0
diff --git a/private/net/svcdlls/lls/test/ct/lic.bat b/private/net/svcdlls/lls/test/ct/lic.bat
new file mode 100644
index 000000000..796b9b6be
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 1.0" "TestUser" 5 "Add License Money 1.0"
+llscmd rpc llslicenseadd "Schedule+ 1.0" "TestUser" 5 "Add License Schedule+ 1.0"
+llscmd rpc llslicenseadd "Mail 1.0" "TestUser" 5 "Add License Mail 1.0"
+llscmd rpc llslicenseadd "MONEY30.WIN 1.0" "TestUser" 5 "Add License MONEY30.WIN 1.0"
+llscmd rpc llslicenseadd "MP420A.DOS 1.0" "TestUser" 5 "Add License MP420A.DOS 1.0"
+llscmd rpc llslicenseadd "MSMAIL.ALL 1.0" "TestUser" 5 "Add License MSMAIL.ALL 1.0"
+llscmd rpc llslicenseadd "Win32 1.0" "TestUser" 5 "Add License Win32 1.0"
+llscmd rpc llslicenseadd "Win32s 1.0" "TestUser" 5 "Add License Win32s 1.0"
+llscmd rpc llslicenseadd "Excel 1.0" "TestUser" 5 "Add License Excel 1.0"
+llscmd rpc llslicenseadd "Word 1.0" "TestUser" 5 "Add License Word 1.0"
+llscmd rpc llslicenseadd "ARCADE10.WIN 1.0" "TestUser" 5 "Add License ARCADE10.WIN 1.0"
+llscmd rpc llslicenseadd "ARTGALL.WIN 1.0" "TestUser" 5 "Add License ARTGALL.WIN 1.0"
+llscmd rpc llslicenseadd "VCP100.WIN 1.0" "TestUser" 5 "Add License VCP100.WIN 1.0"
+llscmd rpc llslicenseadd "WINLIB 1.0" "TestUser" 5 "Add License WINLIB 1.0"
+llscmd rpc llslicenseadd "ACCESS20.WIN 1.0" "TestUser" 5 "Add License ACCESS20.WIN 1.0"
+llscmd rpc llslicenseadd "FORTRN32.WIN 1.0" "TestUser" 5 "Add License FORTRN32.WIN 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "IMSL51.DOS 1.0" "TestUser" 5 "Add License IMSL51.DOS 1.0"
+llscmd rpc llslicenseadd "Chicago 1.0" "TestUser" 5 "Add License Chicago 1.0"
+llscmd rpc llslicenseadd "Windows95 1.0" "TestUser" 5 "Add License Windows95 1.0"
+llscmd rpc llslicenseadd "Access 1.0" "TestUser" 5 "Add License Access 1.0"
+llscmd rpc llslicenseadd "MASM611 1.0" "TestUser" 5 "Add License MASM611 1.0"
+llscmd rpc llslicenseadd "MFCKIT 1.0" "TestUser" 5 "Add License MFCKIT 1.0"
+llscmd rpc llslicenseadd "mt300a.all 1.0" "TestUser" 5 "Add License mt300a.all 1.0"
+llscmd rpc llslicenseadd "QWGRAPH 1.0" "TestUser" 5 "Add License QWGRAPH 1.0"
+llscmd rpc llslicenseadd "VB10.DOS 1.0" "TestUser" 5 "Add License VB10.DOS 1.0"
+llscmd rpc llslicenseadd "CBT 1.0" "TestUser" 5 "Add License CBT 1.0"
+llscmd rpc llslicenseadd "EISPAK11.WIN 1.0" "TestUser" 5 "Add License EISPAK11.WIN 1.0"
+llscmd rpc llslicenseadd "ENCART95.WIN 1.0" "TestUser" 5 "Add License ENCART95.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL30.PM 1.0" "TestUser" 5 "Add License EXCEL30.PM 1.0"
+llscmd rpc llslicenseadd "VB30.WIN 1.0" "TestUser" 5 "Add License VB30.WIN 1.0"
+llscmd rpc llslicenseadd "MASM 1.0" "TestUser" 5 "Add License MASM 1.0"
+llscmd rpc llslicenseadd "OLE 1.0" "TestUser" 5 "Add License OLE 1.0"
+llscmd rpc llslicenseadd "Chart 1.0" "TestUser" 5 "Add License Chart 1.0"
+llscmd rpc llslicenseadd "Encarta 1.0" "TestUser" 5 "Add License Encarta 1.0"
+llscmd rpc llslicenseadd "FlightSim 1.0" "TestUser" 5 "Add License FlightSim 1.0"
+llscmd rpc llslicenseadd "Paradox 1.0" "TestUser" 5 "Add License Paradox 1.0"
+llscmd rpc llslicenseadd "VC150.WIN 1.0" "TestUser" 5 "Add License VC150.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.MAC 1.0" "TestUser" 5 "Add License VC20.MAC 1.0"
+llscmd rpc llslicenseadd "IMAGER10.WIN 1.0" "TestUser" 5 "Add License IMAGER10.WIN 1.0"
+llscmd rpc llslicenseadd "LRND300.DOS 1.0" "TestUser" 5 "Add License LRND300.DOS 1.0"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 1.0" "TestUser" 5 "Add License MMGOLF10.WIN 1.0"
+llscmd rpc llslicenseadd "MOM.NT 1.0" "TestUser" 5 "Add License MOM.NT 1.0"
+llscmd rpc llslicenseadd "MSDOS622.DOS 1.0 " "TestUser" 5 "Add License MSDOS622.DOS 1.0"
+llscmd rpc llslicenseadd "MSKBD1.ALL 1.0" "TestUser" 5 "Add License MSKBD1.ALL 1.0"
+llscmd rpc llslicenseadd "MSLOGO.TTF 1.0" "TestUser" 5 "Add License MSLOGO.TTF 1.0"
+llscmd rpc llslicenseadd "MSSMS10 1.0" "TestUser" 5 "Add License MSSMS10 1.0"
+llscmd rpc llslicenseadd "MOM42.WIN 1.0" "TestUser" 5 "Add License MOM42.WIN 1.0"
+llscmd rpc llslicenseadd "SNA 1.0" "TestUser" 5 "Add License SNA 1.0"
+llscmd rpc llslicenseadd "Exchange 1.0" "TestUser" 5 "Add License Exchange 1.0"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 1.0" "TestUser" 5 "Add License MS_EFORM.WIN 1.0"
+llscmd rpc llslicenseadd "ODAKIT 1.0" "TestUser" 5 "Add License ODAKIT 1.0"
+llscmd rpc llslicenseadd "ODBC210.WIN 1.0" "TestUser" 5 "Add License ODBC210.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.NT 1.0" "TestUser" 5 "Add License VC20.NT 1.0"
+llscmd rpc llslicenseadd "BOB.WIN 1.0" "TestUser" 5 "Add License BOB.WIN 1.0"
+llscmd rpc llslicenseadd "BOOK94.WIN 1.0" "TestUser" 5 "Add License BOOK94.WIN 1.0"
+llscmd rpc llslicenseadd "BOWEP.WIN 1.0" "TestUser" 5 "Add License BOWEP.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL50.NT 1.0" "TestUser" 5 "Add License EXCEL50.NT 1.0"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 1.0" "TestUser" 5 "Add License EXCEL50C.WIN 1.0"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 1.0" "TestUser" 5 "Add License FLTSIM5a.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 1.0" "TestUser" 5 "Add License FOXPR26A.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 1.0" "TestUser" 5 "Add License FOXPR26A.WIN 1.0"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 1.0" "TestUser" 5 "Add License FOXPRO26.UNX 1.0"
+llscmd rpc llslicenseadd "WKGRP311.DOS 1.0" "TestUser" 5 "Add License WKGRP311.DOS 1.0"
+llscmd rpc llslicenseadd "wlo09 1.0" "TestUser" 5 "Add License wlo09 1.0"
+llscmd rpc llslicenseadd "wlo10 1.0" "TestUser" 5 "Add License wlo10 1.0"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 1.0" "TestUser" 5 "Add License ACCSOLPK.WIN 1.0"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 1.0" "TestUser" 5 "Add License ACCUPTLS.WIN 1.0"
+llscmd rpc llslicenseadd "ADT20.WIN 1.0" "TestUser" 5 "Add License ADT20.WIN 1.0"
+llscmd rpc llslicenseadd "GS100.DOS 1.0" "TestUser" 5 "Add License GS100.DOS 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 1.0" "TestUser" 5 "Add License ODBCDDP2.NT 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 1.0" "TestUser" 5 "Add License ODBCDDP2.WIN 1.0"
+llscmd rpc llslicenseadd "OFF42C.WIN 1.0" "TestUser" 5 "Add License OFF42C.WIN 1.0"
+llscmd rpc llslicenseadd "OFF43c.WIN 1.0" "TestUser" 5 "Add License OFF43c.WIN 1.0"
+llscmd rpc llslicenseadd "OFFAST10.WIN 1.0" "TestUser" 5 "Add License OFFAST10.WIN 1.0"
+llscmd rpc llslicenseadd "OFFDK10.WIN 1.0" "TestUser" 5 "Add License OFFDK10.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPACK.WIN 1.0" "TestUser" 5 "Add License FONTPACK.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 1.0" "TestUser" 5 "Add License FONTPAK2.WIN 1.0"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 1.0" "TestUser" 5 "Add License HPFNTSET.WIN 1.0"
+llscmd rpc llslicenseadd "hyper.100 1.0" "TestUser" 5 "Add License hyper.100 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "LM22 1.0" "TestUser" 5 "Add License LM22 1.0"
+llscmd rpc llslicenseadd "LMRES10.OS2 1.0" "TestUser" 5 "Add License LMRES10.OS2 1.0"
+llscmd rpc llslicenseadd "LMSFMAC1.0A 1.0" "TestUser" 5 "Add License LMSFMAC1.0A 1.0"
+llscmd rpc llslicenseadd "OFFICE.NT 1.0" "TestUser" 5 "Add License OFFICE.NT 1.0"
+llscmd rpc llslicenseadd "OLE202.WIN 1.0" "TestUser" 5 "Add License OLE202.WIN 1.0"
+llscmd rpc llslicenseadd "ONLN200.WIN 1.0" "TestUser" 5 "Add License ONLN200.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.WIN 1.0" "TestUser" 5 "Add License PROJ40.WIN 1.0"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 1.0" "TestUser" 5 "Add License MSTCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "MVWR200.WIN 1.0" "TestUser" 5 "Add License MVWR200.WIN 1.0"
+llscmd rpc llslicenseadd "NETMON 1.0" "TestUser" 5 "Add License NETMON 1.0"
+llscmd rpc llslicenseadd "OS2-1.31 1.0" "TestUser" 5 "Add License OS2-1.31 1.0"
+llscmd rpc llslicenseadd "PMSUB35.NT 1.0" "TestUser" 5 "Add License PMSUB35.NT 1.0"
+llscmd rpc llslicenseadd "RAS11.LM 1.0" "TestUser" 5 "Add License RAS11.LM 1.0"
+llscmd rpc llslicenseadd "PUB20a.WIN 1.0" "TestUser" 5 "Add License PUB20a.WIN 1.0"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 1.0" "TestUser" 5 "Add License PUBDSIGN.WIN 1.0"
+llscmd rpc llslicenseadd "SAMPLER 1.0" "TestUser" 5 "Add License SAMPLER 1.0"
+llscmd rpc llslicenseadd "SCENES20.WIN 1.0" "TestUser" 5 "Add License SCENES20.WIN 1.0"
+llscmd rpc llslicenseadd "SCHED10A.WIN 1.0" "TestUser" 5 "Add License SCHED10A.WIN 1.0"
+llscmd rpc llslicenseadd "SGML10.WIN 1.0" "TestUser" 5 "Add License SGML10.WIN 1.0"
+llscmd rpc llslicenseadd "SLM.ALL 1.0" "TestUser" 5 "Add License SLM.ALL 1.0"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 1.0" "TestUser" 5 "Add License SNDBIT10.WIN 1.0"
+llscmd rpc llslicenseadd "SPACE10.DOS 1.0" "TestUser" 5 "Add License SPACE10.DOS 1.0"
+llscmd rpc llslicenseadd "SS30.DOS 1.0" "TestUser" 5 "Add License SS30.DOS 1.0"
+llscmd rpc llslicenseadd "ss31std.win 1.0" "TestUser" 5 "Add License ss31std.win 1.0"
+llscmd rpc llslicenseadd "WA100.WIN 1.0" "TestUser" 5 "Add License WA100.WIN 1.0"
+llscmd rpc llslicenseadd "WEP10B.WIN 1.0" "TestUser" 5 "Add License WEP10B.WIN 1.0"
+llscmd rpc llslicenseadd "WEP20.WIN 1.0" "TestUser" 5 "Add License WEP20.WIN 1.0"
+llscmd rpc llslicenseadd "WEP30.WIN 1.0" "TestUser" 5 "Add License WEP30.WIN 1.0"
+llscmd rpc llslicenseadd "WEP40.WIN 1.0" "TestUser" 5 "Add License WEP40.WIN 1.0"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 1.0" "TestUser" 5 "Add License WGTPLT11.WIN 1.0"
+llscmd rpc llslicenseadd "WORD11B.PM 1.0" "TestUser" 5 "Add License WORD11B.PM 1.0"
+llscmd rpc llslicenseadd "WORD6.DOS 1.0" "TestUser" 5 "Add License WORD6.DOS 1.0"
+llscmd rpc llslicenseadd "WORD60.NT 1.0" "TestUser" 5 "Add License WORD60.NT 1.0"
+llscmd rpc llslicenseadd "WORD60C.WIN 1.0" "TestUser" 5 "Add License WORD60C.WIN 1.0"
+llscmd rpc llslicenseadd "word6cnv.nt 1.0" "TestUser" 5 "Add License word6cnv.nt 1.0"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 1.0" "TestUser" 5 "Add License WORD6CNV.WIN 1.0"
+llscmd rpc llslicenseadd "WORDVW60.WIN 1.0" "TestUser" 5 "Add License WORDVW60.WIN 1.0"
+llscmd rpc llslicenseadd "WORKS30.DOS 1.0" "TestUser" 5 "Add License WORKS30.DOS 1.0"
+llscmd rpc llslicenseadd "WORKS30B.WIN 1.0" "TestUser" 5 "Add License WORKS30B.WIN 1.0"
+llscmd rpc llslicenseadd "WPP310.WIN 1.0" "TestUser" 5 "Add License WPP310.WIN 1.0"
+llscmd rpc llslicenseadd "WRITER10.WIN 1.0" "TestUser" 5 "Add License WRITER10.WIN 1.0"
+llscmd rpc llslicenseadd "3com 1.0" "TestUser" 5 "Add License 3com 1.0"
+llscmd rpc llslicenseadd "BLPNT901.ALL 1.0" "TestUser" 5 "Add License BLPNT901.ALL 1.0"
+llscmd rpc llslicenseadd "ARTIST10.WIN 1.0" "TestUser" 5 "Add License ARTIST10.WIN 1.0"
+llscmd rpc llslicenseadd "BASEBL94.WIN 1.0" "TestUser" 5 "Add License BASEBL94.WIN 1.0"
+llscmd rpc llslicenseadd "BA331.DOS 1.0" "TestUser" 5 "Add License BA331.DOS 1.0"
+llscmd rpc llslicenseadd "OS/2 1.0" "TestUser" 5 "Add License OS/2 1.0"
+llscmd rpc llslicenseadd "NetWare 1.0" "TestUser" 5 "Add License NetWare 1.0"
+llscmd rpc llslicenseadd "DOS 1.0" "TestUser" 5 "Add License DOS 1.0"
+llscmd rpc llslicenseadd "C 1.0" "TestUser" 5 "Add License C 1.0"
+llscmd rpc llslicenseadd "CHART300.DOS 1.0" "TestUser" 5 "Add License CHART300.DOS 1.0"
+llscmd rpc llslicenseadd "CINEMA94.WIN 1.0" "TestUser" 5 "Add License CINEMA94.WIN 1.0"
+llscmd rpc llslicenseadd "DELTA10A.WIN 1.0" "TestUser" 5 "Add License DELTA10A.WIN 1.0"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 1.0" "TestUser" 5 "Add License DINOSAUR.WIN 1.0"
+llscmd rpc llslicenseadd "DRAW10.WIN 1.0" "TestUser" 5 "Add License DRAW10.WIN 1.0"
+llscmd rpc llslicenseadd "COBOL500 1.0" "TestUser" 5 "Add License COBOL500 1.0"
+llscmd rpc llslicenseadd "FORT510A.DOS 1.0" "TestUser" 5 "Add License FORT510A.DOS 1.0"
+llscmd rpc llslicenseadd "FORTRN32.NT 1.0" "TestUser" 5 "Add License FORTRN32.NT 1.0"
+llscmd rpc llslicenseadd "VC21.NT 1.0" "TestUser" 5 "Add License VC21.NT 1.0"
+llscmd rpc llslicenseadd "cdex 1.0" "TestUser" 5 "Add License cdex 1.0"
+llscmd rpc llslicenseadd "CLIENTS.NET 1.0" "TestUser" 5 "Add License CLIENTS.NET 1.0"
+llscmd rpc llslicenseadd "CMSRVWS.11 1.0" "TestUser" 5 "Add License CMSRVWS.11 1.0"
+llscmd rpc llslicenseadd "DCIDDK10 1.0" "TestUser" 5 "Add License DCIDDK10 1.0"
+llscmd rpc llslicenseadd "PPT40c.WIN 1.0" "TestUser" 5 "Add License PPT40c.WIN 1.0"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 1.0" "TestUser" 5 "Add License PROFIT1B.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.DOS 1.0" "TestUser" 5 "Add License PROJ40.DOS 1.0"
+llscmd rpc llslicenseadd "LMUNIX 1.0" "TestUser" 5 "Add License LMUNIX 1.0"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 1.0" "TestUser" 5 "Add License MOUSE10a.ALL 1.0"
+llscmd rpc llslicenseadd "MSD211.DOS 1.0" "TestUser" 5 "Add License MSD211.DOS 1.0"
+llscmd rpc llslicenseadd "RESKIT.NT 1.0" "TestUser" 5 "Add License RESKIT.NT 1.0"
+llscmd rpc llslicenseadd "SNA21.NT 1.0" "TestUser" 5 "Add License SNA21.NT 1.0"
+llscmd rpc llslicenseadd "SQL.NT 1.0" "TestUser" 5 "Add License SQL.NT 1.0"
+llscmd rpc llslicenseadd "SQL42B.OS2 1.0" "TestUser" 5 "Add License SQL42B.OS2 1.0"
+llscmd rpc llslicenseadd "SQLRES10.OS2 1.0" "TestUser" 5 "Add License SQLRES10.OS2 1.0"
+llscmd rpc llslicenseadd "WFW311.WIN 1.0" "TestUser" 5 "Add License WFW311.WIN 1.0"
+llscmd rpc llslicenseadd "WFWCONN.WIN 1.0" "TestUser" 5 "Add License WFWCONN.WIN 1.0"
+llscmd rpc llslicenseadd "WIN31 1.0" "TestUser" 5 "Add License WIN31 1.0"
+llscmd rpc llslicenseadd "WING10.WIN 1.0" "TestUser" 5 "Add License WING10.WIN 1.0"
+llscmd rpc llslicenseadd "winlogin.win 1.0" "TestUser" 5 "Add License winlogin.win 1.0"
+llscmd rpc llslicenseadd "WINNT.NT 1.0" "TestUser" 5 "Add License WINNT.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.SRV 1.0" "TestUser" 5 "Add License WINNT35.SRV 1.0"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 1.0" "TestUser" 5 "Add License WMDLRSDK.WIN 1.0"
+llscmd rpc llslicenseadd "GOLF20.WIN 1.0" "TestUser" 5 "Add License GOLF20.WIN 1.0"
+llscmd rpc llslicenseadd "GRAPH50.WIN 1.0" "TestUser" 5 "Add License GRAPH50.WIN 1.0"
+llscmd rpc llslicenseadd "GREETING.WIN 1.0" "TestUser" 5 "Add License GREETING.WIN 1.0"
+llscmd rpc llslicenseadd "VC20A.NT 1.0" "TestUser" 5 "Add License VC20A.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.WKS 1.0" "TestUser" 5 "Add License WINNT35.WKS 1.0"
+llscmd rpc llslicenseadd "wpen100a.win 1.0" "TestUser" 5 "Add License wpen100a.win 1.0"
+llscmd rpc llslicenseadd "WPS.WIN 1.0" "TestUser" 5 "Add License WPS.WIN 1.0"
+llscmd rpc llslicenseadd "TAPI10.WIN 1.0" "TestUser" 5 "Add License TAPI10.WIN 1.0"
+llscmd rpc llslicenseadd "TCPIP.ALL 1.0" "TestUser" 5 "Add License TCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "TCPUTIL1.0 1.0" "TestUser" 5 "Add License TCPUTIL1.0 1.0"
+llscmd rpc llslicenseadd "VFW11.WIN 1.0" "TestUser" 5 "Add License VFW11.WIN 1.0"
+llscmd rpc llslicenseadd "WRESKIT2.00 1.0" "TestUser" 5 "Add License WRESKIT2.00 1.0"
+llscmd rpc llslicenseadd "WSS20.WIN 1.0" "TestUser" 5 "Add License WSS20.WIN 1.0"
+llscmd rpc llslicenseadd "MacWord 1.0" "TestUser" 5 "Add License MacWord 1.0"
+llscmd rpc llslicenseadd "PowerPoint 1.0" "TestUser" 5 "Add License PowerPoint 1.0"
+llscmd rpc llslicenseadd "BoundsChecker 1.0" "TestUser" 5 "Add License BoundsChecker 1.0"
+llscmd rpc llslicenseadd "Quattro 1.0" "TestUser" 5 "Add License Quattro 1.0"
+llscmd rpc llslicenseadd "WordPerfect 1.0" "TestUser" 5 "Add License WordPerfect 1.0"
+llscmd rpc llslicenseadd "Office 1.0" "TestUser" 5 "Add License Office 1.0"
+llscmd rpc llslicenseadd "PerfectOffice 1.0" "TestUser" 5 "Add License PerfectOffice 1.0"
+llscmd rpc llslicenseadd "SQL 1.0" "TestUser" 5 "Add License SQL 1.0"
diff --git a/private/net/svcdlls/lls/test/ct/lic1.bat b/private/net/svcdlls/lls/test/ct/lic1.bat
new file mode 100644
index 000000000..796b9b6be
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic1.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 1.0" "TestUser" 5 "Add License Money 1.0"
+llscmd rpc llslicenseadd "Schedule+ 1.0" "TestUser" 5 "Add License Schedule+ 1.0"
+llscmd rpc llslicenseadd "Mail 1.0" "TestUser" 5 "Add License Mail 1.0"
+llscmd rpc llslicenseadd "MONEY30.WIN 1.0" "TestUser" 5 "Add License MONEY30.WIN 1.0"
+llscmd rpc llslicenseadd "MP420A.DOS 1.0" "TestUser" 5 "Add License MP420A.DOS 1.0"
+llscmd rpc llslicenseadd "MSMAIL.ALL 1.0" "TestUser" 5 "Add License MSMAIL.ALL 1.0"
+llscmd rpc llslicenseadd "Win32 1.0" "TestUser" 5 "Add License Win32 1.0"
+llscmd rpc llslicenseadd "Win32s 1.0" "TestUser" 5 "Add License Win32s 1.0"
+llscmd rpc llslicenseadd "Excel 1.0" "TestUser" 5 "Add License Excel 1.0"
+llscmd rpc llslicenseadd "Word 1.0" "TestUser" 5 "Add License Word 1.0"
+llscmd rpc llslicenseadd "ARCADE10.WIN 1.0" "TestUser" 5 "Add License ARCADE10.WIN 1.0"
+llscmd rpc llslicenseadd "ARTGALL.WIN 1.0" "TestUser" 5 "Add License ARTGALL.WIN 1.0"
+llscmd rpc llslicenseadd "VCP100.WIN 1.0" "TestUser" 5 "Add License VCP100.WIN 1.0"
+llscmd rpc llslicenseadd "WINLIB 1.0" "TestUser" 5 "Add License WINLIB 1.0"
+llscmd rpc llslicenseadd "ACCESS20.WIN 1.0" "TestUser" 5 "Add License ACCESS20.WIN 1.0"
+llscmd rpc llslicenseadd "FORTRN32.WIN 1.0" "TestUser" 5 "Add License FORTRN32.WIN 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "IMSL51.DOS 1.0" "TestUser" 5 "Add License IMSL51.DOS 1.0"
+llscmd rpc llslicenseadd "Chicago 1.0" "TestUser" 5 "Add License Chicago 1.0"
+llscmd rpc llslicenseadd "Windows95 1.0" "TestUser" 5 "Add License Windows95 1.0"
+llscmd rpc llslicenseadd "Access 1.0" "TestUser" 5 "Add License Access 1.0"
+llscmd rpc llslicenseadd "MASM611 1.0" "TestUser" 5 "Add License MASM611 1.0"
+llscmd rpc llslicenseadd "MFCKIT 1.0" "TestUser" 5 "Add License MFCKIT 1.0"
+llscmd rpc llslicenseadd "mt300a.all 1.0" "TestUser" 5 "Add License mt300a.all 1.0"
+llscmd rpc llslicenseadd "QWGRAPH 1.0" "TestUser" 5 "Add License QWGRAPH 1.0"
+llscmd rpc llslicenseadd "VB10.DOS 1.0" "TestUser" 5 "Add License VB10.DOS 1.0"
+llscmd rpc llslicenseadd "CBT 1.0" "TestUser" 5 "Add License CBT 1.0"
+llscmd rpc llslicenseadd "EISPAK11.WIN 1.0" "TestUser" 5 "Add License EISPAK11.WIN 1.0"
+llscmd rpc llslicenseadd "ENCART95.WIN 1.0" "TestUser" 5 "Add License ENCART95.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL30.PM 1.0" "TestUser" 5 "Add License EXCEL30.PM 1.0"
+llscmd rpc llslicenseadd "VB30.WIN 1.0" "TestUser" 5 "Add License VB30.WIN 1.0"
+llscmd rpc llslicenseadd "MASM 1.0" "TestUser" 5 "Add License MASM 1.0"
+llscmd rpc llslicenseadd "OLE 1.0" "TestUser" 5 "Add License OLE 1.0"
+llscmd rpc llslicenseadd "Chart 1.0" "TestUser" 5 "Add License Chart 1.0"
+llscmd rpc llslicenseadd "Encarta 1.0" "TestUser" 5 "Add License Encarta 1.0"
+llscmd rpc llslicenseadd "FlightSim 1.0" "TestUser" 5 "Add License FlightSim 1.0"
+llscmd rpc llslicenseadd "Paradox 1.0" "TestUser" 5 "Add License Paradox 1.0"
+llscmd rpc llslicenseadd "VC150.WIN 1.0" "TestUser" 5 "Add License VC150.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.MAC 1.0" "TestUser" 5 "Add License VC20.MAC 1.0"
+llscmd rpc llslicenseadd "IMAGER10.WIN 1.0" "TestUser" 5 "Add License IMAGER10.WIN 1.0"
+llscmd rpc llslicenseadd "LRND300.DOS 1.0" "TestUser" 5 "Add License LRND300.DOS 1.0"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 1.0" "TestUser" 5 "Add License MMGOLF10.WIN 1.0"
+llscmd rpc llslicenseadd "MOM.NT 1.0" "TestUser" 5 "Add License MOM.NT 1.0"
+llscmd rpc llslicenseadd "MSDOS622.DOS 1.0 " "TestUser" 5 "Add License MSDOS622.DOS 1.0"
+llscmd rpc llslicenseadd "MSKBD1.ALL 1.0" "TestUser" 5 "Add License MSKBD1.ALL 1.0"
+llscmd rpc llslicenseadd "MSLOGO.TTF 1.0" "TestUser" 5 "Add License MSLOGO.TTF 1.0"
+llscmd rpc llslicenseadd "MSSMS10 1.0" "TestUser" 5 "Add License MSSMS10 1.0"
+llscmd rpc llslicenseadd "MOM42.WIN 1.0" "TestUser" 5 "Add License MOM42.WIN 1.0"
+llscmd rpc llslicenseadd "SNA 1.0" "TestUser" 5 "Add License SNA 1.0"
+llscmd rpc llslicenseadd "Exchange 1.0" "TestUser" 5 "Add License Exchange 1.0"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 1.0" "TestUser" 5 "Add License MS_EFORM.WIN 1.0"
+llscmd rpc llslicenseadd "ODAKIT 1.0" "TestUser" 5 "Add License ODAKIT 1.0"
+llscmd rpc llslicenseadd "ODBC210.WIN 1.0" "TestUser" 5 "Add License ODBC210.WIN 1.0"
+llscmd rpc llslicenseadd "VC20.NT 1.0" "TestUser" 5 "Add License VC20.NT 1.0"
+llscmd rpc llslicenseadd "BOB.WIN 1.0" "TestUser" 5 "Add License BOB.WIN 1.0"
+llscmd rpc llslicenseadd "BOOK94.WIN 1.0" "TestUser" 5 "Add License BOOK94.WIN 1.0"
+llscmd rpc llslicenseadd "BOWEP.WIN 1.0" "TestUser" 5 "Add License BOWEP.WIN 1.0"
+llscmd rpc llslicenseadd "EXCEL50.NT 1.0" "TestUser" 5 "Add License EXCEL50.NT 1.0"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 1.0" "TestUser" 5 "Add License EXCEL50C.WIN 1.0"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 1.0" "TestUser" 5 "Add License FLTSIM5a.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 1.0" "TestUser" 5 "Add License FOXPR26A.DOS 1.0"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 1.0" "TestUser" 5 "Add License FOXPR26A.WIN 1.0"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 1.0" "TestUser" 5 "Add License FOXPRO26.UNX 1.0"
+llscmd rpc llslicenseadd "WKGRP311.DOS 1.0" "TestUser" 5 "Add License WKGRP311.DOS 1.0"
+llscmd rpc llslicenseadd "wlo09 1.0" "TestUser" 5 "Add License wlo09 1.0"
+llscmd rpc llslicenseadd "wlo10 1.0" "TestUser" 5 "Add License wlo10 1.0"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 1.0" "TestUser" 5 "Add License ACCSOLPK.WIN 1.0"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 1.0" "TestUser" 5 "Add License ACCUPTLS.WIN 1.0"
+llscmd rpc llslicenseadd "ADT20.WIN 1.0" "TestUser" 5 "Add License ADT20.WIN 1.0"
+llscmd rpc llslicenseadd "GS100.DOS 1.0" "TestUser" 5 "Add License GS100.DOS 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 1.0" "TestUser" 5 "Add License ODBCDDP2.NT 1.0"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 1.0" "TestUser" 5 "Add License ODBCDDP2.WIN 1.0"
+llscmd rpc llslicenseadd "OFF42C.WIN 1.0" "TestUser" 5 "Add License OFF42C.WIN 1.0"
+llscmd rpc llslicenseadd "OFF43c.WIN 1.0" "TestUser" 5 "Add License OFF43c.WIN 1.0"
+llscmd rpc llslicenseadd "OFFAST10.WIN 1.0" "TestUser" 5 "Add License OFFAST10.WIN 1.0"
+llscmd rpc llslicenseadd "OFFDK10.WIN 1.0" "TestUser" 5 "Add License OFFDK10.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPACK.WIN 1.0" "TestUser" 5 "Add License FONTPACK.WIN 1.0"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 1.0" "TestUser" 5 "Add License FONTPAK2.WIN 1.0"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 1.0" "TestUser" 5 "Add License HPFNTSET.WIN 1.0"
+llscmd rpc llslicenseadd "hyper.100 1.0" "TestUser" 5 "Add License hyper.100 1.0"
+llscmd rpc llslicenseadd "IMSL.NT 1.0" "TestUser" 5 "Add License IMSL.NT 1.0"
+llscmd rpc llslicenseadd "IMSL32.DOS 1.0" "TestUser" 5 "Add License IMSL32.DOS 1.0"
+llscmd rpc llslicenseadd "LM22 1.0" "TestUser" 5 "Add License LM22 1.0"
+llscmd rpc llslicenseadd "LMRES10.OS2 1.0" "TestUser" 5 "Add License LMRES10.OS2 1.0"
+llscmd rpc llslicenseadd "LMSFMAC1.0A 1.0" "TestUser" 5 "Add License LMSFMAC1.0A 1.0"
+llscmd rpc llslicenseadd "OFFICE.NT 1.0" "TestUser" 5 "Add License OFFICE.NT 1.0"
+llscmd rpc llslicenseadd "OLE202.WIN 1.0" "TestUser" 5 "Add License OLE202.WIN 1.0"
+llscmd rpc llslicenseadd "ONLN200.WIN 1.0" "TestUser" 5 "Add License ONLN200.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.WIN 1.0" "TestUser" 5 "Add License PROJ40.WIN 1.0"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 1.0" "TestUser" 5 "Add License MSTCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "MVWR200.WIN 1.0" "TestUser" 5 "Add License MVWR200.WIN 1.0"
+llscmd rpc llslicenseadd "NETMON 1.0" "TestUser" 5 "Add License NETMON 1.0"
+llscmd rpc llslicenseadd "OS2-1.31 1.0" "TestUser" 5 "Add License OS2-1.31 1.0"
+llscmd rpc llslicenseadd "PMSUB35.NT 1.0" "TestUser" 5 "Add License PMSUB35.NT 1.0"
+llscmd rpc llslicenseadd "RAS11.LM 1.0" "TestUser" 5 "Add License RAS11.LM 1.0"
+llscmd rpc llslicenseadd "PUB20a.WIN 1.0" "TestUser" 5 "Add License PUB20a.WIN 1.0"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 1.0" "TestUser" 5 "Add License PUBDSIGN.WIN 1.0"
+llscmd rpc llslicenseadd "SAMPLER 1.0" "TestUser" 5 "Add License SAMPLER 1.0"
+llscmd rpc llslicenseadd "SCENES20.WIN 1.0" "TestUser" 5 "Add License SCENES20.WIN 1.0"
+llscmd rpc llslicenseadd "SCHED10A.WIN 1.0" "TestUser" 5 "Add License SCHED10A.WIN 1.0"
+llscmd rpc llslicenseadd "SGML10.WIN 1.0" "TestUser" 5 "Add License SGML10.WIN 1.0"
+llscmd rpc llslicenseadd "SLM.ALL 1.0" "TestUser" 5 "Add License SLM.ALL 1.0"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 1.0" "TestUser" 5 "Add License SNDBIT10.WIN 1.0"
+llscmd rpc llslicenseadd "SPACE10.DOS 1.0" "TestUser" 5 "Add License SPACE10.DOS 1.0"
+llscmd rpc llslicenseadd "SS30.DOS 1.0" "TestUser" 5 "Add License SS30.DOS 1.0"
+llscmd rpc llslicenseadd "ss31std.win 1.0" "TestUser" 5 "Add License ss31std.win 1.0"
+llscmd rpc llslicenseadd "WA100.WIN 1.0" "TestUser" 5 "Add License WA100.WIN 1.0"
+llscmd rpc llslicenseadd "WEP10B.WIN 1.0" "TestUser" 5 "Add License WEP10B.WIN 1.0"
+llscmd rpc llslicenseadd "WEP20.WIN 1.0" "TestUser" 5 "Add License WEP20.WIN 1.0"
+llscmd rpc llslicenseadd "WEP30.WIN 1.0" "TestUser" 5 "Add License WEP30.WIN 1.0"
+llscmd rpc llslicenseadd "WEP40.WIN 1.0" "TestUser" 5 "Add License WEP40.WIN 1.0"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 1.0" "TestUser" 5 "Add License WGTPLT11.WIN 1.0"
+llscmd rpc llslicenseadd "WORD11B.PM 1.0" "TestUser" 5 "Add License WORD11B.PM 1.0"
+llscmd rpc llslicenseadd "WORD6.DOS 1.0" "TestUser" 5 "Add License WORD6.DOS 1.0"
+llscmd rpc llslicenseadd "WORD60.NT 1.0" "TestUser" 5 "Add License WORD60.NT 1.0"
+llscmd rpc llslicenseadd "WORD60C.WIN 1.0" "TestUser" 5 "Add License WORD60C.WIN 1.0"
+llscmd rpc llslicenseadd "word6cnv.nt 1.0" "TestUser" 5 "Add License word6cnv.nt 1.0"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 1.0" "TestUser" 5 "Add License WORD6CNV.WIN 1.0"
+llscmd rpc llslicenseadd "WORDVW60.WIN 1.0" "TestUser" 5 "Add License WORDVW60.WIN 1.0"
+llscmd rpc llslicenseadd "WORKS30.DOS 1.0" "TestUser" 5 "Add License WORKS30.DOS 1.0"
+llscmd rpc llslicenseadd "WORKS30B.WIN 1.0" "TestUser" 5 "Add License WORKS30B.WIN 1.0"
+llscmd rpc llslicenseadd "WPP310.WIN 1.0" "TestUser" 5 "Add License WPP310.WIN 1.0"
+llscmd rpc llslicenseadd "WRITER10.WIN 1.0" "TestUser" 5 "Add License WRITER10.WIN 1.0"
+llscmd rpc llslicenseadd "3com 1.0" "TestUser" 5 "Add License 3com 1.0"
+llscmd rpc llslicenseadd "BLPNT901.ALL 1.0" "TestUser" 5 "Add License BLPNT901.ALL 1.0"
+llscmd rpc llslicenseadd "ARTIST10.WIN 1.0" "TestUser" 5 "Add License ARTIST10.WIN 1.0"
+llscmd rpc llslicenseadd "BASEBL94.WIN 1.0" "TestUser" 5 "Add License BASEBL94.WIN 1.0"
+llscmd rpc llslicenseadd "BA331.DOS 1.0" "TestUser" 5 "Add License BA331.DOS 1.0"
+llscmd rpc llslicenseadd "OS/2 1.0" "TestUser" 5 "Add License OS/2 1.0"
+llscmd rpc llslicenseadd "NetWare 1.0" "TestUser" 5 "Add License NetWare 1.0"
+llscmd rpc llslicenseadd "DOS 1.0" "TestUser" 5 "Add License DOS 1.0"
+llscmd rpc llslicenseadd "C 1.0" "TestUser" 5 "Add License C 1.0"
+llscmd rpc llslicenseadd "CHART300.DOS 1.0" "TestUser" 5 "Add License CHART300.DOS 1.0"
+llscmd rpc llslicenseadd "CINEMA94.WIN 1.0" "TestUser" 5 "Add License CINEMA94.WIN 1.0"
+llscmd rpc llslicenseadd "DELTA10A.WIN 1.0" "TestUser" 5 "Add License DELTA10A.WIN 1.0"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 1.0" "TestUser" 5 "Add License DINOSAUR.WIN 1.0"
+llscmd rpc llslicenseadd "DRAW10.WIN 1.0" "TestUser" 5 "Add License DRAW10.WIN 1.0"
+llscmd rpc llslicenseadd "COBOL500 1.0" "TestUser" 5 "Add License COBOL500 1.0"
+llscmd rpc llslicenseadd "FORT510A.DOS 1.0" "TestUser" 5 "Add License FORT510A.DOS 1.0"
+llscmd rpc llslicenseadd "FORTRN32.NT 1.0" "TestUser" 5 "Add License FORTRN32.NT 1.0"
+llscmd rpc llslicenseadd "VC21.NT 1.0" "TestUser" 5 "Add License VC21.NT 1.0"
+llscmd rpc llslicenseadd "cdex 1.0" "TestUser" 5 "Add License cdex 1.0"
+llscmd rpc llslicenseadd "CLIENTS.NET 1.0" "TestUser" 5 "Add License CLIENTS.NET 1.0"
+llscmd rpc llslicenseadd "CMSRVWS.11 1.0" "TestUser" 5 "Add License CMSRVWS.11 1.0"
+llscmd rpc llslicenseadd "DCIDDK10 1.0" "TestUser" 5 "Add License DCIDDK10 1.0"
+llscmd rpc llslicenseadd "PPT40c.WIN 1.0" "TestUser" 5 "Add License PPT40c.WIN 1.0"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 1.0" "TestUser" 5 "Add License PROFIT1B.WIN 1.0"
+llscmd rpc llslicenseadd "PROJ40.DOS 1.0" "TestUser" 5 "Add License PROJ40.DOS 1.0"
+llscmd rpc llslicenseadd "LMUNIX 1.0" "TestUser" 5 "Add License LMUNIX 1.0"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 1.0" "TestUser" 5 "Add License MOUSE10a.ALL 1.0"
+llscmd rpc llslicenseadd "MSD211.DOS 1.0" "TestUser" 5 "Add License MSD211.DOS 1.0"
+llscmd rpc llslicenseadd "RESKIT.NT 1.0" "TestUser" 5 "Add License RESKIT.NT 1.0"
+llscmd rpc llslicenseadd "SNA21.NT 1.0" "TestUser" 5 "Add License SNA21.NT 1.0"
+llscmd rpc llslicenseadd "SQL.NT 1.0" "TestUser" 5 "Add License SQL.NT 1.0"
+llscmd rpc llslicenseadd "SQL42B.OS2 1.0" "TestUser" 5 "Add License SQL42B.OS2 1.0"
+llscmd rpc llslicenseadd "SQLRES10.OS2 1.0" "TestUser" 5 "Add License SQLRES10.OS2 1.0"
+llscmd rpc llslicenseadd "WFW311.WIN 1.0" "TestUser" 5 "Add License WFW311.WIN 1.0"
+llscmd rpc llslicenseadd "WFWCONN.WIN 1.0" "TestUser" 5 "Add License WFWCONN.WIN 1.0"
+llscmd rpc llslicenseadd "WIN31 1.0" "TestUser" 5 "Add License WIN31 1.0"
+llscmd rpc llslicenseadd "WING10.WIN 1.0" "TestUser" 5 "Add License WING10.WIN 1.0"
+llscmd rpc llslicenseadd "winlogin.win 1.0" "TestUser" 5 "Add License winlogin.win 1.0"
+llscmd rpc llslicenseadd "WINNT.NT 1.0" "TestUser" 5 "Add License WINNT.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.SRV 1.0" "TestUser" 5 "Add License WINNT35.SRV 1.0"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 1.0" "TestUser" 5 "Add License WMDLRSDK.WIN 1.0"
+llscmd rpc llslicenseadd "GOLF20.WIN 1.0" "TestUser" 5 "Add License GOLF20.WIN 1.0"
+llscmd rpc llslicenseadd "GRAPH50.WIN 1.0" "TestUser" 5 "Add License GRAPH50.WIN 1.0"
+llscmd rpc llslicenseadd "GREETING.WIN 1.0" "TestUser" 5 "Add License GREETING.WIN 1.0"
+llscmd rpc llslicenseadd "VC20A.NT 1.0" "TestUser" 5 "Add License VC20A.NT 1.0"
+llscmd rpc llslicenseadd "WINNT35.WKS 1.0" "TestUser" 5 "Add License WINNT35.WKS 1.0"
+llscmd rpc llslicenseadd "wpen100a.win 1.0" "TestUser" 5 "Add License wpen100a.win 1.0"
+llscmd rpc llslicenseadd "WPS.WIN 1.0" "TestUser" 5 "Add License WPS.WIN 1.0"
+llscmd rpc llslicenseadd "TAPI10.WIN 1.0" "TestUser" 5 "Add License TAPI10.WIN 1.0"
+llscmd rpc llslicenseadd "TCPIP.ALL 1.0" "TestUser" 5 "Add License TCPIP.ALL 1.0"
+llscmd rpc llslicenseadd "TCPUTIL1.0 1.0" "TestUser" 5 "Add License TCPUTIL1.0 1.0"
+llscmd rpc llslicenseadd "VFW11.WIN 1.0" "TestUser" 5 "Add License VFW11.WIN 1.0"
+llscmd rpc llslicenseadd "WRESKIT2.00 1.0" "TestUser" 5 "Add License WRESKIT2.00 1.0"
+llscmd rpc llslicenseadd "WSS20.WIN 1.0" "TestUser" 5 "Add License WSS20.WIN 1.0"
+llscmd rpc llslicenseadd "MacWord 1.0" "TestUser" 5 "Add License MacWord 1.0"
+llscmd rpc llslicenseadd "PowerPoint 1.0" "TestUser" 5 "Add License PowerPoint 1.0"
+llscmd rpc llslicenseadd "BoundsChecker 1.0" "TestUser" 5 "Add License BoundsChecker 1.0"
+llscmd rpc llslicenseadd "Quattro 1.0" "TestUser" 5 "Add License Quattro 1.0"
+llscmd rpc llslicenseadd "WordPerfect 1.0" "TestUser" 5 "Add License WordPerfect 1.0"
+llscmd rpc llslicenseadd "Office 1.0" "TestUser" 5 "Add License Office 1.0"
+llscmd rpc llslicenseadd "PerfectOffice 1.0" "TestUser" 5 "Add License PerfectOffice 1.0"
+llscmd rpc llslicenseadd "SQL 1.0" "TestUser" 5 "Add License SQL 1.0"
diff --git a/private/net/svcdlls/lls/test/ct/lic2.bat b/private/net/svcdlls/lls/test/ct/lic2.bat
new file mode 100644
index 000000000..9fb7d805b
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lic2.bat
@@ -0,0 +1,186 @@
+llscmd rpc llslicenseadd "Money 3.1" "TestUser" 1 "Add License Money 3.1"
+llscmd rpc llslicenseadd "Schedule+ 3.1" "TestUser" 2 "Add License Schedule+ 3.1"
+llscmd rpc llslicenseadd "Mail 3.1" "TestUser" 3 "Add License Mail 3.1"
+llscmd rpc llslicenseadd "MONEY30.WIN 3.1" "TestUser" 4 "Add License MONEY30.WIN 3.1"
+llscmd rpc llslicenseadd "MP420A.DOS 3.1" "TestUser" 5 "Add License MP420A.DOS 3.1"
+llscmd rpc llslicenseadd "MSMAIL.ALL 3.1" "TestUser" 6 "Add License MSMAIL.ALL 3.1"
+llscmd rpc llslicenseadd "Win32 3.1" "TestUser" 7 "Add License Win32 3.1"
+llscmd rpc llslicenseadd "Win32s 3.1" "TestUser" 8 "Add License Win32s 3.1"
+llscmd rpc llslicenseadd "Excel 3.1" "TestUser" 9 "Add License Excel 3.1"
+llscmd rpc llslicenseadd "Word 3.1" "TestUser" 10 "Add License Word 3.1"
+llscmd rpc llslicenseadd "ARCADE10.WIN 3.1" "TestUser" 11 "Add License ARCADE10.WIN 3.1"
+llscmd rpc llslicenseadd "ARTGALL.WIN 3.1" "TestUser" 12 "Add License ARTGALL.WIN 3.1"
+llscmd rpc llslicenseadd "VCP100.WIN 3.1" "TestUser" 13 "Add License VCP100.WIN 3.1"
+llscmd rpc llslicenseadd "WINLIB 3.1" "TestUser" 14 "Add License WINLIB 3.1"
+llscmd rpc llslicenseadd "ACCESS20.WIN 3.1" "TestUser" 15 "Add License ACCESS20.WIN 3.1"
+llscmd rpc llslicenseadd "FORTRN32.WIN 3.1" "TestUser" 16 "Add License FORTRN32.WIN 3.1"
+llscmd rpc llslicenseadd "IMSL.NT 3.1" "TestUser" 17 "Add License IMSL.NT 3.1"
+llscmd rpc llslicenseadd "IMSL32.DOS 3.1" "TestUser" 18 "Add License IMSL32.DOS 3.1"
+llscmd rpc llslicenseadd "IMSL51.DOS 3.1" "TestUser" 19 "Add License IMSL51.DOS 3.1"
+llscmd rpc llslicenseadd "Chicago 3.1" "TestUser" 20 "Add License Chicago 3.1"
+llscmd rpc llslicenseadd "Windows95 3.1" "TestUser" 21 "Add License Windows95 3.1"
+llscmd rpc llslicenseadd "Access 3.1" "TestUser" 22 "Add License Access 3.1"
+llscmd rpc llslicenseadd "MASM611 3.1" "TestUser" 23 "Add License MASM611 3.1"
+llscmd rpc llslicenseadd "MFCKIT 3.1" "TestUser" 24 "Add License MFCKIT 3.1"
+llscmd rpc llslicenseadd "mt300a.all 3.1" "TestUser" 25 "Add License mt300a.all 3.1"
+llscmd rpc llslicenseadd "QWGRAPH 3.1" "TestUser" 26 "Add License QWGRAPH 3.1"
+llscmd rpc llslicenseadd "VB10.DOS 3.1" "TestUser" 27 "Add License VB10.DOS 3.1"
+llscmd rpc llslicenseadd "CBT 3.1" "TestUser" 28 "Add License CBT 3.1"
+llscmd rpc llslicenseadd "EISPAK11.WIN 3.1" "TestUser" 29 "Add License EISPAK11.WIN 3.1"
+llscmd rpc llslicenseadd "ENCART95.WIN 3.1" "TestUser" 30 "Add License ENCART95.WIN 3.1"
+llscmd rpc llslicenseadd "EXCEL30.PM 3.1" "TestUser" 31 "Add License EXCEL30.PM 3.1"
+llscmd rpc llslicenseadd "VB30.WIN 3.1" "TestUser" 32 "Add License VB30.WIN 3.1"
+llscmd rpc llslicenseadd "MASM 3.1" "TestUser" 33 "Add License MASM 3.1"
+llscmd rpc llslicenseadd "OLE 3.1" "TestUser" 34 "Add License OLE 3.1"
+llscmd rpc llslicenseadd "Chart 3.1" "TestUser" 35 "Add License Chart 3.1"
+llscmd rpc llslicenseadd "Encarta 3.1" "TestUser" 36 "Add License Encarta 3.1"
+llscmd rpc llslicenseadd "FlightSim 3.1" "TestUser" 37 "Add License FlightSim 3.1"
+llscmd rpc llslicenseadd "Paradox 3.1" "TestUser" 38 "Add License Paradox 3.1"
+llscmd rpc llslicenseadd "VC150.WIN 3.1" "TestUser" 39 "Add License VC150.WIN 3.1"
+llscmd rpc llslicenseadd "VC20.MAC 3.1" "TestUser" 40 "Add License VC20.MAC 3.1"
+llscmd rpc llslicenseadd "IMAGER10.WIN 3.1" "TestUser" 41 "Add License IMAGER10.WIN 3.1"
+llscmd rpc llslicenseadd "LRND300.DOS 3.1" "TestUser" 42 "Add License LRND300.DOS 3.1"
+llscmd rpc llslicenseadd "MMGOLF10.WIN 3.1" "TestUser" 43 "Add License MMGOLF10.WIN 3.1"
+llscmd rpc llslicenseadd "MOM.NT 3.1" "TestUser" 44 "Add License MOM.NT 3.1"
+llscmd rpc llslicenseadd "MSDOS622.DOS 3.1 " "TestUser" 45 "Add License MSDOS622.DOS 3.1"
+llscmd rpc llslicenseadd "MSKBD1.ALL 3.1" "TestUser" 46 "Add License MSKBD1.ALL 3.1"
+llscmd rpc llslicenseadd "MSLOGO.TTF 3.1" "TestUser" 47 "Add License MSLOGO.TTF 3.1"
+llscmd rpc llslicenseadd "MSSMS10 3.1" "TestUser" 48 "Add License MSSMS10 3.1"
+llscmd rpc llslicenseadd "MOM42.WIN 3.1" "TestUser" 49 "Add License MOM42.WIN 3.1"
+llscmd rpc llslicenseadd "SNA 3.1" "TestUser" 50 "Add License SNA 3.1"
+llscmd rpc llslicenseadd "Exchange 3.1" "TestUser" 51 "Add License Exchange 3.1"
+llscmd rpc llslicenseadd "MS_EFORM.WIN 3.1" "TestUser" 52 "Add License MS_EFORM.WIN 3.1"
+llscmd rpc llslicenseadd "ODAKIT 3.1" "TestUser" 53 "Add License ODAKIT 3.1"
+llscmd rpc llslicenseadd "ODBC210.WIN 3.1" "TestUser" 54 "Add License ODBC210.WIN 3.1"
+llscmd rpc llslicenseadd "VC20.NT 3.1" "TestUser" 55 "Add License VC20.NT 3.1"
+llscmd rpc llslicenseadd "BOB.WIN 3.1" "TestUser" 56 "Add License BOB.WIN 3.1"
+llscmd rpc llslicenseadd "BOOK94.WIN 3.1" "TestUser" 57 "Add License BOOK94.WIN 3.1"
+llscmd rpc llslicenseadd "BOWEP.WIN 3.1" "TestUser" 58 "Add License BOWEP.WIN 3.1"
+llscmd rpc llslicenseadd "EXCEL50.NT 3.1" "TestUser" 59 "Add License EXCEL50.NT 3.1"
+llscmd rpc llslicenseadd "EXCEL50C.WIN 3.1" "TestUser" 60 "Add License EXCEL50C.WIN 3.1"
+llscmd rpc llslicenseadd "FLTSIM5a.DOS 3.1" "TestUser" 61 "Add License FLTSIM5a.DOS 3.1"
+llscmd rpc llslicenseadd "FOXPR26A.DOS 3.1" "TestUser" 62 "Add License FOXPR26A.DOS 3.1"
+llscmd rpc llslicenseadd "FOXPR26A.WIN 3.1" "TestUser" 63 "Add License FOXPR26A.WIN 3.1"
+llscmd rpc llslicenseadd "FOXPRO26.UNX 3.1" "TestUser" 64 "Add License FOXPRO26.UNX 3.1"
+llscmd rpc llslicenseadd "WKGRP311.DOS 3.1" "TestUser" 65 "Add License WKGRP311.DOS 3.1"
+llscmd rpc llslicenseadd "wlo09 3.1" "TestUser" 66 "Add License wlo09 3.1"
+llscmd rpc llslicenseadd "wlo10 3.1" "TestUser" 67 "Add License wlo10 3.1"
+llscmd rpc llslicenseadd "ACCSOLPK.WIN 3.1" "TestUser" 68 "Add License ACCSOLPK.WIN 3.1"
+llscmd rpc llslicenseadd "ACCUPTLS.WIN 3.1" "TestUser" 69 "Add License ACCUPTLS.WIN 3.1"
+llscmd rpc llslicenseadd "ADT20.WIN 3.1" "TestUser" 70 "Add License ADT20.WIN 3.1"
+llscmd rpc llslicenseadd "GS100.DOS 3.1" "TestUser" 71 "Add License GS100.DOS 3.1"
+llscmd rpc llslicenseadd "ODBCDDP2.NT 3.1" "TestUser" 72 "Add License ODBCDDP2.NT 3.1"
+llscmd rpc llslicenseadd "ODBCDDP2.WIN 3.1" "TestUser" 73 "Add License ODBCDDP2.WIN 3.1"
+llscmd rpc llslicenseadd "OFF42C.WIN 3.1" "TestUser" 74 "Add License OFF42C.WIN 3.1"
+llscmd rpc llslicenseadd "OFF43c.WIN 3.1" "TestUser" 75 "Add License OFF43c.WIN 3.1"
+llscmd rpc llslicenseadd "OFFAST10.WIN 3.1" "TestUser" 76 "Add License OFFAST10.WIN 3.1"
+llscmd rpc llslicenseadd "OFFDK10.WIN 3.1" "TestUser" 77 "Add License OFFDK10.WIN 3.1"
+llscmd rpc llslicenseadd "FONTPACK.WIN 3.1" "TestUser" 78 "Add License FONTPACK.WIN 3.1"
+llscmd rpc llslicenseadd "FONTPAK2.WIN 3.1" "TestUser" 79 "Add License FONTPAK2.WIN 3.1"
+llscmd rpc llslicenseadd "HPFNTSET.WIN 3.1" "TestUser" 80 "Add License HPFNTSET.WIN 3.1"
+llscmd rpc llslicenseadd "hyper.100 3.1" "TestUser" 81 "Add License hyper.100 3.1"
+llscmd rpc llslicenseadd "IMSL.NT 3.1" "TestUser" 82 "Add License IMSL.NT 3.1"
+llscmd rpc llslicenseadd "IMSL32.DOS 3.1" "TestUser" 83 "Add License IMSL32.DOS 3.1"
+llscmd rpc llslicenseadd "LM22 3.1" "TestUser" 84 "Add License LM22 3.1"
+llscmd rpc llslicenseadd "LMRES10.OS2 3.1" "TestUser" 85 "Add License LMRES10.OS2 3.1"
+llscmd rpc llslicenseadd "LMSFMAC3.1A 3.1" "TestUser" 86 "Add License LMSFMAC3.1A 3.1"
+llscmd rpc llslicenseadd "OFFICE.NT 3.1" "TestUser" 87 "Add License OFFICE.NT 3.1"
+llscmd rpc llslicenseadd "OLE202.WIN 3.1" "TestUser" 88 "Add License OLE202.WIN 3.1"
+llscmd rpc llslicenseadd "ONLN200.WIN 3.1" "TestUser" 89 "Add License ONLN200.WIN 3.1"
+llscmd rpc llslicenseadd "PROJ40.WIN 3.1" "TestUser" 90 "Add License PROJ40.WIN 3.1"
+llscmd rpc llslicenseadd "MSTCPIP.ALL 3.1" "TestUser" 91 "Add License MSTCPIP.ALL 3.1"
+llscmd rpc llslicenseadd "MVWR200.WIN 3.1" "TestUser" 92 "Add License MVWR200.WIN 3.1"
+llscmd rpc llslicenseadd "NETMON 3.1" "TestUser" 93 "Add License NETMON 3.1"
+llscmd rpc llslicenseadd "OS2-1.31 3.1" "TestUser" 94 "Add License OS2-1.31 3.1"
+llscmd rpc llslicenseadd "PMSUB35.NT 3.1" "TestUser" 95 "Add License PMSUB35.NT 3.1"
+llscmd rpc llslicenseadd "RAS11.LM 3.1" "TestUser" 96 "Add License RAS11.LM 3.1"
+llscmd rpc llslicenseadd "PUB20a.WIN 3.1" "TestUser" 97 "Add License PUB20a.WIN 3.1"
+llscmd rpc llslicenseadd "PUBDSIGN.WIN 3.1" "TestUser" 98 "Add License PUBDSIGN.WIN 3.1"
+llscmd rpc llslicenseadd "SAMPLER 3.1" "TestUser" 99 "Add License SAMPLER 3.1"
+llscmd rpc llslicenseadd "SCENES20.WIN 3.1" "TestUser" 100 "Add License SCENES20.WIN 3.1"
+llscmd rpc llslicenseadd "SCHED10A.WIN 3.1" "TestUser" 101 "Add License SCHED10A.WIN 3.1"
+llscmd rpc llslicenseadd "SGML10.WIN 3.1" "TestUser" 102 "Add License SGML10.WIN 3.1"
+llscmd rpc llslicenseadd "SLM.ALL 3.1" "TestUser" 103 "Add License SLM.ALL 3.1"
+llscmd rpc llslicenseadd "SNDBIT10.WIN 3.1" "TestUser" 104 "Add License SNDBIT10.WIN 3.1"
+llscmd rpc llslicenseadd "SPACE10.DOS 3.1" "TestUser" 105 "Add License SPACE10.DOS 3.1"
+llscmd rpc llslicenseadd "SS30.DOS 3.1" "TestUser" 105 "Add License SS30.DOS 3.1"
+llscmd rpc llslicenseadd "ss31std.win 3.1" "TestUser" 105 "Add License ss31std.win 3.1"
+llscmd rpc llslicenseadd "WA100.WIN 3.1" "TestUser" 105 "Add License WA100.WIN 3.1"
+llscmd rpc llslicenseadd "WEP10B.WIN 3.1" "TestUser" 105 "Add License WEP10B.WIN 3.1"
+llscmd rpc llslicenseadd "WEP20.WIN 3.1" "TestUser" 115 "Add License WEP20.WIN 3.1"
+llscmd rpc llslicenseadd "WEP30.WIN 3.1" "TestUser" 115 "Add License WEP30.WIN 3.1"
+llscmd rpc llslicenseadd "WEP40.WIN 3.1" "TestUser" 115 "Add License WEP40.WIN 3.1"
+llscmd rpc llslicenseadd "WGTPLT11.WIN 3.1" "TestUser" 115 "Add License WGTPLT11.WIN 3.1"
+llscmd rpc llslicenseadd "WORD11B.PM 3.1" "TestUser" 115 "Add License WORD11B.PM 3.1"
+llscmd rpc llslicenseadd "WORD6.DOS 3.1" "TestUser" 115 "Add License WORD6.DOS 3.1"
+llscmd rpc llslicenseadd "WORD60.NT 3.1" "TestUser" 115 "Add License WORD60.NT 3.1"
+llscmd rpc llslicenseadd "WORD60C.WIN 3.1" "TestUser" 115 "Add License WORD60C.WIN 3.1"
+llscmd rpc llslicenseadd "word6cnv.nt 3.1" "TestUser" 115 "Add License word6cnv.nt 3.1"
+llscmd rpc llslicenseadd "WORD6CNV.WIN 3.1" "TestUser" 115 "Add License WORD6CNV.WIN 3.1"
+llscmd rpc llslicenseadd "WORDVW60.WIN 3.1" "TestUser" 125 "Add License WORDVW60.WIN 3.1"
+llscmd rpc llslicenseadd "WORKS30.DOS 3.1" "TestUser" 125 "Add License WORKS30.DOS 3.1"
+llscmd rpc llslicenseadd "WORKS30B.WIN 3.1" "TestUser" 125 "Add License WORKS30B.WIN 3.1"
+llscmd rpc llslicenseadd "WPP310.WIN 3.1" "TestUser" 125 "Add License WPP310.WIN 3.1"
+llscmd rpc llslicenseadd "WRITER10.WIN 3.1" "TestUser" 125 "Add License WRITER10.WIN 3.1"
+llscmd rpc llslicenseadd "3com 3.1" "TestUser" 125 "Add License 3com 3.1"
+llscmd rpc llslicenseadd "BLPNT901.ALL 3.1" "TestUser" 125 "Add License BLPNT901.ALL 3.1"
+llscmd rpc llslicenseadd "ARTIST10.WIN 3.1" "TestUser" 125 "Add License ARTIST10.WIN 3.1"
+llscmd rpc llslicenseadd "BASEBL94.WIN 3.1" "TestUser" 125 "Add License BASEBL94.WIN 3.1"
+llscmd rpc llslicenseadd "BA331.DOS 3.1" "TestUser" 125 "Add License BA331.DOS 3.1"
+llscmd rpc llslicenseadd "OS/2 3.1" "TestUser" 135 "Add License OS/2 3.1"
+llscmd rpc llslicenseadd "NetWare 3.1" "TestUser" 135 "Add License NetWare 3.1"
+llscmd rpc llslicenseadd "DOS 3.1" "TestUser" 135 "Add License DOS 3.1"
+llscmd rpc llslicenseadd "C 3.1" "TestUser" 135 "Add License C 3.1"
+llscmd rpc llslicenseadd "CHART300.DOS 3.1" "TestUser" 135 "Add License CHART300.DOS 3.1"
+llscmd rpc llslicenseadd "CINEMA94.WIN 3.1" "TestUser" 135 "Add License CINEMA94.WIN 3.1"
+llscmd rpc llslicenseadd "DELTA10A.WIN 3.1" "TestUser" 135 "Add License DELTA10A.WIN 3.1"
+llscmd rpc llslicenseadd "DINOSAUR.WIN 3.1" "TestUser" 135 "Add License DINOSAUR.WIN 3.1"
+llscmd rpc llslicenseadd "DRAW10.WIN 3.1" "TestUser" 135 "Add License DRAW10.WIN 3.1"
+llscmd rpc llslicenseadd "COBOL500 3.1" "TestUser" 135 "Add License COBOL500 3.1"
+llscmd rpc llslicenseadd "FORT510A.DOS 3.1" "TestUser" 145 "Add License FORT510A.DOS 3.1"
+llscmd rpc llslicenseadd "FORTRN32.NT 3.1" "TestUser" 145 "Add License FORTRN32.NT 3.1"
+llscmd rpc llslicenseadd "VC21.NT 3.1" "TestUser" 145 "Add License VC21.NT 3.1"
+llscmd rpc llslicenseadd "cdex 3.1" "TestUser" 145 "Add License cdex 3.1"
+llscmd rpc llslicenseadd "CLIENTS.NET 3.1" "TestUser" 145 "Add License CLIENTS.NET 3.1"
+llscmd rpc llslicenseadd "CMSRVWS.11 3.1" "TestUser" 145 "Add License CMSRVWS.11 3.1"
+llscmd rpc llslicenseadd "DCIDDK10 3.1" "TestUser" 145 "Add License DCIDDK10 3.1"
+llscmd rpc llslicenseadd "PPT40c.WIN 3.1" "TestUser" 145 "Add License PPT40c.WIN 3.1"
+llscmd rpc llslicenseadd "PROFIT1B.WIN 3.1" "TestUser" 145 "Add License PROFIT1B.WIN 3.1"
+llscmd rpc llslicenseadd "PROJ40.DOS 3.1" "TestUser" 145 "Add License PROJ40.DOS 3.1"
+llscmd rpc llslicenseadd "LMUNIX 3.1" "TestUser" 155 "Add License LMUNIX 3.1"
+llscmd rpc llslicenseadd "MOUSE10a.ALL 3.1" "TestUser" 155 "Add License MOUSE10a.ALL 3.1"
+llscmd rpc llslicenseadd "MSD211.DOS 3.1" "TestUser" 155 "Add License MSD211.DOS 3.1"
+llscmd rpc llslicenseadd "RESKIT.NT 3.1" "TestUser" 155 "Add License RESKIT.NT 3.1"
+llscmd rpc llslicenseadd "SNA21.NT 3.1" "TestUser" 155 "Add License SNA21.NT 3.1"
+llscmd rpc llslicenseadd "SQL.NT 3.1" "TestUser" 155 "Add License SQL.NT 3.1"
+llscmd rpc llslicenseadd "SQL42B.OS2 3.1" "TestUser" 155 "Add License SQL42B.OS2 3.1"
+llscmd rpc llslicenseadd "SQLRES10.OS2 3.1" "TestUser" 155 "Add License SQLRES10.OS2 3.1"
+llscmd rpc llslicenseadd "WFW311.WIN 3.1" "TestUser" 155 "Add License WFW311.WIN 3.1"
+llscmd rpc llslicenseadd "WFWCONN.WIN 3.1" "TestUser" 155 "Add License WFWCONN.WIN 3.1"
+llscmd rpc llslicenseadd "WIN31 3.1" "TestUser" 165 "Add License WIN31 3.1"
+llscmd rpc llslicenseadd "WING10.WIN 3.1" "TestUser" 165 "Add License WING10.WIN 3.1"
+llscmd rpc llslicenseadd "winlogin.win 3.1" "TestUser" 165 "Add License winlogin.win 3.1"
+llscmd rpc llslicenseadd "WINNT.NT 3.1" "TestUser" 165 "Add License WINNT.NT 3.1"
+llscmd rpc llslicenseadd "WINNT35.SRV 3.1" "TestUser" 165 "Add License WINNT35.SRV 3.1"
+llscmd rpc llslicenseadd "WMDLRSDK.WIN 3.1" "TestUser" 165 "Add License WMDLRSDK.WIN 3.1"
+llscmd rpc llslicenseadd "GOLF20.WIN 3.1" "TestUser" 165 "Add License GOLF20.WIN 3.1"
+llscmd rpc llslicenseadd "GRAPH50.WIN 3.1" "TestUser" 165 "Add License GRAPH50.WIN 3.1"
+llscmd rpc llslicenseadd "GREETING.WIN 3.1" "TestUser" 165 "Add License GREETING.WIN 3.1"
+llscmd rpc llslicenseadd "VC20A.NT 3.1" "TestUser" 165 "Add License VC20A.NT 3.1"
+llscmd rpc llslicenseadd "WINNT35.WKS 3.1" "TestUser" 175 "Add License WINNT35.WKS 3.1"
+llscmd rpc llslicenseadd "wpen100a.win 3.1" "TestUser" 175 "Add License wpen100a.win 3.1"
+llscmd rpc llslicenseadd "WPS.WIN 3.1" "TestUser" 175 "Add License WPS.WIN 3.1"
+llscmd rpc llslicenseadd "TAPI10.WIN 3.1" "TestUser" 175 "Add License TAPI10.WIN 3.1"
+llscmd rpc llslicenseadd "TCPIP.ALL 3.1" "TestUser" 175 "Add License TCPIP.ALL 3.1"
+llscmd rpc llslicenseadd "TCPUTIL3.1 3.1" "TestUser" 175 "Add License TCPUTIL3.1 3.1"
+llscmd rpc llslicenseadd "VFW11.WIN 3.1" "TestUser" 175 "Add License VFW11.WIN 3.1"
+llscmd rpc llslicenseadd "WRESKIT2.00 3.1" "TestUser" 175 "Add License WRESKIT2.00 3.1"
+llscmd rpc llslicenseadd "WSS20.WIN 3.1" "TestUser" 175 "Add License WSS20.WIN 3.1"
+llscmd rpc llslicenseadd "MacWord 3.1" "TestUser" 175 "Add License MacWord 3.1"
+llscmd rpc llslicenseadd "PowerPoint 3.1" "TestUser" 185 "Add License PowerPoint 3.1"
+llscmd rpc llslicenseadd "BoundsChecker 3.1" "TestUser" 185 "Add License BoundsChecker 3.1"
+llscmd rpc llslicenseadd "Quattro 3.1" "TestUser" 185 "Add License Quattro 3.1"
+llscmd rpc llslicenseadd "WordPerfect 3.1" "TestUser" 185 "Add License WordPerfect 3.1"
+llscmd rpc llslicenseadd "Office 3.1" "TestUser" 185 "Add License Office 3.1"
+llscmd rpc llslicenseadd "PerfectOffice 3.1" "TestUser" 185 "Add License PerfectOffice 3.1"
+llscmd rpc llslicenseadd "SQL 3.1" "TestUser" 185 "Add License SQL 3.1"
diff --git a/private/net/svcdlls/lls/test/ct/license.txt b/private/net/svcdlls/lls/test/ct/license.txt
new file mode 100644
index 000000000..04ba5362e
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/license.txt
@@ -0,0 +1,248 @@
+License Logging Service
+Last Update: 11-21-94 6:30pm
+by: arth
+
+What is this licensing Stuff?
+------------------------------
+
+All back-office applications, and all the server-services will now track
+useage and check for license compliance. There will be two licensing modes:
+
+ 1. Per Seat - A per workstation license for the server services on a
+ network. This basically counts up how many unique users (based on
+ the username) have ever used the service and warns the admin when the
+ license limit has been reached.
+ 2. Concurrent - A maximum simultaneous user limit on a server. I.E. Limit
+ the number of simultaneous sessions that can connect to a service.
+
+Any server-service or back-office application can use either of these
+licensing modes on any particular server as selected by the admin. Therefore
+on the same server SQL could be running in Per Seat mode and RAS could be
+running in Concurrent licensing mode.
+
+File and print services (SMB Server, FPNW Server, Print Service, MAC) will use
+a shared pool of licenses in Concurrent licensing mode. Therefore if
+File and Print licensing limit is set to 100 and 50 people are using the SMB
+server, and 50 people are using the FPNW server then no more users can access
+any of the file and print services.
+
+During installation the setup program will ask what mode of licensing the
+admin is using, and if using Concurrent licensing, what session limit to
+enforce (I.E. how many licenses the admin has bought). The NT setup program
+will do this for file and print services. The back-office applications
+(SQL, SNA, etc..) will handle this license configuration in their individual
+setup programs.
+
+What must I do?
+---------------
+
+First you will need the header and libs from NT build 856 or later to get the
+latest license API and LsaLogonUser changes.
+
+If you have your own installation/setup program then you need to put up
+a dialog to check for the mode of licensing and session limit. Thomaspa's
+group is working on a DLL and common dialog for this. This information is
+written out to the registry.
+
+In the service itself there are basically three different options depending
+on what your needs are:
+
+ 1. Let LsaLogonUser handle licensing for you.
+ 2. Directly call the License API's and have it handle all licensing for you.
+ 3. Handle Concurrent based licensing yourself and call the License API's
+ for Per Seat licensing.
+
+1. If you call LsaLogonUser to validate connections then it is fairly easy.
+First you need to call LSALogonUser with LSA_CALL_LICENSE_SERVER
+(from sdk\inc\ntlsa.h) or'd into the AuthenticationParameter. This tells
+LsaLogonUser to call the license service, the default is to not call the
+Licensing service.
+
+With this bit set, LsaLogonUser will call the license API for you, and the
+License API will determine what mode of licensing should be used and act
+appropriatly.
+
+You must also check the return from LsaLogonUser for the new return
+STATUS_LICENSE_QUOTA_EXCEEDED. This means the license limit was exceeded
+and you should not let the user on.
+
+ Note: When the token received from LsaLogonUser is freed the License
+ service will free the session. LsaLogonUser already returns a token and
+ expects the code to free it, so no changes should be required in your code
+ for this.
+
+2. If you call the Licensing API's directly the changes are also fairly easy.
+There are two API's NtLicenseRequest and NtLicenseFree (from sdk\inc\ntlsapi.h)
+Call NtLicenseRequest when a session is established and NtLicenseFree when a
+session is terminated.
+
+This method is used by the SMB server because at the time that it makes the
+call to LsaLogonUser the service doesn't know if the connection is for
+file i/o or pipe i/o, and pipe i/o isn't supposed to be licensed.
+
+The Licensing API will determine what type of licensing mode should be used
+and act accordingly without any special code in the service.
+
+3. Doing it this way means you handle the Concurrent licensing yourself.
+You must read from the registry the licensing mode and if in Concurrent
+licensing mode you must track the session limits and reject users. You
+should also have a background thread that periodically checks the registry
+to see if the licensing info has changed. Only call the Licensing API's
+if you are in Per Seat licensing mode.
+
+SQL does it this way as currently SQL tracks session connection and tear
+down in a way that is difficult to integrate with the licensing API, and it
+is easier for them to do it themselves.
+
+Note: If your service is one of the "file and print services" then you *MUST*
+use one of the other options and let the License API handle Concurrent
+Licensing. This allows the License API code to limit the file and print
+services using a shared pool of licenses.
+
+I need to call the Licensing API directly, how do I do this?
+------------------------------------------------------------
+
+First, let me (arth) know that you need to call the API directly so if there
+are any changes then I can let you know about them.
+
+The header file is in %NT_ROOT%\public\sdk\inc\ntlsapi.h
+The DLL is in %NT_ROOT%\public\sdk\lib\*\ntlsapi.dll
+
+The only two function calls you care about are:
+
+LS_STATUS_CODE LS_API_ENTRY NtLicenseRequest(
+ LPSTR ProductName,
+ LPSTR Version,
+ LS_HANDLE FAR *LicenseHandle,
+ NT_LS_DATA *NtData);
+
+
+LS_STATUS_CODE LS_API_ENTRY NtLSFreeHandle(
+ LS_HANDLE LicenseHandle );
+
+There are currently only three return codes you need to worry about:
+
+ LS_SUCCESS - Everything worked
+ LS_INSUFFICIENT_UNITS - The license limit was exceeded (reject user)
+ LS_RESOURCES_UNAVAILABLE - Out of memory
+
+The NtData field is used to pass in the username or SID and is defined as:
+
+typedef struct {
+ ULONG DataType; // Type of the following data, ie. user name, sid...
+ VOID *Data; // Actual data. username, sid, etc...
+ // if call the unicode API character data
+ // must be in unicode as well
+ BOOL IsAdmin;
+} NT_LS_DATA;
+
+Set DataType to NT_LS_USER_NAME (defined in ntlsapi.h) if the Data field will
+contain a username, and NT_LS_USER_SID if it will contain a SID.
+Note: Please pass in a username if possible as it is quicker.
+
+IsAdmin is a bool used to tell if the user is an Admin. This allows an admin
+to still get a connection if the session limit is reached. If you don't want
+or care if an admin is allowed to connect when the session limit is reached,
+then just always pass in FALSE for this field.
+
+A typical call would therefore look something like:
+
+{
+ NT_LS_DATA LsData;
+ LS_STATUS_CODE err;
+ LS_HANDLE LicenseHandle;
+
+ ...
+
+ LsData.DataType = NT_LS_USER_NAME;
+ LsData.Data = (VOID *) MyUserNameField; // wherever you keep the username
+ LsData.IsAdmin = FALSE; // or whatever appropriate in your case.
+
+ err = NtLicenseRequest("Microsoft SQL Server", // Your service name
+ "4.0", // Version of product
+ &LicenseHandle,
+ &LsData);
+
+ switch (err) {
+ case LS_SUCCESS:
+ // Go ahead and do what you want
+ break;
+
+ case LS_INSUFFICIENT_UNITS:
+ // Disallow user connection
+ break;
+
+ case LS_RESOURCES_UNAVAILABLE:
+ // License Service got an out of memory so report error as approp.
+ break;
+ }
+
+
+ ...
+
+ NtLSFreeHandle(LicenseHandle);
+
+ ...
+
+}
+
+ANSI and Unicode endpoints are both provided. The Data field values should
+be in ANSI if calling the ANSI API's and Unicode if calling the Unicode API's.
+
+My server is in kernel mode, how do I call these API's?
+-------------------------------------------------------
+
+All kernel mode servers should have a user-mode service (at least when I
+checked before they all did). You need to thunk up to your user-mode
+service and then have it make the API call.
+
+If this causes a big problem then let me know. The License API DLL will need
+to make an LPC call to the License Service so when this is coded you could
+just make the LPC call directly, but sticking with using the DLL makes it
+easier to change things in the future.
+
+
+My server is connectionless, and using the Licensing API's isn't feasible.
+--------------------------------------------------------------------------
+
+Send me (arth) mail, these have to be handled on a case by case basis. Some
+of the internet servers have this problem and the current plan is to only call
+the licensing API's for authenticated connections.
+
+I'm handling Concurrent licensing Directly, what is the registry format?
+------------------------------------------------------------------------
+
+The following is the format that server apps should use for setting and
+checking the mode they operate in:
+
+ Key = \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LicenseInfo
+ Value= ErrorControl : REG_DWORD : 0x1
+ Value= Start : REG_DWORD : 0x3
+ Value= Type : REG_DWORD : 0x4
+
+Subkeys :
+
+ \SNA
+ \SQL
+ \FilePrint
+
+Value for All Subkeys=
+ Mode : REG_DWORD : (0x0 = Per Seat Mode, 0x1 = Concurrent/Per Server Mode)
+ ConcurrentLimit : REG_DWORD : (0x<limit>,
+ ie. 0x100 = 256 concurrent user limit)
+ FlipAllow : REG_DWORD : (0x0 = can change license mode, 0x1 license mode
+ can't be changed. Server apps are only allowed to
+ switch their license mode once, so after the first
+ switch, this value would be set to non-zero, then
+ the UI will warn about further changes to the
+ licence mode.)
+
+Issue: Server/service apps should poll the value every hour or so in case of
+change. Otherwise, if more licenses are added, or the mode changes, is it
+acceptable to require the server apps to be stopped, and restarted?
+
+
+What is the timeline for this stuff?
+------------------------------------
+
+These changes need to be in the servers by the PPC release.
diff --git a/private/net/svcdlls/lls/test/ct/lls.doc b/private/net/svcdlls/lls/test/ct/lls.doc
new file mode 100644
index 000000000..8ddad2ee0
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/lls.doc
Binary files differ
diff --git a/private/net/svcdlls/lls/test/ct/manyp.bat b/private/net/svcdlls/lls/test/ct/manyp.bat
new file mode 100644
index 000000000..b23a3e206
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/manyp.bat
@@ -0,0 +1,76 @@
+@echo off
+llscmd add user manyp prod1 4.0
+llscmd add user manyp prod1 3.0
+llscmd add user manyp prod1 1.0
+llscmd add user manyp prod1 6.0
+llscmd add user manyp prod1 8.0
+llscmd add user manyp prod1 9.0
+llscmd add user manyp prod1 2.0
+llscmd add user manyp prod1 5.0
+llscmd add user manyp prod1 7.0
+llscmd add user manyp prod1 4.2
+llscmd add user manyp prod1 3.3
+llscmd add user manyp prod1 1.5
+llscmd add user manyp prod1 6.1
+llscmd add user manyp prod1 8.7
+llscmd add user manyp prod1 9.1
+llscmd add user manyp prod1 2.7
+llscmd add user manyp prod1 5.3
+llscmd add user manyp prod1 7.1
+
+llscmd add user manyp prodx 4.0
+llscmd add user manyp prodx 3.0
+llscmd add user manyp prodx 1.0
+llscmd add user manyp prodx 6.0
+llscmd add user manyp prodx 8.0
+llscmd add user manyp prodx 9.0
+llscmd add user manyp prodx 2.0
+llscmd add user manyp prodx 5.0
+llscmd add user manyp prodx 7.0
+llscmd add user manyp prodx 4.2
+llscmd add user manyp prodx 3.3
+llscmd add user manyp prodx 1.5
+llscmd add user manyp prodx 6.1
+llscmd add user manyp prodx 8.7
+llscmd add user manyp prodx 9.1
+llscmd add user manyp prodx 2.7
+llscmd add user manyp prodx 5.3
+llscmd add user manyp prodx 7.1
+
+llscmd add user manyp proda 4.0
+llscmd add user manyp proda 3.0
+llscmd add user manyp proda 1.0
+llscmd add user manyp proda 6.0
+llscmd add user manyp proda 8.0
+llscmd add user manyp proda 9.0
+llscmd add user manyp proda 2.0
+llscmd add user manyp proda 5.0
+llscmd add user manyp proda 7.0
+llscmd add user manyp proda 4.2
+llscmd add user manyp proda 3.3
+llscmd add user manyp proda 1.5
+llscmd add user manyp proda 6.1
+llscmd add user manyp proda 8.7
+llscmd add user manyp proda 9.1
+llscmd add user manyp proda 2.7
+llscmd add user manyp proda 5.3
+llscmd add user manyp proda 7.1
+
+llscmd add user manyp prodv 4.0
+llscmd add user manyp prodd 3.0
+llscmd add user manyp prodw 1.0
+llscmd add user manyp prod3 6.0
+llscmd add user manyp prod7 8.0
+llscmd add user manyp prodh 9.0
+llscmd add user manyp prodj 2.0
+llscmd add user manyp prodk 5.0
+llscmd add user manyp prodl 7.0
+llscmd add user manyp prods 4.2
+llscmd add user manyp prod2 3.3
+llscmd add user manyp prod4 1.5
+llscmd add user manyp prodv 6.1
+llscmd add user manyp prodf 8.7
+llscmd add user manyp prody 9.1
+llscmd add user manyp produ 2.7
+llscmd add user manyp prod8 5.3
+llscmd add user manyp prod9 7.1
diff --git a/private/net/svcdlls/lls/test/ct/map1.bat b/private/net/svcdlls/lls/test/ct/map1.bat
new file mode 100644
index 000000000..d6dd4c551
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/map1.bat
@@ -0,0 +1,95 @@
+llscmd rpc llsmappingadd BigMap1 1 "Map - BigMap1"
+llscmd rpc llsmappingadd BigMap2 2 "Map - BigMap2"
+llscmd rpc llsmappingadd BigMap3 3 "Map - BigMap3"
+llscmd rpc llsmappingadd BigMap4 4 "Map - BigMap4"
+llscmd rpc llsmappingadd BigMap5 5 "Map - BigMap5"
+llscmd rpc llsmappingadd BigMap6 6 "Map - BigMap6"
+llscmd rpc llsmappingadd BigMap7 7 "Map - BigMap7"
+llscmd rpc llsmappingadd BigMap8 8 "Map - BigMap8"
+llscmd rpc llsmappingadd BigMap9 9 "Map - BigMap9"
+llscmd rpc llsmappingadd BigMap10 10 "Map - BigMap10"
+llscmd rpc llsmappingadd BigMap11 11 "Map - BigMap11"
+llscmd rpc llsmappingadd BigMap12 12 "Map - BigMap12"
+llscmd rpc llsmappingadd BigMap13 13 "Map - BigMap13"
+llscmd rpc llsmappingadd BigMap14 14 "Map - BigMap14"
+llscmd rpc llsmappingadd BigMap15 15 "Map - BigMap15"
+llscmd rpc llsmappingadd BigMap16 16 "Map - BigMap16"
+llscmd rpc llsmappingadd BigMap17 17 "Map - BigMap17"
+llscmd rpc llsmappingadd BigMap18 18 "Map - BigMap18"
+llscmd rpc llsmappingadd BigMap19 19 "Map - BigMap19"
+llscmd rpc llsmappingadd BigMap20 20 "Map - BigMap20"
+llscmd rpc llsmappingadd BigMap21 21 "Map - BigMap21"
+llscmd rpc llsmappingadd BigMap22 22 "Map - BigMap22"
+llscmd rpc llsmappingadd BigMap23 23 "Map - BigMap23"
+llscmd rpc llsmappingadd BigMap24 24 "Map - BigMap24"
+llscmd rpc llsmappingadd BigMap25 25 "Map - BigMap25"
+llscmd rpc llsmappingadd BigMap26 26 "Map - BigMap26"
+llscmd rpc llsmappingadd BigMap27 27 "Map - BigMap27"
+llscmd rpc llsmappingadd BigMap28 28 "Map - BigMap28"
+llscmd rpc llsmappingadd BigMap29 29 "Map - BigMap29"
+llscmd rpc llsmappingadd BigMap30 30 "Map - BigMap30"
+llscmd rpc llsmappingadd BigMap31 31 "Map - BigMap31"
+llscmd rpc llsmappingadd BigMap32 32 "Map - BigMap32"
+llscmd rpc llsmappingadd BigMap33 33 "Map - BigMap33"
+llscmd rpc llsmappingadd BigMap34 34 "Map - BigMap34"
+llscmd rpc llsmappingadd BigMap35 35 "Map - BigMap35"
+llscmd rpc llsmappingadd BigMap36 36 "Map - BigMap36"
+llscmd rpc llsmappingadd BigMap37 37 "Map - BigMap37"
+llscmd rpc llsmappingadd BigMap38 38 "Map - BigMap38"
+llscmd rpc llsmappingadd BigMap39 39 "Map - BigMap39"
+llscmd rpc llsmappingadd BigMap40 40 "Map - BigMap40"
+llscmd rpc llsmappingadd BigMap41 41 "Map - BigMap41"
+llscmd rpc llsmappingadd BigMap42 42 "Map - BigMap42"
+llscmd rpc llsmappingadd BigMap43 43 "Map - BigMap43"
+llscmd rpc llsmappingadd BigMap44 44 "Map - BigMap44"
+llscmd rpc llsmappingadd BigMap45 45 "Map - BigMap45"
+llscmd rpc llsmappingadd BigMap46 46 "Map - BigMap46"
+llscmd rpc llsmappingadd BigMap47 47 "Map - BigMap47"
+llscmd rpc llsmappingadd BigMap48 48 "Map - BigMap48"
+llscmd rpc llsmappingadd BigMap49 49 "Map - BigMap49"
+llscmd rpc llsmappingadd BigMap50 50 "Map - BigMap50"
+llscmd rpc llsmappingadd BigMap51 51 "Map - BigMap51"
+llscmd rpc llsmappingadd BigMap52 52 "Map - BigMap52"
+llscmd rpc llsmappingadd BigMap53 53 "Map - BigMap53"
+llscmd rpc llsmappingadd BigMap54 54 "Map - BigMap54"
+llscmd rpc llsmappingadd BigMap55 55 "Map - BigMap55"
+llscmd rpc llsmappingadd BigMap56 56 "Map - BigMap56"
+llscmd rpc llsmappingadd BigMap57 57 "Map - BigMap57"
+llscmd rpc llsmappingadd BigMap58 58 "Map - BigMap58"
+llscmd rpc llsmappingadd BigMap59 59 "Map - BigMap59"
+llscmd rpc llsmappingadd BigMap60 60 "Map - BigMap60"
+llscmd rpc llsmappingadd BigMap61 61 "Map - BigMap61"
+llscmd rpc llsmappingadd BigMap62 62 "Map - BigMap62"
+llscmd rpc llsmappingadd BigMap63 63 "Map - BigMap63"
+llscmd rpc llsmappingadd BigMap64 64 "Map - BigMap64"
+llscmd rpc llsmappingadd BigMap65 65 "Map - BigMap65"
+llscmd rpc llsmappingadd BigMap66 66 "Map - BigMap66"
+llscmd rpc llsmappingadd BigMap67 67 "Map - BigMap67"
+llscmd rpc llsmappingadd BigMap68 68 "Map - BigMap68"
+llscmd rpc llsmappingadd BigMap69 69 "Map - BigMap69"
+llscmd rpc llsmappingadd BigMap70 70 "Map - BigMap70"
+llscmd rpc llsmappingadd BigMap71 71 "Map - BigMap71"
+llscmd rpc llsmappingadd BigMap72 72 "Map - BigMap72"
+llscmd rpc llsmappingadd BigMap73 73 "Map - BigMap73"
+llscmd rpc llsmappingadd BigMap74 74 "Map - BigMap74"
+llscmd rpc llsmappingadd BigMap75 75 "Map - BigMap75"
+llscmd rpc llsmappingadd BigMap76 76 "Map - BigMap76"
+llscmd rpc llsmappingadd BigMap77 77 "Map - BigMap77"
+llscmd rpc llsmappingadd BigMap78 78 "Map - BigMap78"
+llscmd rpc llsmappingadd BigMap79 79 "Map - BigMap79"
+llscmd rpc llsmappingadd BigMap80 80 "Map - BigMap80"
+llscmd rpc llsmappingadd BigMap81 81 "Map - BigMap81"
+llscmd rpc llsmappingadd BigMap82 82 "Map - BigMap82"
+llscmd rpc llsmappingadd BigMap83 83 "Map - BigMap83"
+llscmd rpc llsmappingadd BigMap84 84 "Map - BigMap84"
+llscmd rpc llsmappingadd BigMap85 85 "Map - BigMap85"
+llscmd rpc llsmappingadd BigMap86 86 "Map - BigMap86"
+llscmd rpc llsmappingadd BigMap87 87 "Map - BigMap87"
+llscmd rpc llsmappingadd BigMap88 88 "Map - BigMap88"
+llscmd rpc llsmappingadd BigMap89 89 "Map - BigMap89"
+llscmd rpc llsmappingadd BigMap90 90 "Map - BigMap90"
+llscmd rpc llsmappingadd BigMap91 91 "Map - BigMap91"
+llscmd rpc llsmappingadd BigMap92 92 "Map - BigMap92"
+llscmd rpc llsmappingadd BigMap93 93 "Map - BigMap93"
+llscmd rpc llsmappingadd BigMap94 94 "Map - BigMap94"
+llscmd rpc llsmappingadd BigMap95 95 "Map - BigMap95"
diff --git a/private/net/svcdlls/lls/test/ct/mp.bat b/private/net/svcdlls/lls/test/ct/mp.bat
new file mode 100644
index 000000000..6cb912cf7
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/mp.bat
@@ -0,0 +1,14 @@
+:loop
+start llscmd add file n.dat 0 %1 %2 2
+start llscmd add file n1.dat 0 %1 %2 2
+start llscmd add file n2.dat 0 %1 %2 2
+start llscmd add file n3.dat 0 %1 %2 2
+start llscmd add file n4.dat 0 %1 %2 2
+start llscmd add file n5.dat 0 %1 %2 2
+start llscmd add file n6.dat 0 %1 %2 2
+start llscmd add file n7.dat 0 %1 %2 2
+start llscmd add file n8.dat 0 %1 %2 2
+start llscmd add file n9.dat 0 %1 %2 2
+start llscmd add file n10.dat 0 %1 %2 2
+sleep 900
+goto loop
diff --git a/private/net/svcdlls/lls/test/ct/n.dat b/private/net/svcdlls/lls/test/ct/n.dat
new file mode 100644
index 000000000..1b1e5a0f1
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n.dat
@@ -0,0 +1,1689 @@
+h-ouse
+-ronaar
+r-rja
+n-txfa
+nin-aa
+cab-ram
+joh-nac
+martad
+acca
+rrea
+markad
+acma
+byrona
+yosha
+julioa
+ccaa
+bertag
+ibmma
+acm-ah
+nor-iya
+hakana
+lisac
+johnal
+melisa
+boba
+rayall
+deba
+je'ffal
+rallen
+l-aural
+lallyn
+venesa
+an-naal
+nancya
+din-oa
+bain
+marioa
+r-aquea
+vitora
+s-cotam
+renam
+e'glea
+audrea
+chrisa
+ntxha
+curta
+davand
+dav'ean
+ntxga
+gavana
+ac'ja
+j_od
+attma
+naomia
+r_oy-and
+sallya
+toman
+m-a'tsan
+jamiea
+r--ickan
+jana
+je'-ffan
+jiman
+bevaok
+takaya
+y'-'-umia
+ca-rloa
+takaar
+shinar
+yukaa
+tonyar
+hiroar
+suza
+kenar
+eadarm
+loria
+nt-xra
+richar
+carnst
+arnulw
+mayuma
+hidas
+richat
+idoa
+ntxbat
+markat
+kevana
+gillea
+micha
+joset
+ntxba
+gma
+saorib
+vikkib
+garyba
+suz-ieb
+michba
+danbag
+frankb
+larryb
+sbaird
+rrkeb
+jolynb
+paulb
+sbaker
+valb
+leeb
+geoba
+dougba
+ctpsb
+hughb
+galb
+leonib
+patba
+ntxjb
+rrjba
+donbar
+jacbar
+roberb
+larrb
+allpak
+mosesb
+donnab
+cibelb
+vspb
+peteba
+abates
+marcb
+pbcnz
+jbattl
+jimbau
+cbaum
+danbau
+bilbax
+kathbe
+ntx'-ab
+celinb
+karenb
+philb
+darryb
+ctpdb
+scre9
+ccsb
+ste'pbe
+ccjben
+leneb
+ntxnb
+pbenoi
+vscb
+micheb
+rrjimb
+chadb
+kar-ib
+nat-hb
+dou-gbe
+kimmob
+amyb
+decdb
+ebenb
+acabe
+andb
+ntxsbe
+michbe
+geralb
+juditb
+savlob
+dsiwm
+willbi
+ellenb
+joseb
+bla-sb
+thomb
+annicb
+marbin
+ntxmb
+daveb
+dirkb
+suebi
+stefb
+patbi
+hilmib
+heleb
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+rrc-b
+cblack
+richbl
+brynb
+alanb
+hyattb
+donnbl
+cblasi
+sbliss
+janebl
+fransb
+inface
+lucibo
+pboehm
+joyb
+pboffo
+rrc-b
+johnbo
+airex
+bobb
+kierab
+laurbo
+danbol
+cathab
+kathbo
+cathb
+coreyb
+garyb
+dadcmb
+nicolb
+ismb
+davidb
+tomb
+ntxsb
+timbo
+ronbo
+benjab
+toddb
+cheryb
+vskb
+shellb
+timboy
+bboyle
+jboyle
+kaijab
+rrc-b
+myriab
+mbrand
+brianb
+darreb
+simonb
+geneb
+tracyb
+lisbd
+ibmdb
+pbrenn
+sineab
+susb
+bryanb
+ebrew
+donbr
+noelb
+elizbr
+galeb
+davb
+billbr
+bribro
+sophib
+henryb
+philbr
+donb
+amybr
+cbrown
+dianab
+acjb
+kirstb
+lesb
+rbrown
+ntxrbr
+albru
+sbrug
+ludob
+vssb
+paulbr
+ccrb
+michbu
+larrbu
+lisab
+carolb
+peteb
+actb
+klausb
+telen
+sbuhr
+louisb
+miltb
+heathb
+helmb
+bburke
+catbu
+billb
+dburn
+kaeb
+ronb
+eileeb
+brettb
+laurab
+ccfb
+gunteb
+gregb
+robbu
+robut
+acwb
+faribu
+noelby
+ntxpbw
+eddac
+oscarc
+scre12
+anneca
+jocelc
+barbca
+janinc
+jasonc
+michca
+edc
+pedroc
+suzc
+wandac
+davc
+carenc
+joanc
+leeca
+markca
+pcamp
+julioc
+mikec
+lucisc
+georgc
+tomasb
+antonc
+stevec
+jimc
+dencar
+tomc
+ntxpc
+annsc
+jcarne
+nanc
+patcar
+loric
+patca
+davic
+kimca
+acmc
+wilmac
+maurc
+kcart
+gregco
+ginnyc
+iainc
+philc
+riccar
+steca
+ntxsc
+louisc
+gianca
+annec
+jjcav
+kencav
+ntxgc
+jeffca
+palmac
+vsmc
+forms
+ntxfc
+cchad
+cchali
+troyc
+cecilc
+carmc
+jasch
+sophic
+abbotc
+inac
+lchang
+nicolc
+vchang
+clintc
+billch
+kench
+lojc
+ntxac
+johnch
+sorlet
+kimc
+weiyc
+rrycc
+ritac
+pierrc
+tomch
+henryc
+willch
+rrkc
+ntxsch
+daich
+yukoc
+abm
+acgc
+ntxjch
+rrtcp
+jojoc
+krisch
+ronch
+javch
+ntxrac
+sebasc
+andrwc
+gkkc
+yvonnc
+kingsc
+bibich
+ntxkch
+philci
+ccipol
+paulc
+tomci
+waltci
+andyc
+bencl
+dclark
+ntxjcl
+johcl
+kclark
+vspc
+rrsc
+derekc
+johncl
+ntxdc
+ibmrc
+shimaa
+silkea
+davida
+chrial
+eallen
+susana
+joseal
+stefam
+paulam
+mettea
+stinev
+henrik
+ralpha
+robann
+csilla
+erica
+marcoa
+malia
+ericas
+alexat
+maubry
+anab
+alexib
+nicolb
+urib
+shantb
+mikeba
+andbar
+beckyb
+scottb
+leonib
+mariab
+reemb
+maddib
+marieb
+romab
+ericba
+brianb
+josefb
+chadb
+ronb
+almub
+tinab
+monikb
+joelb
+yannb
+tarunb
+stephb
+veronb
+stefab
+bernb
+dougbl
+boccom
+laurib
+johanb
+erinb
+sinbo
+michab
+markbo
+julb
+kamilb
+laurb
+colinb
+taniab
+pbrad
+jonbr
+edb
+annieb
+jmbrie
+lisab
+joakib
+kbrost
+aaronb
+paulbr
+seambr
+elainb
+kaib
+alainb
+courtb
+richbu
+liambu
+ferbu
+hanneb
+felicb
+seanb
+maytec
+nelsc
+armanc
+nicoc
+camcar
+juanjc
+tiziac
+marcoc
+tonyc
+jpcast
+damiac
+ignac
+sabinc
+gordc
+jaruc
+cbchae
+ammonc
+jaksch
+stephc
+jimc
+normac
+fredc
+richch
+cheric
+ericc
+dmchoi
+gabc
+plchua
+josefc
+crisci
+mikecl
+coracl
+danco
+antonc
+maxinc
+karenc
+cecelw
+alberc
+claudc
+ajc
+valerc
+maxicu
+brenc
+wolfz
+maxic
+peteda
+jacqd
+robd
+bobk
+hiltom
+katjad
+marnod
+serged
+antond
+michde
+freddv
+hansd
+tdiver
+ylvad
+emerd
+chelod
+julied
+jdrage
+markdr
+chridu
+yvand
+delphd
+leed
+michd
+betind
+stephv
+georgd
+owenea
+stepea
+owene
+kime
+rone
+helene
+annete
+marcee
+gabreh
+nathfa
+helenf
+masudf
+jeruf
+brunof
+marif
+debbif
+jonf
+joachf
+cathf
+pennyf
+kimbef
+andref
+billf
+damfin
+solfo
+jacquf
+pacof
+antonf
+tomfr
+bjornf
+mickeg
+werng
+mariag
+julieg
+georgg
+roxang
+beng
+mardcg
+rafg
+jimeg
+maiog
+laurga
+giagat
+mattga
+laurg
+bgenar
+fredge
+lidiag
+stefag
+michag
+minah
+mgietl
+robg
+juligm
+stephg
+dannyg
+davidg
+xavg
+sophig
+danieg
+cgold
+merisg
+osnatg
+josem
+diman
+fredgo
+erendg
+luisgo
+rosag
+grantg
+andygo
+sgort
+jeanpg
+geneg
+scottg
+chiarg
+richgr
+bruceg
+adamg
+avishg
+sandg
+sophg
+andreg
+ugrob
+sissyg
+guenth
+jhaas
+erezh
+mariha
+tommyh
+ahaigh
+svenh
+jackih
+johnha
+perh
+katreh
+gerhar
+charlh
+sanjh
+robhau
+tomokh
+erich
+brenhe
+andreh
+markuh
+raphh
+bernh
+brendh
+olafh
+klaush
+enidh
+ayolh
+jorgeh
+bobhe
+adrih
+loreth
+juanjh
+stefah
+gerdh
+kathah
+maxh
+micheh
+juergh
+scotth
+torhol
+toddh
+szabh
+martah
+shood
+jillh
+shannh
+clarah
+liborh
+jhosty
+chanth
+amyhs
+helenh
+chhsu
+franh
+janyh
+tracyh
+dusth
+davhug
+bradh
+moyah
+jormah
+mustai
+kevini
+leei
+junkoi
+jamest
+karenj
+joej
+davidj
+haylj
+rosej
+gertj
+lanaj
+hollyj
+jcjan
+isek
+alexka
+giselk
+mihok
+annemk
+peteke
+lizak
+hyeyk
+hskim
+bking
+timk
+cko
+fannyk
+davek
+rolank
+craigk
+martik
+kotay
+fredk
+carolk
+georgk
+chrisk
+geirk
+juliak
+andrk
+mank
+kerstk
+tckuo
+tomokk
+skwan
+tony
+markul
+johnla
+drewl
+darlan
+kenl
+oritl
+benjla
+klau
+markl
+mlaur
+delphl
+pyledu
+evinl
+dlee
+hjlee
+hklee
+ivyl
+jonsul
+kwlee
+sjlee
+wglee
+ivole
+peterl
+steple
+nikil
+serenl
+glinah
+lsliau
+hannal
+rafael
+angell
+roblim
+merjal
+billli
+avihal
+birdlo
+sergel
+alberl
+helenl
+carlol
+josel
+juanl
+annel
+lilyl
+gerryl
+vicenl
+nathl
+edlf
+editl
+derekl
+saml
+alphal
+kirbyl
+luisl
+dickl
+torull
+cristm
+mmaerk
+leonm
+sergm
+brendm
+andrwm
+tinym
+magdem
+franma
+kaym
+nichm
+caswem
+arnam
+rickm
+carlam
+helenm
+farish
+majda
+sarya
+monaa
+ahmada
+tawfad
+dach
+krissa
+krisa
+paula
+aleena
+debraa
+bja
+radams
+stepha
+josepa
+colbya
+allana
+shola
+dawna
+wahaba
+conraa
+paulal
+ralexa
+ricka
+willal
+beckya
+darrea
+bkena
+saraha
+trical
+callis
+bga
+anasal
+emada
+marya
+kamila
+marca
+amyan
+ericaa
+janeta
+tinaa
+garant
+franka
+joea
+davapp
+denisa
+stevar
+briana
+mikea
+randya
+rafar
+noela
+michas
+davea
+kellas
+petash
+alexa
+paulat
+teresa
+daveat
+heidia
+mikeau
+amya
+cloa
+akilaz
+aprib
+tricib
+bruceb
+chrisb
+ryanba
+tomb
+tohoub
+pbain
+bwb
+gayleb
+branb
+sharib
+johnb
+dbanks
+rayb
+cbarb
+waldb
+larrba
+jeremb
+mimid
+garyba
+bpb
+hughb
+kenba
+markba
+alib
+koyb
+patb
+dbaur
+jerib
+alexb
+brubax
+gregbe
+laurib
+annb
+aarob
+johnbe
+karbec
+davb
+dbeers
+bmbe
+teresb
+paulbe
+aaronb
+griogb
+ericb
+tracbe
+janeb
+bradb
+leslib
+tinab
+dougbe
+jonib
+joelbe
+sharb
+dougb
+andreb
+kberg
+ryanb
+cynthb
+dberry
+cherbe
+bmarcb
+tamib
+gileb
+patbe
+mikebi
+bryab
+kathb
+jamebi
+kenb
+billbi
+jennib
+danbl
+jasonb
+philib
+alisbl
+amybl
+camib
+alexbl
+patbl
+carlab
+aprilb
+marcbl
+brandb
+kristb
+jbolic
+garybo
+ellenb
+eribo
+courtb
+coletb
+larryb
+marcb
+johnbo
+ronb
+kimbb
+launab
+willbo
+ericbo
+cboyd
+gboyd
+dianab
+glennb
+debb
+kimb
+cnamb
+rbrand
+jimbr
+jeffbr
+ritab
+ednakb
+lisab
+fumiko
+josepb
+janicb
+davib
+joanb
+jacb
+johnbr
+cynb
+dbrown
+djbrow
+jbrown
+lesb
+scottb
+sbrown
+btb
+lynnb
+maryb
+lisabr
+rogb
+bbrunn
+rubenb
+virgbr
+normb
+jeffb
+connib
+balb
+robinb
+garybu
+khoib
+janb
+johnbu
+peterb
+dewbur
+aileeb
+stevbu
+patbur
+lorrib
+sanhb
+kaarib
+sbutch
+bab
+johbu
+bsb
+sandyb
+anitab
+donbyr
+briac
+dcain
+deanc
+bobca
+davec
+lancec
+craigc
+jocan
+scotca
+phylc
+acarav
+chrisc
+bnc
+johnca
+jasonc
+johca
+richc
+robcar
+therca
+kerryc
+karenc
+franc
+chrica
+davic
+paulc
+cindyc
+tiffc
+richca
+sheilc
+markca
+santhc
+amyc
+sethc
+mikec
+robc
+jamiec
+salc
+micch
+cchai
+lizch
+karlc
+bbc
+alexch
+kchang
+kirsch
+rogc
+cync
+gregch
+marcic
+kchase
+avac
+samch
+denche
+bjch
+allenc
+andyc
+robtch
+dchiu
+cchov
+mchow
+waic
+charch
+melinc
+melodc
+deechr
+ninac
+jchung
+tinc
+yongc
+robch
+charcl
+gregc
+arletc
+rosecl
+carolc
+sherrc
+brianc
+gcluff
+philc
+ccobb
+bvc
+stevco
+jaschc
+chadc
+darrec
+bkc
+thomc
+bjc
+billc
+kevct
+yannac
+royco
+jcomb
+dianec
+rosac
+dcook
+davidc
+tcook
+gregco
+chasc
+jeffco
+lcope
+cnadc
+liscor
+charc
+stevc
+kandcw
+peteco
+lisaco
+scottc
+chipc
+brc
+chrcox
+gretc
+willc
+bwc
+danc
+russc
+joaoac
+royc
+doylec
+barbcr
+floydc
+timc
+angelc
+kenc
+charlc
+dcurle
+robync
+ronc
+davcu
+btc
+maziad
+suzied
+kdahl
+pauld
+gregmd
+ldalto
+tuand
+harda
+tomd
+almond
+debdas
+jand
+hendav
+tomda
+robdav
+bodied
+dand
+jeffda
+kated
+samd
+miked
+raqdm
+danld
+stevd
+marisd
+sylvad
+cyndid
+nicod
+normd
+francd
+gennd
+joed
+cdemai
+nicold
+andyd
+stuard
+russd
+candye
+kathd
+kathrd
+heathd
+jeffd
+johndi
+seandi
+jdirks
+markdi
+briand
+michd
+timdo
+carodo
+stand
+amyd
+josed
+pauldo
+windd
+carld
+teresd
+johndo
+davidd
+irinad
+rondru
+audied
+sanjad
+darona
+maiko
+yumiak
+cecila
+reneea
+sandia
+ulrika
+chara
+harai
+tomoka
+eduarv
+chucka
+normaa
+miaa
+kersta
+annaba
+stefa
+adaya
+paala
+laylaa
+kirstb
+naokib
+scotba
+erikb
+bevb
+garyb
+annb
+pederb
+brendb
+joeb
+shellb
+karlb
+ubazik
+donnab
+armanb
+paulib
+yukob
+larsb
+lourdb
+robbi
+chribi
+ainslb
+cblair
+ernstb
+maribo
+thomb
+nickb
+sboyd
+johnbr
+daveb
+cbrown
+stuarb
+alineb
+maxb
+kevinb
+kathyc
+maggic
+geovac
+tinac
+marutc
+israc
+rayc
+eunhyo
+sycho
+amitc
+adriac
+shellc
+angeco
+lilic
+elisac
+ccrane
+samc
+ferguc
+barbc
+madiec
+tracec
+yukikd
+karend
+rolfdp
+larryd
+fredde
+cmed
+jand
+andred
+lised
+anad
+chardo
+patd
+denduf
+barryd
+orland
+jamied
+ryane
+susane
+keithe
+mohame
+briane
+linge
+done
+michfe
+mirnaf
+trevfi
+ericf
+sioflo
+lisaf
+sterlf
+stevef
+peggyf
+mfrey
+derekf
+geralf
+mariyf
+naokof
+tamakf
+miwaf
+yumikf
+keikof
+shawng
+marthg
+olivig
+lisaga
+jeffga
+mongm
+xavige
+roseg
+sandyg
+larsg
+paulgo
+camilg
+tomomg
+dongr
+andreg
+paulg
+sgray
+johng
+timg
+sgreen
+rgregg
+peerg
+markh
+lisah
+zachh
+gregh
+mikeha
+robh
+ryanh
+arih
+satomh
+jennha
+akirah
+rossh
+taeh
+kyokoh
+erich
+carinh
+earlh
+fumikh
+collh
+yoshih
+ysho
+davhof
+jkhong
+eriho
+goh
+jeanh
+path
+satoi
+isaoi
+akikoi
+levi
+fuyuki
+yukoi
+michii
+noriki
+etsuki
+nozomi
+hiroi
+yukai
+balraj
+barbj
+travj
+endoj
+johanj
+emilyj
+gregj
+chrisj
+susanj
+kaisak
+ayumik
+hisakk
+megk
+minnak
+yokok
+kkato
+yukok
+denrec
+clionk
+donkel
+masayk
+jennik
+alik
+ikuek
+jeanhe
+minjuk
+yskim
+klausk
+masaek
+emikok
+saorik
+akemik
+bobk
+tkubo
+hansk
+yasuek
+bjl
+alexal
+randyl
+stephl
+markl
+waynel
+jennl
+johnle
+michel
+markli
+waltl
+hazell
+cathlo
+gyorgl
+jodyl
+bgl
+elvil
+maglo
+candyl
+kathlo
+luisfl
+sergl
+mariol
+aidal
+kempl
+ewaldl
+ruthl
+patm
+joem
+chrim
+hegem
+youkom
+keikom
+mayum
+nobum
+angiem
+nannm
+melism
+errolm
+jerrym
+charlm
+mitchm
+stasim
+peterm
+chrime
+misahm
+tomokm
+ericmo
+hermm
+patmo
+angelm
+robmu
+madhum
+davmul
+sharym
+sachn
+chiekn
+hirokn
+sachin
+naozn
+hyojn
+basiln
+miran
+hegen
+michn
+michin
+maarn
+bayarn
+jonnor
+monikn
+mobara
+kaoruo
+kyokoo
+masato
+hideko
+takako
+mihook
+tadaso
+sollis
+jaceko
+juano
+monipa
+manpal
+parkky
+stevpa
+kamp
+magdap
+sperez
+scotpe
+curtp
+brianp
+kerstp
+cynthp
+lynnp
+annikp
+adriar
+laurar
+peterr
+alexmr
+oliver
+larsr
+hrawet
+isabcr
+kater
+warrob
+mariar
+marilr
+sandrr
+ellear
+annr
+btro
+drush
+akikos
+kazuks
+mihos
+mikasa
+norsak
+mikes
+stevsa
+juansc
+satoes
+erikas
+keikos
+emikos
+noriks
+yasuns
+jims
+tsukas
+shiges
+birsch
+robsc
+harals
+marls
+olafs
+birgis
+mseki
+masaes
+kaorus
+ramis
+bjjs
+shizus
+yoshis
+vics
+karens
+jessmi
+richso
+monsot
+terrys
+susast
+hugos
+etsuks
+davsum
+krists
+masaks
+terues
+yumis
+aleksz
+yukot
+nortak
+keikot
+johnt
+yumikt
+timeat
+bct
+stevet
+pamt
+shinot
+junet
+urmilt
+szilvt
+michit
+germat
+keikto
+glent
+marktr
+juliet
+shimat
+ttsuji
+yukikt
+melt
+jturn
+chieu
+akikou
+megumu
+naokou
+juandu
+diegov
+veronv
+mariov
+mariav
+billw
+helenw
+lorriw
+kanakw
+coreyw
+carlaw
+ronw
+aliciw
+scottw
+royw
+vanwh
+evaw
+bodilw
+arnew
+mikewi
+robwim
diff --git a/private/net/svcdlls/lls/test/ct/n1.dat b/private/net/svcdlls/lls/test/ct/n1.dat
new file mode 100644
index 000000000..2ac6c1d55
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n1.dat
@@ -0,0 +1,1000 @@
+house1
+ronaar1
+rrja1
+ntxfa1
+ninaa1
+cabram1
+johnac1
+martad1
+acca1
+rrea1
+markad1
+acma1
+byrona1
+yosha1
+julioa1
+ccaa1
+bertag1
+ibmma1
+acmah1
+noriya1
+hakana1
+lisac1
+johnal1
+melisa1
+boba1
+rayall1
+deba1
+jeffal1
+rallen1
+laural1
+lallyn1
+venesa1
+annaal1
+nancya1
+dinoa1
+bain1
+marioa1
+raquea1
+vitora1
+scotam1
+renam1
+eglea1
+audrea1
+chrisa1
+ntxha1
+curta1
+davand1
+davean1
+ntxga1
+gavana1
+acja1
+jod1
+attma1
+naomia1
+royand1
+sallya1
+toman1
+matsan1
+jamiea1
+rickan1
+jana1
+jeffan1
+jiman1
+bevaok1
+takaya1
+yumia1
+carloa1
+takaar1
+shinar1
+yukaa1
+tonyar1
+hiroar1
+suza1
+kenar1
+eadarm1
+loria1
+ntxra1
+richar1
+carnst1
+arnulw1
+mayuma1
+hidas1
+richat1
+idoa1
+ntxbat1
+markat1
+kevana1
+gillea1
+micha1
+joset1
+ntxba1
+gma1
+saorib1
+vikkib1
+garyba1
+suzieb1
+michba1
+danbag1
+frankb1
+larryb1
+sbaird1
+rrkeb1
+jolynb1
+paulb1
+sbaker1
+valb1
+leeb1
+geoba1
+dougba1
+ctpsb1
+hughb1
+galb1
+leonib1
+patba1
+ntxjb1
+rrjba1
+donbar1
+jacbar1
+roberb1
+larrb1
+allpak1
+mosesb1
+donnab1
+cibelb1
+vspb1
+peteba1
+abates1
+marcb1
+pbcnz1
+jbattl1
+jimbau1
+cbaum1
+danbau1
+bilbax1
+kathbe1
+ntxab1
+celinb1
+karenb1
+philb1
+darryb1
+ctpdb1
+scre91
+ccsb1
+stepbe1
+ccjben1
+leneb1
+ntxnb1
+pbenoi1
+vscb1
+micheb1
+rrjimb1
+chadb1
+karib1
+nathb1
+dougbe1
+kimmob1
+amyb1
+decdb1
+ebenb1
+acabe1
+andb1
+ntxsbe1
+michbe1
+geralb1
+juditb1
+savlob1
+dsiwm1
+willbi1
+ellenb1
+joseb1
+blasb1
+thomb1
+annicb1
+marbin1
+ntxmb1
+daveb1
+dirkb1
+suebi1
+stefb1
+patbi1
+hilmib1
+heleb1
+rrcb1
+cblack1
+richbl1
+brynb1
+alanb1
+hyattb1
+donnbl1
+cblasi1
+sbliss1
+janebl1
+fransb1
+inface1
+lucibo1
+pboehm1
+joyb1
+pboffo1
+johnbo1
+airex1
+bobb1
+kierab1
+laurbo1
+danbol1
+cathab1
+kathbo1
+cathb1
+coreyb1
+garyb1
+dadcmb1
+nicolb1
+ismb1
+davidb1
+tomb1
+ntxsb1
+timbo1
+ronbo1
+benjab1
+toddb1
+cheryb1
+vskb1
+shellb1
+timboy1
+bboyle1
+jboyle1
+kaijab1
+myriab1
+mbrand1
+brianb1
+darreb1
+simonb1
+geneb1
+tracyb1
+lisbd1
+ibmdb1
+pbrenn1
+sineab1
+susb1
+bryanb1
+ebrew1
+donbr1
+noelb1
+elizbr1
+galeb1
+davb1
+billbr1
+bribro1
+sophib1
+henryb1
+philbr1
+donb1
+amybr1
+cbrown1
+dianab1
+acjb1
+kirstb1
+lesb1
+rbrown1
+ntxrbr1
+albru1
+sbrug1
+ludob1
+vssb1
+paulbr1
+ccrb1
+michbu1
+larrbu1
+lisab1
+carolb1
+peteb1
+actb1
+klausb1
+telen1
+sbuhr1
+louisb1
+miltb1
+heathb1
+helmb1
+bburke1
+catbu1
+billb1
+dburn1
+kaeb1
+ronb1
+eileeb1
+brettb1
+laurab1
+ccfb1
+gunteb1
+gregb1
+robbu1
+robut1
+acwb1
+faribu1
+noelby1
+ntxpbw1
+eddac1
+oscarc1
+scre121
+anneca1
+jocelc1
+barbca1
+janinc1
+jasonc1
+michca1
+edc1
+pedroc1
+suzc1
+wandac1
+davc1
+carenc1
+joanc1
+leeca1
+markca1
+pcamp1
+julioc1
+mikec1
+lucisc1
+georgc1
+tomasb1
+antonc1
+stevec1
+jimc1
+dencar1
+tomc1
+ntxpc1
+annsc1
+jcarne1
+nanc1
+patcar1
+loric1
+patca1
+davic1
+kimca1
+acmc1
+wilmac1
+maurc1
+kcart1
+gregco1
+ginnyc1
+iainc1
+philc1
+riccar1
+steca1
+ntxsc1
+louisc1
+gianca1
+annec1
+jjcav1
+kencav1
+ntxgc1
+jeffca1
+palmac1
+vsmc1
+forms1
+ntxfc1
+cchad1
+cchali1
+troyc1
+cecilc1
+carmc1
+jasch1
+sophic1
+abbotc1
+inac1
+lchang1
+nicolc1
+vchang1
+clintc1
+billch1
+kench1
+lojc1
+ntxac1
+johnch1
+sorlet1
+kimc1
+weiyc1
+rrycc1
+ritac1
+pierrc1
+tomch1
+henryc1
+willch1
+rrkc1
+ntxsch1
+daich1
+yukoc1
+abm1
+acgc1
+ntxjch1
+rrtcp1
+jojoc1
+krisch1
+ronch1
+javch1
+ntxrac1
+sebasc1
+andrwc1
+gkkc1
+yvonnc1
+kingsc1
+bibich1
+ntxkch1
+philci1
+ccipol1
+paulc1
+tomci1
+waltci1
+andyc1
+bencl1
+dclark1
+ntxjcl1
+johcl1
+kclark1
+vspc1
+rrsc1
+derekc1
+johncl1
+ntxdc1
+ibmrc1
+shimaa1
+silkea1
+davida1
+chrial1
+eallen1
+susana1
+joseal1
+stefam1
+paulam1
+mettea1
+stinev1
+henrik1
+ralpha1
+robann1
+csilla1
+erica1
+marcoa1
+malia1
+ericas1
+alexat1
+maubry1
+anab1
+alexib1
+nicolb1
+urib1
+shantb1
+mikeba1
+andbar1
+beckyb1
+scottb1
+leonib1
+mariab1
+reemb1
+maddib1
+marieb1
+romab1
+ericba1
+brianb1
+josefb1
+chadb1
+ronb1
+almub1
+tinab1
+monikb1
+joelb1
+yannb1
+tarunb1
+stephb1
+veronb1
+stefab1
+bernb1
+dougbl1
+boccom1
+laurib1
+johanb1
+erinb1
+sinbo1
+michab1
+markbo1
+julb1
+kamilb1
+laurb1
+colinb1
+taniab1
+pbrad1
+jonbr1
+edb1
+annieb1
+jmbrie1
+lisab1
+joakib1
+kbrost1
+aaronb1
+paulbr1
+seambr1
+elainb1
+kaib1
+alainb1
+courtb1
+richbu1
+liambu1
+ferbu1
+hanneb1
+felicb1
+seanb1
+maytec1
+nelsc1
+armanc1
+nicoc1
+camcar1
+juanjc1
+tiziac1
+marcoc1
+tonyc1
+jpcast1
+damiac1
+ignac1
+sabinc1
+gordc1
+jaruc1
+cbchae1
+ammonc1
+jaksch1
+stephc1
+jimc1
+normac1
+fredc1
+richch1
+cheric1
+ericc1
+dmchoi1
+gabc1
+plchua1
+josefc1
+crisci1
+mikecl1
+coracl1
+danco1
+antonc1
+maxinc1
+karenc1
+cecelw1
+alberc1
+claudc1
+ajc1
+valerc1
+maxicu1
+brenc1
+wolfz1
+maxic1
+peteda1
+jacqd1
+robd1
+bobk1
+hiltom1
+katjad1
+marnod1
+serged1
+antond1
+michde1
+freddv1
+hansd1
+tdiver1
+ylvad1
+emerd1
+chelod1
+julied1
+jdrage1
+markdr1
+chridu1
+yvand1
+delphd1
+leed1
+michd1
+betind1
+stephv1
+georgd1
+owenea1
+stepea1
+owene1
+kime1
+rone1
+helene1
+annete1
+marcee1
+gabreh1
+nathfa1
+helenf1
+masudf1
+jeruf1
+brunof1
+marif1
+debbif1
+jonf1
+joachf1
+cathf1
+pennyf1
+kimbef1
+andref1
+billf1
+damfin1
+solfo1
+jacquf1
+pacof1
+antonf1
+tomfr1
+bjornf1
+mickeg1
+werng1
+mariag1
+julieg1
+georgg1
+roxang1
+beng1
+mardcg1
+rafg1
+jimeg1
+maiog1
+laurga1
+giagat1
+mattga1
+laurg1
+bgenar1
+fredge1
+lidiag1
+stefag1
+michag1
+minah1
+mgietl1
+robg1
+juligm1
+stephg1
+dannyg1
+davidg1
+xavg1
+sophig1
+danieg1
+cgold1
+merisg1
+osnatg1
+josem1
+diman1
+fredgo1
+erendg1
+luisgo1
+rosag1
+grantg1
+andygo1
+sgort1
+jeanpg1
+geneg1
+scottg1
+chiarg1
+richgr1
+bruceg1
+adamg1
+avishg1
+sandg1
+sophg1
+andreg1
+ugrob1
+sissyg1
+guenth1
+jhaas1
+erezh1
+mariha1
+tommyh1
+ahaigh1
+svenh1
+jackih1
+johnha1
+perh1
+katreh1
+gerhar1
+charlh1
+sanjh1
+robhau1
+tomokh1
+erich1
+brenhe1
+andreh1
+markuh1
+raphh1
+bernh1
+brendh1
+olafh1
+klaush1
+enidh1
+ayolh1
+jorgeh1
+bobhe1
+adrih1
+loreth1
+juanjh1
+stefah1
+gerdh1
+kathah1
+maxh1
+micheh1
+juergh1
+scotth1
+torhol1
+toddh1
+szabh1
+martah1
+shood1
+jillh1
+shannh1
+clarah1
+liborh1
+jhosty1
+chanth1
+amyhs1
+helenh1
+chhsu1
+franh1
+janyh1
+tracyh1
+dusth1
+davhug1
+bradh1
+moyah1
+jormah1
+mustai1
+kevini1
+leei1
+junkoi1
+jamest1
+karenj1
+joej1
+davidj1
+haylj1
+rosej1
+gertj1
+lanaj1
+hollyj1
+jcjan1
+isek1
+alexka1
+giselk1
+mihok1
+annemk1
+peteke1
+lizak1
+hyeyk1
+hskim1
+bking1
+timk1
+cko1
+fannyk1
+davek1
+rolank1
+craigk1
+martik1
+kotay1
+fredk1
+carolk1
+georgk1
+chrisk
+geirk1
+juliak1
+andrk1
+mank1
+kerstk1
+tckuo1
+tomokk1
+skwan1
+tony1
+markul1
+johnla1
+drewl1
+darlan1
+kenl1
+oritl1
+benjla1
+klau1
+markl1
+mlaur1
+delph1
+pyledu1
+evinl1
+dlee1
+hjlee1
+hklee1
+ivyl1
+jonsul1
+kwlee1
+sjlee1
+wglee1
+ivole1
+peterl1
+steple1
+nikil1
+serenl1
+glinah1
+lsliau1
+hannal1
+rafael1
+angell1
+roblim1
+merjal1
+billli1
+avihal1
+birdlo1
+sergel1
+alberl1
+helenl1
+carlol1
+josel1
+juanl1
+annel1
+lilyl1
+gerryl1
+vicenl1
+nathl1
+edlf1
+editl1
+derekl1
+saml1
+alphal1
+kirbyl1
+luisl1
+dickl1
+torull1
+cristm1
+mmaerk1
+leonm1
+sergm1
+brendm1
+andrwm1
+tinym1
+magdem1
+franma1
+kaym1
+nichm1
+caswem1
+arnam1
+rickm1
+carlam1
+helenm1
+farish1
+majda1
+sarya1
+monaa1
+ahmada1
+tawfad1
+dach1
+krissa1
+krisa1
+paula1
+aleena1
+debraa1
+bja1
+radams1
+stepha1
+josepa1
+colbya1
+allana1
+shola1
+dawna1
+wahaba1
+conraa1
+paulal1
+ralexa1
+ricka1
+willal1
+beckya1
+darrea1
+bkena1
+saraha1
+trical1
+callis1
+bga1
+anasal1
+emada1
+marya1
+kamila1
+marca1
+amyan1
+ericaa1
+janeta1
+tinaa1
+garant1
+franka1
+joea1
+davapp1
+denisa1
+stevar1
+briana1
+mikea1
+randya1
+rafar1
+noela1
+michas1
+davea1
+kellas1
+petash1
+alexa1
+paulat1
+teresa1
+daveat1
+heidia1
+mikeau1
+amya1
+cloa1
+akilaz1
+aprib1
+tricib1
+bruceb1
+chrisb1
+ryanba1
+tomb1
+tohoub1
+pbain1
+bwb1
+gayleb1
+branb1
+sharib1
+johnb1
+dbanks1
+rayb1
+cbarb1
+waldb1
+larrba1
+jeremb1
+mimid1
+garyba1
+bpb1
+hughb1
+kenba1
+markba1
+alib1
+koyb1
+patb1
+dbaur1
+jerib1
+alexb1
+brubax1
+gregbe1
+laurib1
+annb1
+aarob1
+johnbe1
+karbec1
+davb1
+dbeers1
+bmbe1
+teresb1
+paulbe1
+aaronb1
+griogb1
+ericb1
+tracbe1
+janeb1
+bradb1
+leslib1
+tinab1
+dougbe1
+jonib1
+joelbe1
+sharb1
+dougb1
+andreb1
+kberg1
+ryanb1
+cynthb1
+dberry1
+cherbe1
+bmarcb1
+tamib1
+gileb1
+patbe1
+mikebi1
+bryab1
+kathb1
+jamebi1
+kenb1
+billbi1
+jennib1
+danbl1
+jasonb1
+philib1
+alisbl1
+amybl1
+camib1
+alexbl1
+patbl1
+carlab1
+aprilb1
+marcbl1
+brandb1
+kristb1
+jbolic1
+garybo1
+ellenb1
+eribo1
+courtb1
+coletb1
+larryb1
+marcb1
diff --git a/private/net/svcdlls/lls/test/ct/n10.dat b/private/net/svcdlls/lls/test/ct/n10.dat
new file mode 100644
index 000000000..0ef1bcec9
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n10.dat
@@ -0,0 +1,1000 @@
+house10
+ronaar10
+rrja10
+ntxfa10
+ninaa10
+cabram10
+johnac10
+martad10
+acca10
+rrea10
+markad10
+acma10
+byrona10
+yosha10
+julioa10
+ccaa10
+bertag10
+ibmma10
+acmah10
+noriya10
+hakana10
+lisac10
+johnal10
+melisa10
+boba10
+rayall10
+deba10
+jeffal10
+rallen10
+laural10
+lallyn10
+venesa10
+annaal10
+nancya10
+dinoa10
+bain10
+marioa10
+raquea10
+vitora10
+scotam10
+renam10
+eglea10
+audrea10
+chrisa10
+ntxha10
+curta10
+davand10
+davean10
+ntxga10
+gavana10
+acja10
+jod10
+attma10
+naomia10
+royand10
+sallya10
+toman10
+matsan10
+jamiea10
+rickan10
+jana10
+jeffan10
+jiman10
+bevaok10
+takaya10
+yumia10
+carloa10
+takaar10
+shinar10
+yukaa10
+tonyar10
+hiroar10
+suza10
+kenar10
+eadarm10
+loria10
+ntxra10
+richar10
+carnst10
+arnulw10
+mayuma10
+hidas10
+richat10
+idoa10
+ntxbat10
+markat10
+kevana10
+gillea10
+micha10
+joset10
+ntxba10
+gma10
+saorib10
+vikkib10
+garyba10
+suzieb10
+michba10
+danbag10
+frankb10
+larryb10
+sbaird10
+rrkeb10
+jolynb10
+paulb10
+sbaker10
+valb10
+leeb10
+geoba10
+dougba10
+ctpsb10
+hughb10
+galb10
+leonib10
+patba10
+ntxjb10
+rrjba10
+donbar10
+jacbar10
+roberb10
+larrb10
+allpak10
+mosesb10
+donnab10
+cibelb10
+vspb10
+peteba10
+abates10
+marcb10
+pbcnz10
+jbattl10
+jimbau10
+cbaum10
+danbau10
+bilbax10
+kathbe10
+ntxab10
+celinb10
+karenb10
+philb10
+darryb10
+ctpdb10
+scre910
+ccsb10
+stepbe10
+ccjben10
+leneb10
+ntxnb10
+pbenoi10
+vscb10
+micheb10
+rrjimb10
+chadb10
+karib10
+nathb10
+dougbe10
+kimmob10
+amyb10
+decdb10
+ebenb10
+acabe10
+andb10
+ntxsbe10
+michbe10
+geralb10
+juditb10
+savlob10
+dsiwm10
+willbi10
+ellenb10
+joseb10
+blasb10
+thomb10
+annicb10
+marbin10
+ntxmb10
+daveb10
+dirkb10
+suebi10
+stefb10
+patbi10
+hilmib10
+heleb10
+rrcb10
+cblack10
+richbl10
+brynb10
+alanb10
+hyattb10
+donnbl10
+cblasi10
+sbliss10
+janebl10
+fransb10
+inface10
+lucibo10
+pboehm10
+joyb10
+pboffo10
+johnbo10
+airex10
+bobb10
+kierab10
+laurbo10
+danbol10
+cathab10
+kathbo10
+cathb10
+coreyb10
+garyb10
+dadcmb10
+nicolb10
+ismb10
+davidb10
+tomb10
+ntxsb10
+timbo10
+ronbo10
+benjab10
+toddb10
+cheryb10
+vskb10
+shellb10
+timboy10
+bboyle10
+jboyle10
+kaijab10
+myriab10
+mbrand10
+brianb10
+darreb10
+simonb10
+geneb10
+tracyb10
+lisbd10
+ibmdb10
+pbrenn10
+sineab10
+susb10
+bryanb10
+ebrew10
+donbr10
+noelb10
+elizbr10
+galeb10
+davb10
+billbr10
+bribro10
+sophib10
+henryb10
+philbr10
+donb10
+amybr10
+cbrown10
+dianab10
+acjb10
+kirstb10
+lesb10
+rbrown10
+ntxrbr10
+albru10
+sbrug10
+ludob10
+vssb10
+paulbr10
+ccrb10
+michbu10
+larrbu10
+lisab10
+carolb10
+peteb10
+actb10
+klausb10
+telen10
+sbuhr10
+louisb10
+miltb10
+heathb10
+helmb10
+bburke10
+catbu10
+billb10
+dburn10
+kaeb10
+ronb10
+eileeb10
+brettb10
+laurab10
+ccfb10
+gunteb10
+gregb10
+robbu10
+robut10
+acwb10
+faribu10
+noelby10
+ntxpbw10
+eddac10
+oscarc10
+scre10210
+anneca10
+jocelc10
+barbca10
+janinc10
+jasonc10
+michca10
+edc10
+pedroc10
+suzc10
+wandac10
+davc10
+carenc10
+joanc10
+leeca10
+markca10
+pcamp10
+julioc10
+mikec10
+lucisc10
+georgc10
+tomasb10
+antonc10
+stevec10
+jimc10
+dencar10
+tomc10
+ntxpc10
+annsc10
+jcarne10
+nanc10
+patcar10
+loric10
+patca10
+davic10
+kimca10
+acmc10
+wilmac10
+maurc10
+kcart10
+gregco10
+ginnyc10
+iainc10
+philc10
+riccar10
+steca10
+ntxsc10
+louisc10
+gianca10
+annec10
+jjcav10
+kencav10
+ntxgc10
+jeffca10
+palmac10
+vsmc10
+forms10
+ntxfc10
+cchad10
+cchali10
+troyc10
+cecilc10
+carmc10
+jasch10
+sophic10
+abbotc10
+inac10
+lchang10
+nicolc10
+vchang10
+clintc10
+billch10
+kench10
+lojc10
+ntxac10
+johnch10
+sorlet10
+kimc10
+weiyc10
+rrycc10
+ritac10
+pierrc10
+tomch10
+henryc10
+willch10
+rrkc10
+ntxsch10
+daich10
+yukoc10
+abm10
+acgc10
+ntxjch10
+rrtcp10
+jojoc10
+krisch10
+ronch10
+javch10
+ntxrac10
+sebasc10
+andrwc10
+gkkc10
+yvonnc10
+kingsc10
+bibich10
+ntxkch10
+philci10
+ccipol10
+paulc10
+tomci10
+waltci10
+andyc10
+bencl10
+dclark10
+ntxjcl10
+johcl10
+kclark10
+vspc10
+rrsc10
+derekc10
+johncl10
+ntxdc10
+ibmrc10
+shimaa10
+silkea10
+davida10
+chrial10
+eallen10
+susana10
+joseal10
+stefam10
+paulam10
+mettea10
+stinev10
+henrik10
+ralpha10
+robann10
+csilla10
+erica10
+marcoa10
+malia10
+ericas10
+alexat10
+maubry10
+anab10
+alexib10
+nicolb10
+urib10
+shantb10
+mikeba10
+andbar10
+beckyb10
+scottb10
+leonib10
+mariab10
+reemb10
+maddib10
+marieb10
+romab10
+ericba10
+brianb10
+josefb10
+chadb10
+ronb10
+almub10
+tinab10
+monikb10
+joelb10
+yannb10
+tarunb10
+stephb10
+veronb10
+stefab10
+bernb10
+dougbl10
+boccom10
+laurib10
+johanb10
+erinb10
+sinbo10
+michab10
+markbo10
+julb10
+kamilb10
+laurb10
+colinb10
+taniab10
+pbrad10
+jonbr10
+edb10
+annieb10
+jmbrie10
+lisab10
+joakib10
+kbrost10
+aaronb10
+paulbr10
+seambr10
+elainb10
+kaib10
+alainb10
+courtb10
+richbu10
+liambu10
+ferbu10
+hanneb10
+felicb10
+seanb10
+maytec10
+nelsc10
+armanc10
+nicoc10
+camcar10
+juanjc10
+tiziac10
+marcoc10
+tonyc10
+jpcast10
+damiac10
+ignac10
+sabinc10
+gordc10
+jaruc10
+cbchae10
+ammonc10
+jaksch10
+stephc10
+jimc10
+normac10
+fredc10
+richch10
+cheric10
+ericc10
+dmchoi10
+gabc10
+plchua10
+josefc10
+crisci10
+mikecl10
+coracl10
+danco10
+antonc10
+maxinc10
+karenc10
+cecelw10
+alberc10
+claudc10
+ajc10
+valerc10
+maxicu10
+brenc10
+wolfz10
+maxic10
+peteda10
+jacqd10
+robd10
+bobk10
+hiltom10
+katjad10
+marnod10
+serged10
+antond10
+michde10
+freddv10
+hansd10
+tdiver10
+ylvad10
+emerd10
+chelod10
+julied10
+jdrage10
+markdr10
+chridu10
+yvand10
+delphd10
+leed10
+michd10
+betind10
+stephv10
+georgd10
+owenea10
+stepea10
+owene10
+kime10
+rone10
+helene10
+annete10
+marcee10
+gabreh10
+nathfa10
+helenf10
+masudf10
+jeruf10
+brunof10
+marif10
+debbif10
+jonf10
+joachf10
+cathf10
+pennyf10
+kimbef10
+andref10
+billf10
+damfin10
+solfo10
+jacquf10
+pacof10
+antonf10
+tomfr10
+bjornf10
+mickeg10
+werng10
+mariag10
+julieg10
+georgg10
+roxang10
+beng10
+mardcg10
+rafg10
+jimeg10
+maiog10
+laurga10
+giagat10
+mattga10
+laurg10
+bgenar10
+fredge10
+lidiag10
+stefag10
+michag10
+minah10
+mgietl10
+robg10
+juligm10
+stephg10
+dannyg10
+davidg10
+xavg10
+sophig10
+danieg10
+cgold10
+merisg10
+osnatg10
+josem10
+diman10
+fredgo10
+erendg10
+luisgo10
+rosag10
+grantg10
+andygo10
+sgort10
+jeanpg10
+geneg10
+scottg10
+chiarg10
+richgr10
+bruceg10
+adamg10
+avishg10
+sandg10
+sophg10
+andreg10
+ugrob10
+sissyg10
+guenth10
+jhaas10
+erezh10
+mariha10
+tommyh10
+ahaigh10
+svenh10
+jackih10
+johnha10
+perh10
+katreh10
+gerhar10
+charlh10
+sanjh10
+robhau10
+tomokh10
+erich10
+brenhe10
+andreh10
+markuh10
+raphh10
+bernh10
+brendh10
+olafh10
+klaush10
+enidh10
+ayolh10
+jorgeh10
+bobhe10
+adrih10
+loreth10
+juanjh10
+stefah10
+gerdh10
+kathah10
+maxh10
+micheh10
+juergh10
+scotth10
+torhol10
+toddh10
+szabh10
+martah10
+shood10
+jillh10
+shannh10
+clarah10
+liborh10
+jhosty10
+chanth10
+amyhs10
+helenh10
+chhsu10
+franh10
+janyh10
+tracyh10
+dusth10
+davhug10
+bradh10
+moyah10
+jormah10
+mustai10
+kevini10
+leei10
+junkoi10
+jamest10
+karenj10
+joej10
+davidj10
+haylj10
+rosej10
+gertj10
+lanaj10
+hollyj10
+jcjan10
+isek10
+alexka10
+giselk10
+mihok10
+annemk10
+peteke10
+lizak10
+hyeyk10
+hskim10
+bking10
+timk10
+cko10
+fannyk10
+davek10
+rolank10
+craigk10
+martik10
+kotay10
+fredk10
+carolk10
+georgk10
+chrisk
+geirk10
+juliak10
+andrk10
+mank10
+kerstk10
+tckuo10
+tomokk10
+skwan10
+tony10
+markul10
+johnla10
+drewl10
+darlan10
+kenl10
+oritl10
+benjla10
+klau10
+markl10
+mlaur10
+delph10
+pyledu10
+evinl10
+dlee10
+hjlee10
+hklee10
+ivyl10
+jonsul10
+kwlee10
+sjlee10
+wglee10
+ivole10
+peterl10
+steple10
+nikil10
+serenl10
+glinah10
+lsliau10
+hannal10
+rafael10
+angell10
+roblim10
+merjal10
+billli10
+avihal10
+birdlo10
+sergel10
+alberl10
+helenl10
+carlol10
+josel10
+juanl10
+annel10
+lilyl10
+gerryl10
+vicenl10
+nathl10
+edlf10
+editl10
+derekl10
+saml10
+alphal10
+kirbyl10
+luisl10
+dickl10
+torull10
+cristm10
+mmaerk10
+leonm10
+sergm10
+brendm10
+andrwm10
+tinym10
+magdem10
+franma10
+kaym10
+nichm10
+caswem10
+arnam10
+rickm10
+carlam10
+helenm10
+farish10
+majda10
+sarya10
+monaa10
+ahmada10
+tawfad10
+dach10
+krissa10
+krisa10
+paula10
+aleena10
+debraa10
+bja10
+radams10
+stepha10
+josepa10
+colbya10
+allana10
+shola10
+dawna10
+wahaba10
+conraa10
+paulal10
+ralexa10
+ricka10
+willal10
+beckya10
+darrea10
+bkena10
+saraha10
+trical10
+callis10
+bga10
+anasal10
+emada10
+marya10
+kamila10
+marca10
+amyan10
+ericaa10
+janeta10
+tinaa10
+garant10
+franka10
+joea10
+davapp10
+denisa10
+stevar10
+briana10
+mikea10
+randya10
+rafar10
+noela10
+michas10
+davea10
+kellas10
+petash10
+alexa10
+paulat10
+teresa10
+daveat10
+heidia10
+mikeau10
+amya10
+cloa10
+akilaz10
+aprib10
+tricib10
+bruceb10
+chrisb10
+ryanba10
+tomb10
+tohoub10
+pbain10
+bwb10
+gayleb10
+branb10
+sharib10
+johnb10
+dbanks10
+rayb10
+cbarb10
+waldb10
+larrba10
+jeremb10
+mimid10
+garyba10
+bpb10
+hughb10
+kenba10
+markba10
+alib10
+koyb10
+patb10
+dbaur10
+jerib10
+alexb10
+brubax10
+gregbe10
+laurib10
+annb10
+aarob10
+johnbe10
+karbec10
+davb10
+dbeers10
+bmbe10
+teresb10
+paulbe10
+aaronb10
+griogb10
+ericb10
+tracbe10
+janeb10
+bradb10
+leslib10
+tinab10
+dougbe10
+jonib10
+joelbe10
+sharb10
+dougb10
+andreb10
+kberg10
+ryanb10
+cynthb10
+dberry10
+cherbe10
+bmarcb10
+tamib10
+gileb10
+patbe10
+mikebi10
+bryab10
+kathb10
+jamebi10
+kenb10
+billbi10
+jennib10
+danbl10
+jasonb10
+philib10
+alisbl10
+amybl10
+camib10
+alexbl10
+patbl10
+carlab10
+aprilb10
+marcbl10
+brandb10
+kristb10
+jbolic10
+garybo10
+ellenb10
+eribo10
+courtb10
+coletb10
+larryb10
+marcb10
diff --git a/private/net/svcdlls/lls/test/ct/n2.dat b/private/net/svcdlls/lls/test/ct/n2.dat
new file mode 100644
index 000000000..bd0b3f8d2
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n2.dat
@@ -0,0 +1,1000 @@
+house2
+ronaar2
+rrja2
+ntxfa2
+ninaa2
+cabram2
+johnac2
+martad2
+acca2
+rrea2
+markad2
+acma2
+byrona2
+yosha2
+julioa2
+ccaa2
+bertag2
+ibmma2
+acmah2
+noriya2
+hakana2
+lisac2
+johnal2
+melisa2
+boba2
+rayall2
+deba2
+jeffal2
+rallen2
+laural2
+lallyn2
+venesa2
+annaal2
+nancya2
+dinoa2
+bain2
+marioa2
+raquea2
+vitora2
+scotam2
+renam2
+eglea2
+audrea2
+chrisa2
+ntxha2
+curta2
+davand2
+davean2
+ntxga2
+gavana2
+acja2
+jod2
+attma2
+naomia2
+royand2
+sallya2
+toman2
+matsan2
+jamiea2
+rickan2
+jana2
+jeffan2
+jiman2
+bevaok2
+takaya2
+yumia2
+carloa2
+takaar2
+shinar2
+yukaa2
+tonyar2
+hiroar2
+suza2
+kenar2
+eadarm2
+loria2
+ntxra2
+richar2
+carnst2
+arnulw2
+mayuma2
+hidas2
+richat2
+idoa2
+ntxbat2
+markat2
+kevana2
+gillea2
+micha2
+joset2
+ntxba2
+gma2
+saorib2
+vikkib2
+garyba2
+suzieb2
+michba2
+danbag2
+frankb2
+larryb2
+sbaird2
+rrkeb2
+jolynb2
+paulb2
+sbaker2
+valb2
+leeb2
+geoba2
+dougba2
+ctpsb2
+hughb2
+galb2
+leonib2
+patba2
+ntxjb2
+rrjba2
+donbar2
+jacbar2
+roberb2
+larrb2
+allpak2
+mosesb2
+donnab2
+cibelb2
+vspb2
+peteba2
+abates2
+marcb2
+pbcnz2
+jbattl2
+jimbau2
+cbaum2
+danbau2
+bilbax2
+kathbe2
+ntxab2
+celinb2
+karenb2
+philb2
+darryb2
+ctpdb2
+scre92
+ccsb2
+stepbe2
+ccjben2
+leneb2
+ntxnb2
+pbenoi2
+vscb2
+micheb2
+rrjimb2
+chadb2
+karib2
+nathb2
+dougbe2
+kimmob2
+amyb2
+decdb2
+ebenb2
+acabe2
+andb2
+ntxsbe2
+michbe2
+geralb2
+juditb2
+savlob2
+dsiwm2
+willbi2
+ellenb2
+joseb2
+blasb2
+thomb2
+annicb2
+marbin2
+ntxmb2
+daveb2
+dirkb2
+suebi2
+stefb2
+patbi2
+hilmib2
+heleb2
+rrcb2
+cblack2
+richbl2
+brynb2
+alanb2
+hyattb2
+donnbl2
+cblasi2
+sbliss2
+janebl2
+fransb2
+inface2
+lucibo2
+pboehm2
+joyb2
+pboffo2
+johnbo2
+airex2
+bobb2
+kierab2
+laurbo2
+danbol2
+cathab2
+kathbo2
+cathb2
+coreyb2
+garyb2
+dadcmb2
+nicolb2
+ismb2
+davidb2
+tomb2
+ntxsb2
+timbo2
+ronbo2
+benjab2
+toddb2
+cheryb2
+vskb2
+shellb2
+timboy2
+bboyle2
+jboyle2
+kaijab2
+myriab2
+mbrand2
+brianb2
+darreb2
+simonb2
+geneb2
+tracyb2
+lisbd2
+ibmdb2
+pbrenn2
+sineab2
+susb2
+bryanb2
+ebrew2
+donbr2
+noelb2
+elizbr2
+galeb2
+davb2
+billbr2
+bribro2
+sophib2
+henryb2
+philbr2
+donb2
+amybr2
+cbrown2
+dianab2
+acjb2
+kirstb2
+lesb2
+rbrown2
+ntxrbr2
+albru2
+sbrug2
+ludob2
+vssb2
+paulbr2
+ccrb2
+michbu2
+larrbu2
+lisab2
+carolb2
+peteb2
+actb2
+klausb2
+telen2
+sbuhr2
+louisb2
+miltb2
+heathb2
+helmb2
+bburke2
+catbu2
+billb2
+dburn2
+kaeb2
+ronb2
+eileeb2
+brettb2
+laurab2
+ccfb2
+gunteb2
+gregb2
+robbu2
+robut2
+acwb2
+faribu2
+noelby2
+ntxpbw2
+eddac2
+oscarc2
+scre222
+anneca2
+jocelc2
+barbca2
+janinc2
+jasonc2
+michca2
+edc2
+pedroc2
+suzc2
+wandac2
+davc2
+carenc2
+joanc2
+leeca2
+markca2
+pcamp2
+julioc2
+mikec2
+lucisc2
+georgc2
+tomasb2
+antonc2
+stevec2
+jimc2
+dencar2
+tomc2
+ntxpc2
+annsc2
+jcarne2
+nanc2
+patcar2
+loric2
+patca2
+davic2
+kimca2
+acmc2
+wilmac2
+maurc2
+kcart2
+gregco2
+ginnyc2
+iainc2
+philc2
+riccar2
+steca2
+ntxsc2
+louisc2
+gianca2
+annec2
+jjcav2
+kencav2
+ntxgc2
+jeffca2
+palmac2
+vsmc2
+forms2
+ntxfc2
+cchad2
+cchali2
+troyc2
+cecilc2
+carmc2
+jasch2
+sophic2
+abbotc2
+inac2
+lchang2
+nicolc2
+vchang2
+clintc2
+billch2
+kench2
+lojc2
+ntxac2
+johnch2
+sorlet2
+kimc2
+weiyc2
+rrycc2
+ritac2
+pierrc2
+tomch2
+henryc2
+willch2
+rrkc2
+ntxsch2
+daich2
+yukoc2
+abm2
+acgc2
+ntxjch2
+rrtcp2
+jojoc2
+krisch2
+ronch2
+javch2
+ntxrac2
+sebasc2
+andrwc2
+gkkc2
+yvonnc2
+kingsc2
+bibich2
+ntxkch2
+philci2
+ccipol2
+paulc2
+tomci2
+waltci2
+andyc2
+bencl2
+dclark2
+ntxjcl2
+johcl2
+kclark2
+vspc2
+rrsc2
+derekc2
+johncl2
+ntxdc2
+ibmrc2
+shimaa2
+silkea2
+davida2
+chrial2
+eallen2
+susana2
+joseal2
+stefam2
+paulam2
+mettea2
+stinev2
+henrik2
+ralpha2
+robann2
+csilla2
+erica2
+marcoa2
+malia2
+ericas2
+alexat2
+maubry2
+anab2
+alexib2
+nicolb2
+urib2
+shantb2
+mikeba2
+andbar2
+beckyb2
+scottb2
+leonib2
+mariab2
+reemb2
+maddib2
+marieb2
+romab2
+ericba2
+brianb2
+josefb2
+chadb2
+ronb2
+almub2
+tinab2
+monikb2
+joelb2
+yannb2
+tarunb2
+stephb2
+veronb2
+stefab2
+bernb2
+dougbl2
+boccom2
+laurib2
+johanb2
+erinb2
+sinbo2
+michab2
+markbo2
+julb2
+kamilb2
+laurb2
+colinb2
+taniab2
+pbrad2
+jonbr2
+edb2
+annieb2
+jmbrie2
+lisab2
+joakib2
+kbrost2
+aaronb2
+paulbr2
+seambr2
+elainb2
+kaib2
+alainb2
+courtb2
+richbu2
+liambu2
+ferbu2
+hanneb2
+felicb2
+seanb2
+maytec2
+nelsc2
+armanc2
+nicoc2
+camcar2
+juanjc2
+tiziac2
+marcoc2
+tonyc2
+jpcast2
+damiac2
+ignac2
+sabinc2
+gordc2
+jaruc2
+cbchae2
+ammonc2
+jaksch2
+stephc2
+jimc2
+normac2
+fredc2
+richch2
+cheric2
+ericc2
+dmchoi2
+gabc2
+plchua2
+josefc2
+crisci2
+mikecl2
+coracl2
+danco2
+antonc2
+maxinc2
+karenc2
+cecelw2
+alberc2
+claudc2
+ajc2
+valerc2
+maxicu2
+brenc2
+wolfz2
+maxic2
+peteda2
+jacqd2
+robd2
+bobk2
+hiltom2
+katjad2
+marnod2
+serged2
+antond2
+michde2
+freddv2
+hansd2
+tdiver2
+ylvad2
+emerd2
+chelod2
+julied2
+jdrage2
+markdr2
+chridu2
+yvand2
+delphd2
+leed2
+michd2
+betind2
+stephv2
+georgd2
+owenea2
+stepea2
+owene2
+kime2
+rone2
+helene2
+annete2
+marcee2
+gabreh2
+nathfa2
+helenf2
+masudf2
+jeruf2
+brunof2
+marif2
+debbif2
+jonf2
+joachf2
+cathf2
+pennyf2
+kimbef2
+andref2
+billf2
+damfin2
+solfo2
+jacquf2
+pacof2
+antonf2
+tomfr2
+bjornf2
+mickeg2
+werng2
+mariag2
+julieg2
+georgg2
+roxang2
+beng2
+mardcg2
+rafg2
+jimeg2
+maiog2
+laurga2
+giagat2
+mattga2
+laurg2
+bgenar2
+fredge2
+lidiag2
+stefag2
+michag2
+minah2
+mgietl2
+robg2
+juligm2
+stephg2
+dannyg2
+davidg2
+xavg2
+sophig2
+danieg2
+cgold2
+merisg2
+osnatg2
+josem2
+diman2
+fredgo2
+erendg2
+luisgo2
+rosag2
+grantg2
+andygo2
+sgort2
+jeanpg2
+geneg2
+scottg2
+chiarg2
+richgr2
+bruceg2
+adamg2
+avishg2
+sandg2
+sophg2
+andreg2
+ugrob2
+sissyg2
+guenth2
+jhaas2
+erezh2
+mariha2
+tommyh2
+ahaigh2
+svenh2
+jackih2
+johnha2
+perh2
+katreh2
+gerhar2
+charlh2
+sanjh2
+robhau2
+tomokh2
+erich2
+brenhe2
+andreh2
+markuh2
+raphh2
+bernh2
+brendh2
+olafh2
+klaush2
+enidh2
+ayolh2
+jorgeh2
+bobhe2
+adrih2
+loreth2
+juanjh2
+stefah2
+gerdh2
+kathah2
+maxh2
+micheh2
+juergh2
+scotth2
+torhol2
+toddh2
+szabh2
+martah2
+shood2
+jillh2
+shannh2
+clarah2
+liborh2
+jhosty2
+chanth2
+amyhs2
+helenh2
+chhsu2
+franh2
+janyh2
+tracyh2
+dusth2
+davhug2
+bradh2
+moyah2
+jormah2
+mustai2
+kevini2
+leei2
+junkoi2
+jamest2
+karenj2
+joej2
+davidj2
+haylj2
+rosej2
+gertj2
+lanaj2
+hollyj2
+jcjan2
+isek2
+alexka2
+giselk2
+mihok2
+annemk2
+peteke2
+lizak2
+hyeyk2
+hskim2
+bking2
+timk2
+cko2
+fannyk2
+davek2
+rolank2
+craigk2
+martik2
+kotay2
+fredk2
+carolk2
+georgk2
+chrisk
+geirk2
+juliak2
+andrk2
+mank2
+kerstk2
+tckuo2
+tomokk2
+skwan2
+tony2
+markul2
+johnla2
+drewl2
+darlan2
+kenl2
+oritl2
+benjla2
+klau2
+markl2
+mlaur2
+delph2
+pyledu2
+evinl2
+dlee2
+hjlee2
+hklee2
+ivyl2
+jonsul2
+kwlee2
+sjlee2
+wglee2
+ivole2
+peterl2
+steple2
+nikil2
+serenl2
+glinah2
+lsliau2
+hannal2
+rafael2
+angell2
+roblim2
+merjal2
+billli2
+avihal2
+birdlo2
+sergel2
+alberl2
+helenl2
+carlol2
+josel2
+juanl2
+annel2
+lilyl2
+gerryl2
+vicenl2
+nathl2
+edlf2
+editl2
+derekl2
+saml2
+alphal2
+kirbyl2
+luisl2
+dickl2
+torull2
+cristm2
+mmaerk2
+leonm2
+sergm2
+brendm2
+andrwm2
+tinym2
+magdem2
+franma2
+kaym2
+nichm2
+caswem2
+arnam2
+rickm2
+carlam2
+helenm2
+farish2
+majda2
+sarya2
+monaa2
+ahmada2
+tawfad2
+dach2
+krissa2
+krisa2
+paula2
+aleena2
+debraa2
+bja2
+radams2
+stepha2
+josepa2
+colbya2
+allana2
+shola2
+dawna2
+wahaba2
+conraa2
+paulal2
+ralexa2
+ricka2
+willal2
+beckya2
+darrea2
+bkena2
+saraha2
+trical2
+callis2
+bga2
+anasal2
+emada2
+marya2
+kamila2
+marca2
+amyan2
+ericaa2
+janeta2
+tinaa2
+garant2
+franka2
+joea2
+davapp2
+denisa2
+stevar2
+briana2
+mikea2
+randya2
+rafar2
+noela2
+michas2
+davea2
+kellas2
+petash2
+alexa2
+paulat2
+teresa2
+daveat2
+heidia2
+mikeau2
+amya2
+cloa2
+akilaz2
+aprib2
+tricib2
+bruceb2
+chrisb2
+ryanba2
+tomb2
+tohoub2
+pbain2
+bwb2
+gayleb2
+branb2
+sharib2
+johnb2
+dbanks2
+rayb2
+cbarb2
+waldb2
+larrba2
+jeremb2
+mimid2
+garyba2
+bpb2
+hughb2
+kenba2
+markba2
+alib2
+koyb2
+patb2
+dbaur2
+jerib2
+alexb2
+brubax2
+gregbe2
+laurib2
+annb2
+aarob2
+johnbe2
+karbec2
+davb2
+dbeers2
+bmbe2
+teresb2
+paulbe2
+aaronb2
+griogb2
+ericb2
+tracbe2
+janeb2
+bradb2
+leslib2
+tinab2
+dougbe2
+jonib2
+joelbe2
+sharb2
+dougb2
+andreb2
+kberg2
+ryanb2
+cynthb2
+dberry2
+cherbe2
+bmarcb2
+tamib2
+gileb2
+patbe2
+mikebi2
+bryab2
+kathb2
+jamebi2
+kenb2
+billbi2
+jennib2
+danbl2
+jasonb2
+philib2
+alisbl2
+amybl2
+camib2
+alexbl2
+patbl2
+carlab2
+aprilb2
+marcbl2
+brandb2
+kristb2
+jbolic2
+garybo2
+ellenb2
+eribo2
+courtb2
+coletb2
+larryb2
+marcb2
diff --git a/private/net/svcdlls/lls/test/ct/n3.dat b/private/net/svcdlls/lls/test/ct/n3.dat
new file mode 100644
index 000000000..97fcd0ecd
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n3.dat
@@ -0,0 +1,1000 @@
+house3
+ronaar3
+rrja3
+ntxfa3
+ninaa3
+cabram3
+johnac3
+martad3
+acca3
+rrea3
+markad3
+acma3
+byrona3
+yosha3
+julioa3
+ccaa3
+bertag3
+ibmma3
+acmah3
+noriya3
+hakana3
+lisac3
+johnal3
+melisa3
+boba3
+rayall3
+deba3
+jeffal3
+rallen3
+laural3
+lallyn3
+venesa3
+annaal3
+nancya3
+dinoa3
+bain3
+marioa3
+raquea3
+vitora3
+scotam3
+renam3
+eglea3
+audrea3
+chrisa3
+ntxha3
+curta3
+davand3
+davean3
+ntxga3
+gavana3
+acja3
+jod3
+attma3
+naomia3
+royand3
+sallya3
+toman3
+matsan3
+jamiea3
+rickan3
+jana3
+jeffan3
+jiman3
+bevaok3
+takaya3
+yumia3
+carloa3
+takaar3
+shinar3
+yukaa3
+tonyar3
+hiroar3
+suza3
+kenar3
+eadarm3
+loria3
+ntxra3
+richar3
+carnst3
+arnulw3
+mayuma3
+hidas3
+richat3
+idoa3
+ntxbat3
+markat3
+kevana3
+gillea3
+micha3
+joset3
+ntxba3
+gma3
+saorib3
+vikkib3
+garyba3
+suzieb3
+michba3
+danbag3
+frankb3
+larryb3
+sbaird3
+rrkeb3
+jolynb3
+paulb3
+sbaker3
+valb3
+leeb3
+geoba3
+dougba3
+ctpsb3
+hughb3
+galb3
+leonib3
+patba3
+ntxjb3
+rrjba3
+donbar3
+jacbar3
+roberb3
+larrb3
+allpak3
+mosesb3
+donnab3
+cibelb3
+vspb3
+peteba3
+abates3
+marcb3
+pbcnz3
+jbattl3
+jimbau3
+cbaum3
+danbau3
+bilbax3
+kathbe3
+ntxab3
+celinb3
+karenb3
+philb3
+darryb3
+ctpdb3
+scre93
+ccsb3
+stepbe3
+ccjben3
+leneb3
+ntxnb3
+pbenoi3
+vscb3
+micheb3
+rrjimb3
+chadb3
+karib3
+nathb3
+dougbe3
+kimmob3
+amyb3
+decdb3
+ebenb3
+acabe3
+andb3
+ntxsbe3
+michbe3
+geralb3
+juditb3
+savlob3
+dsiwm3
+willbi3
+ellenb3
+joseb3
+blasb3
+thomb3
+annicb3
+marbin3
+ntxmb3
+daveb3
+dirkb3
+suebi3
+stefb3
+patbi3
+hilmib3
+heleb3
+rrcb3
+cblack3
+richbl3
+brynb3
+alanb3
+hyattb3
+donnbl3
+cblasi3
+sbliss3
+janebl3
+fransb3
+inface3
+lucibo3
+pboehm3
+joyb3
+pboffo3
+johnbo3
+airex3
+bobb3
+kierab3
+laurbo3
+danbol3
+cathab3
+kathbo3
+cathb3
+coreyb3
+garyb3
+dadcmb3
+nicolb3
+ismb3
+davidb3
+tomb3
+ntxsb3
+timbo3
+ronbo3
+benjab3
+toddb3
+cheryb3
+vskb3
+shellb3
+timboy3
+bboyle3
+jboyle3
+kaijab3
+myriab3
+mbrand3
+brianb3
+darreb3
+simonb3
+geneb3
+tracyb3
+lisbd3
+ibmdb3
+pbrenn3
+sineab3
+susb3
+bryanb3
+ebrew3
+donbr3
+noelb3
+elizbr3
+galeb3
+davb3
+billbr3
+bribro3
+sophib3
+henryb3
+philbr3
+donb3
+amybr3
+cbrown3
+dianab3
+acjb3
+kirstb3
+lesb3
+rbrown3
+ntxrbr3
+albru3
+sbrug3
+ludob3
+vssb3
+paulbr3
+ccrb3
+michbu3
+larrbu3
+lisab3
+carolb3
+peteb3
+actb3
+klausb3
+telen3
+sbuhr3
+louisb3
+miltb3
+heathb3
+helmb3
+bburke3
+catbu3
+billb3
+dburn3
+kaeb3
+ronb3
+eileeb3
+brettb3
+laurab3
+ccfb3
+gunteb3
+gregb3
+robbu3
+robut3
+acwb3
+faribu3
+noelby3
+ntxpbw3
+eddac3
+oscarc3
+scre323
+anneca3
+jocelc3
+barbca3
+janinc3
+jasonc3
+michca3
+edc3
+pedroc3
+suzc3
+wandac3
+davc3
+carenc3
+joanc3
+leeca3
+markca3
+pcamp3
+julioc3
+mikec3
+lucisc3
+georgc3
+tomasb3
+antonc3
+stevec3
+jimc3
+dencar3
+tomc3
+ntxpc3
+annsc3
+jcarne3
+nanc3
+patcar3
+loric3
+patca3
+davic3
+kimca3
+acmc3
+wilmac3
+maurc3
+kcart3
+gregco3
+ginnyc3
+iainc3
+philc3
+riccar3
+steca3
+ntxsc3
+louisc3
+gianca3
+annec3
+jjcav3
+kencav3
+ntxgc3
+jeffca3
+palmac3
+vsmc3
+forms3
+ntxfc3
+cchad3
+cchali3
+troyc3
+cecilc3
+carmc3
+jasch3
+sophic3
+abbotc3
+inac3
+lchang3
+nicolc3
+vchang3
+clintc3
+billch3
+kench3
+lojc3
+ntxac3
+johnch3
+sorlet3
+kimc3
+weiyc3
+rrycc3
+ritac3
+pierrc3
+tomch3
+henryc3
+willch3
+rrkc3
+ntxsch3
+daich3
+yukoc3
+abm3
+acgc3
+ntxjch3
+rrtcp3
+jojoc3
+krisch3
+ronch3
+javch3
+ntxrac3
+sebasc3
+andrwc3
+gkkc3
+yvonnc3
+kingsc3
+bibich3
+ntxkch3
+philci3
+ccipol3
+paulc3
+tomci3
+waltci3
+andyc3
+bencl3
+dclark3
+ntxjcl3
+johcl3
+kclark3
+vspc3
+rrsc3
+derekc3
+johncl3
+ntxdc3
+ibmrc3
+shimaa3
+silkea3
+davida3
+chrial3
+eallen3
+susana3
+joseal3
+stefam3
+paulam3
+mettea3
+stinev3
+henrik3
+ralpha3
+robann3
+csilla3
+erica3
+marcoa3
+malia3
+ericas3
+alexat3
+maubry3
+anab3
+alexib3
+nicolb3
+urib3
+shantb3
+mikeba3
+andbar3
+beckyb3
+scottb3
+leonib3
+mariab3
+reemb3
+maddib3
+marieb3
+romab3
+ericba3
+brianb3
+josefb3
+chadb3
+ronb3
+almub3
+tinab3
+monikb3
+joelb3
+yannb3
+tarunb3
+stephb3
+veronb3
+stefab3
+bernb3
+dougbl3
+boccom3
+laurib3
+johanb3
+erinb3
+sinbo3
+michab3
+markbo3
+julb3
+kamilb3
+laurb3
+colinb3
+taniab3
+pbrad3
+jonbr3
+edb3
+annieb3
+jmbrie3
+lisab3
+joakib3
+kbrost3
+aaronb3
+paulbr3
+seambr3
+elainb3
+kaib3
+alainb3
+courtb3
+richbu3
+liambu3
+ferbu3
+hanneb3
+felicb3
+seanb3
+maytec3
+nelsc3
+armanc3
+nicoc3
+camcar3
+juanjc3
+tiziac3
+marcoc3
+tonyc3
+jpcast3
+damiac3
+ignac3
+sabinc3
+gordc3
+jaruc3
+cbchae3
+ammonc3
+jaksch3
+stephc3
+jimc3
+normac3
+fredc3
+richch3
+cheric3
+ericc3
+dmchoi3
+gabc3
+plchua3
+josefc3
+crisci3
+mikecl3
+coracl3
+danco3
+antonc3
+maxinc3
+karenc3
+cecelw3
+alberc3
+claudc3
+ajc3
+valerc3
+maxicu3
+brenc3
+wolfz3
+maxic3
+peteda3
+jacqd3
+robd3
+bobk3
+hiltom3
+katjad3
+marnod3
+serged3
+antond3
+michde3
+freddv3
+hansd3
+tdiver3
+ylvad3
+emerd3
+chelod3
+julied3
+jdrage3
+markdr3
+chridu3
+yvand3
+delphd3
+leed3
+michd3
+betind3
+stephv3
+georgd3
+owenea3
+stepea3
+owene3
+kime3
+rone3
+helene3
+annete3
+marcee3
+gabreh3
+nathfa3
+helenf3
+masudf3
+jeruf3
+brunof3
+marif3
+debbif3
+jonf3
+joachf3
+cathf3
+pennyf3
+kimbef3
+andref3
+billf3
+damfin3
+solfo3
+jacquf3
+pacof3
+antonf3
+tomfr3
+bjornf3
+mickeg3
+werng3
+mariag3
+julieg3
+georgg3
+roxang3
+beng3
+mardcg3
+rafg3
+jimeg3
+maiog3
+laurga3
+giagat3
+mattga3
+laurg3
+bgenar3
+fredge3
+lidiag3
+stefag3
+michag3
+minah3
+mgietl3
+robg3
+juligm3
+stephg3
+dannyg3
+davidg3
+xavg3
+sophig3
+danieg3
+cgold3
+merisg3
+osnatg3
+josem3
+diman3
+fredgo3
+erendg3
+luisgo3
+rosag3
+grantg3
+andygo3
+sgort3
+jeanpg3
+geneg3
+scottg3
+chiarg3
+richgr3
+bruceg3
+adamg3
+avishg3
+sandg3
+sophg3
+andreg3
+ugrob3
+sissyg3
+guenth3
+jhaas3
+erezh3
+mariha3
+tommyh3
+ahaigh3
+svenh3
+jackih3
+johnha3
+perh3
+katreh3
+gerhar3
+charlh3
+sanjh3
+robhau3
+tomokh3
+erich3
+brenhe3
+andreh3
+markuh3
+raphh3
+bernh3
+brendh3
+olafh3
+klaush3
+enidh3
+ayolh3
+jorgeh3
+bobhe3
+adrih3
+loreth3
+juanjh3
+stefah3
+gerdh3
+kathah3
+maxh3
+micheh3
+juergh3
+scotth3
+torhol3
+toddh3
+szabh3
+martah3
+shood3
+jillh3
+shannh3
+clarah3
+liborh3
+jhosty3
+chanth3
+amyhs3
+helenh3
+chhsu3
+franh3
+janyh3
+tracyh3
+dusth3
+davhug3
+bradh3
+moyah3
+jormah3
+mustai3
+kevini3
+leei3
+junkoi3
+jamest3
+karenj3
+joej3
+davidj3
+haylj3
+rosej3
+gertj3
+lanaj3
+hollyj3
+jcjan3
+isek3
+alexka3
+giselk3
+mihok3
+annemk3
+peteke3
+lizak3
+hyeyk3
+hskim3
+bking3
+timk3
+cko3
+fannyk3
+davek3
+rolank3
+craigk3
+martik3
+kotay3
+fredk3
+carolk3
+georgk3
+chrisk
+geirk3
+juliak3
+andrk3
+mank3
+kerstk3
+tckuo3
+tomokk3
+skwan3
+tony3
+markul3
+johnla3
+drewl3
+darlan3
+kenl3
+oritl3
+benjla3
+klau3
+markl3
+mlaur3
+delph3
+pyledu3
+evinl3
+dlee3
+hjlee3
+hklee3
+ivyl3
+jonsul3
+kwlee3
+sjlee3
+wglee3
+ivole3
+peterl3
+steple3
+nikil3
+serenl3
+glinah3
+lsliau3
+hannal3
+rafael3
+angell3
+roblim3
+merjal3
+billli3
+avihal3
+birdlo3
+sergel3
+alberl3
+helenl3
+carlol3
+josel3
+juanl3
+annel3
+lilyl3
+gerryl3
+vicenl3
+nathl3
+edlf3
+editl3
+derekl3
+saml3
+alphal3
+kirbyl3
+luisl3
+dickl3
+torull3
+cristm3
+mmaerk3
+leonm3
+sergm3
+brendm3
+andrwm3
+tinym3
+magdem3
+franma3
+kaym3
+nichm3
+caswem3
+arnam3
+rickm3
+carlam3
+helenm3
+farish3
+majda3
+sarya3
+monaa3
+ahmada3
+tawfad3
+dach3
+krissa3
+krisa3
+paula3
+aleena3
+debraa3
+bja3
+radams3
+stepha3
+josepa3
+colbya3
+allana3
+shola3
+dawna3
+wahaba3
+conraa3
+paulal3
+ralexa3
+ricka3
+willal3
+beckya3
+darrea3
+bkena3
+saraha3
+trical3
+callis3
+bga3
+anasal3
+emada3
+marya3
+kamila3
+marca3
+amyan3
+ericaa3
+janeta3
+tinaa3
+garant3
+franka3
+joea3
+davapp3
+denisa3
+stevar3
+briana3
+mikea3
+randya3
+rafar3
+noela3
+michas3
+davea3
+kellas3
+petash3
+alexa3
+paulat3
+teresa3
+daveat3
+heidia3
+mikeau3
+amya3
+cloa3
+akilaz3
+aprib3
+tricib3
+bruceb3
+chrisb3
+ryanba3
+tomb3
+tohoub3
+pbain3
+bwb3
+gayleb3
+branb3
+sharib3
+johnb3
+dbanks3
+rayb3
+cbarb3
+waldb3
+larrba3
+jeremb3
+mimid3
+garyba3
+bpb3
+hughb3
+kenba3
+markba3
+alib3
+koyb3
+patb3
+dbaur3
+jerib3
+alexb3
+brubax3
+gregbe3
+laurib3
+annb3
+aarob3
+johnbe3
+karbec3
+davb3
+dbeers3
+bmbe3
+teresb3
+paulbe3
+aaronb3
+griogb3
+ericb3
+tracbe3
+janeb3
+bradb3
+leslib3
+tinab3
+dougbe3
+jonib3
+joelbe3
+sharb3
+dougb3
+andreb3
+kberg3
+ryanb3
+cynthb3
+dberry3
+cherbe3
+bmarcb3
+tamib3
+gileb3
+patbe3
+mikebi3
+bryab3
+kathb3
+jamebi3
+kenb3
+billbi3
+jennib3
+danbl3
+jasonb3
+philib3
+alisbl3
+amybl3
+camib3
+alexbl3
+patbl3
+carlab3
+aprilb3
+marcbl3
+brandb3
+kristb3
+jbolic3
+garybo3
+ellenb3
+eribo3
+courtb3
+coletb3
+larryb3
+marcb3
diff --git a/private/net/svcdlls/lls/test/ct/n4.dat b/private/net/svcdlls/lls/test/ct/n4.dat
new file mode 100644
index 000000000..156eb0463
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n4.dat
@@ -0,0 +1,1000 @@
+house4
+ronaar4
+rrja4
+ntxfa4
+ninaa4
+cabram4
+johnac4
+martad4
+acca4
+rrea4
+markad4
+acma4
+byrona4
+yosha4
+julioa4
+ccaa4
+bertag4
+ibmma4
+acmah4
+noriya4
+hakana4
+lisac4
+johnal4
+melisa4
+boba4
+rayall4
+deba4
+jeffal4
+rallen4
+laural4
+lallyn4
+venesa4
+annaal4
+nancya4
+dinoa4
+bain4
+marioa4
+raquea4
+vitora4
+scotam4
+renam4
+eglea4
+audrea4
+chrisa4
+ntxha4
+curta4
+davand4
+davean4
+ntxga4
+gavana4
+acja4
+jod4
+attma4
+naomia4
+royand4
+sallya4
+toman4
+matsan4
+jamiea4
+rickan4
+jana4
+jeffan4
+jiman4
+bevaok4
+takaya4
+yumia4
+carloa4
+takaar4
+shinar4
+yukaa4
+tonyar4
+hiroar4
+suza4
+kenar4
+eadarm4
+loria4
+ntxra4
+richar4
+carnst4
+arnulw4
+mayuma4
+hidas4
+richat4
+idoa4
+ntxbat4
+markat4
+kevana4
+gillea4
+micha4
+joset4
+ntxba4
+gma4
+saorib4
+vikkib4
+garyba4
+suzieb4
+michba4
+danbag4
+frankb4
+larryb4
+sbaird4
+rrkeb4
+jolynb4
+paulb4
+sbaker4
+valb4
+leeb4
+geoba4
+dougba4
+ctpsb4
+hughb4
+galb4
+leonib4
+patba4
+ntxjb4
+rrjba4
+donbar4
+jacbar4
+roberb4
+larrb4
+allpak4
+mosesb4
+donnab4
+cibelb4
+vspb4
+peteba4
+abates4
+marcb4
+pbcnz4
+jbattl4
+jimbau4
+cbaum4
+danbau4
+bilbax4
+kathbe4
+ntxab4
+celinb4
+karenb4
+philb4
+darryb4
+ctpdb4
+scre94
+ccsb4
+stepbe4
+ccjben4
+leneb4
+ntxnb4
+pbenoi4
+vscb4
+micheb4
+rrjimb4
+chadb4
+karib4
+nathb4
+dougbe4
+kimmob4
+amyb4
+decdb4
+ebenb4
+acabe4
+andb4
+ntxsbe4
+michbe4
+geralb4
+juditb4
+savlob4
+dsiwm4
+willbi4
+ellenb4
+joseb4
+blasb4
+thomb4
+annicb4
+marbin4
+ntxmb4
+daveb4
+dirkb4
+suebi4
+stefb4
+patbi4
+hilmib4
+heleb4
+rrcb4
+cblack4
+richbl4
+brynb4
+alanb4
+hyattb4
+donnbl4
+cblasi4
+sbliss4
+janebl4
+fransb4
+inface4
+lucibo4
+pboehm4
+joyb4
+pboffo4
+johnbo4
+airex4
+bobb4
+kierab4
+laurbo4
+danbol4
+cathab4
+kathbo4
+cathb4
+coreyb4
+garyb4
+dadcmb4
+nicolb4
+ismb4
+davidb4
+tomb4
+ntxsb4
+timbo4
+ronbo4
+benjab4
+toddb4
+cheryb4
+vskb4
+shellb4
+timboy4
+bboyle4
+jboyle4
+kaijab4
+myriab4
+mbrand4
+brianb4
+darreb4
+simonb4
+geneb4
+tracyb4
+lisbd4
+ibmdb4
+pbrenn4
+sineab4
+susb4
+bryanb4
+ebrew4
+donbr4
+noelb4
+elizbr4
+galeb4
+davb4
+billbr4
+bribro4
+sophib4
+henryb4
+philbr4
+donb4
+amybr4
+cbrown4
+dianab4
+acjb4
+kirstb4
+lesb4
+rbrown4
+ntxrbr4
+albru4
+sbrug4
+ludob4
+vssb4
+paulbr4
+ccrb4
+michbu4
+larrbu4
+lisab4
+carolb4
+peteb4
+actb4
+klausb4
+telen4
+sbuhr4
+louisb4
+miltb4
+heathb4
+helmb4
+bburke4
+catbu4
+billb4
+dburn4
+kaeb4
+ronb4
+eileeb4
+brettb4
+laurab4
+ccfb4
+gunteb4
+gregb4
+robbu4
+robut4
+acwb4
+faribu4
+noelby4
+ntxpbw4
+eddac4
+oscarc4
+scre424
+anneca4
+jocelc4
+barbca4
+janinc4
+jasonc4
+michca4
+edc4
+pedroc4
+suzc4
+wandac4
+davc4
+carenc4
+joanc4
+leeca4
+markca4
+pcamp4
+julioc4
+mikec4
+lucisc4
+georgc4
+tomasb4
+antonc4
+stevec4
+jimc4
+dencar4
+tomc4
+ntxpc4
+annsc4
+jcarne4
+nanc4
+patcar4
+loric4
+patca4
+davic4
+kimca4
+acmc4
+wilmac4
+maurc4
+kcart4
+gregco4
+ginnyc4
+iainc4
+philc4
+riccar4
+steca4
+ntxsc4
+louisc4
+gianca4
+annec4
+jjcav4
+kencav4
+ntxgc4
+jeffca4
+palmac4
+vsmc4
+forms4
+ntxfc4
+cchad4
+cchali4
+troyc4
+cecilc4
+carmc4
+jasch4
+sophic4
+abbotc4
+inac4
+lchang4
+nicolc4
+vchang4
+clintc4
+billch4
+kench4
+lojc4
+ntxac4
+johnch4
+sorlet4
+kimc4
+weiyc4
+rrycc4
+ritac4
+pierrc4
+tomch4
+henryc4
+willch4
+rrkc4
+ntxsch4
+daich4
+yukoc4
+abm4
+acgc4
+ntxjch4
+rrtcp4
+jojoc4
+krisch4
+ronch4
+javch4
+ntxrac4
+sebasc4
+andrwc4
+gkkc4
+yvonnc4
+kingsc4
+bibich4
+ntxkch4
+philci4
+ccipol4
+paulc4
+tomci4
+waltci4
+andyc4
+bencl4
+dclark4
+ntxjcl4
+johcl4
+kclark4
+vspc4
+rrsc4
+derekc4
+johncl4
+ntxdc4
+ibmrc4
+shimaa4
+silkea4
+davida4
+chrial4
+eallen4
+susana4
+joseal4
+stefam4
+paulam4
+mettea4
+stinev4
+henrik4
+ralpha4
+robann4
+csilla4
+erica4
+marcoa4
+malia4
+ericas4
+alexat4
+maubry4
+anab4
+alexib4
+nicolb4
+urib4
+shantb4
+mikeba4
+andbar4
+beckyb4
+scottb4
+leonib4
+mariab4
+reemb4
+maddib4
+marieb4
+romab4
+ericba4
+brianb4
+josefb4
+chadb4
+ronb4
+almub4
+tinab4
+monikb4
+joelb4
+yannb4
+tarunb4
+stephb4
+veronb4
+stefab4
+bernb4
+dougbl4
+boccom4
+laurib4
+johanb4
+erinb4
+sinbo4
+michab4
+markbo4
+julb4
+kamilb4
+laurb4
+colinb4
+taniab4
+pbrad4
+jonbr4
+edb4
+annieb4
+jmbrie4
+lisab4
+joakib4
+kbrost4
+aaronb4
+paulbr4
+seambr4
+elainb4
+kaib4
+alainb4
+courtb4
+richbu4
+liambu4
+ferbu4
+hanneb4
+felicb4
+seanb4
+maytec4
+nelsc4
+armanc4
+nicoc4
+camcar4
+juanjc4
+tiziac4
+marcoc4
+tonyc4
+jpcast4
+damiac4
+ignac4
+sabinc4
+gordc4
+jaruc4
+cbchae4
+ammonc4
+jaksch4
+stephc4
+jimc4
+normac4
+fredc4
+richch4
+cheric4
+ericc4
+dmchoi4
+gabc4
+plchua4
+josefc4
+crisci4
+mikecl4
+coracl4
+danco4
+antonc4
+maxinc4
+karenc4
+cecelw4
+alberc4
+claudc4
+ajc4
+valerc4
+maxicu4
+brenc4
+wolfz4
+maxic4
+peteda4
+jacqd4
+robd4
+bobk4
+hiltom4
+katjad4
+marnod4
+serged4
+antond4
+michde4
+freddv4
+hansd4
+tdiver4
+ylvad4
+emerd4
+chelod4
+julied4
+jdrage4
+markdr4
+chridu4
+yvand4
+delphd4
+leed4
+michd4
+betind4
+stephv4
+georgd4
+owenea4
+stepea4
+owene4
+kime4
+rone4
+helene4
+annete4
+marcee4
+gabreh4
+nathfa4
+helenf4
+masudf4
+jeruf4
+brunof4
+marif4
+debbif4
+jonf4
+joachf4
+cathf4
+pennyf4
+kimbef4
+andref4
+billf4
+damfin4
+solfo4
+jacquf4
+pacof4
+antonf4
+tomfr4
+bjornf4
+mickeg4
+werng4
+mariag4
+julieg4
+georgg4
+roxang4
+beng4
+mardcg4
+rafg4
+jimeg4
+maiog4
+laurga4
+giagat4
+mattga4
+laurg4
+bgenar4
+fredge4
+lidiag4
+stefag4
+michag4
+minah4
+mgietl4
+robg4
+juligm4
+stephg4
+dannyg4
+davidg4
+xavg4
+sophig4
+danieg4
+cgold4
+merisg4
+osnatg4
+josem4
+diman4
+fredgo4
+erendg4
+luisgo4
+rosag4
+grantg4
+andygo4
+sgort4
+jeanpg4
+geneg4
+scottg4
+chiarg4
+richgr4
+bruceg4
+adamg4
+avishg4
+sandg4
+sophg4
+andreg4
+ugrob4
+sissyg4
+guenth4
+jhaas4
+erezh4
+mariha4
+tommyh4
+ahaigh4
+svenh4
+jackih4
+johnha4
+perh4
+katreh4
+gerhar4
+charlh4
+sanjh4
+robhau4
+tomokh4
+erich4
+brenhe4
+andreh4
+markuh4
+raphh4
+bernh4
+brendh4
+olafh4
+klaush4
+enidh4
+ayolh4
+jorgeh4
+bobhe4
+adrih4
+loreth4
+juanjh4
+stefah4
+gerdh4
+kathah4
+maxh4
+micheh4
+juergh4
+scotth4
+torhol4
+toddh4
+szabh4
+martah4
+shood4
+jillh4
+shannh4
+clarah4
+liborh4
+jhosty4
+chanth4
+amyhs4
+helenh4
+chhsu4
+franh4
+janyh4
+tracyh4
+dusth4
+davhug4
+bradh4
+moyah4
+jormah4
+mustai4
+kevini4
+leei4
+junkoi4
+jamest4
+karenj4
+joej4
+davidj4
+haylj4
+rosej4
+gertj4
+lanaj4
+hollyj4
+jcjan4
+isek4
+alexka4
+giselk4
+mihok4
+annemk4
+peteke4
+lizak4
+hyeyk4
+hskim4
+bking4
+timk4
+cko4
+fannyk4
+davek4
+rolank4
+craigk4
+martik4
+kotay4
+fredk4
+carolk4
+georgk4
+chrisk
+geirk4
+juliak4
+andrk4
+mank4
+kerstk4
+tckuo4
+tomokk4
+skwan4
+tony4
+markul4
+johnla4
+drewl4
+darlan4
+kenl4
+oritl4
+benjla4
+klau4
+markl4
+mlaur4
+delph4
+pyledu4
+evinl4
+dlee4
+hjlee4
+hklee4
+ivyl4
+jonsul4
+kwlee4
+sjlee4
+wglee4
+ivole4
+peterl4
+steple4
+nikil4
+serenl4
+glinah4
+lsliau4
+hannal4
+rafael4
+angell4
+roblim4
+merjal4
+billli4
+avihal4
+birdlo4
+sergel4
+alberl4
+helenl4
+carlol4
+josel4
+juanl4
+annel4
+lilyl4
+gerryl4
+vicenl4
+nathl4
+edlf4
+editl4
+derekl4
+saml4
+alphal4
+kirbyl4
+luisl4
+dickl4
+torull4
+cristm4
+mmaerk4
+leonm4
+sergm4
+brendm4
+andrwm4
+tinym4
+magdem4
+franma4
+kaym4
+nichm4
+caswem4
+arnam4
+rickm4
+carlam4
+helenm4
+farish4
+majda4
+sarya4
+monaa4
+ahmada4
+tawfad4
+dach4
+krissa4
+krisa4
+paula4
+aleena4
+debraa4
+bja4
+radams4
+stepha4
+josepa4
+colbya4
+allana4
+shola4
+dawna4
+wahaba4
+conraa4
+paulal4
+ralexa4
+ricka4
+willal4
+beckya4
+darrea4
+bkena4
+saraha4
+trical4
+callis4
+bga4
+anasal4
+emada4
+marya4
+kamila4
+marca4
+amyan4
+ericaa4
+janeta4
+tinaa4
+garant4
+franka4
+joea4
+davapp4
+denisa4
+stevar4
+briana4
+mikea4
+randya4
+rafar4
+noela4
+michas4
+davea4
+kellas4
+petash4
+alexa4
+paulat4
+teresa4
+daveat4
+heidia4
+mikeau4
+amya4
+cloa4
+akilaz4
+aprib4
+tricib4
+bruceb4
+chrisb4
+ryanba4
+tomb4
+tohoub4
+pbain4
+bwb4
+gayleb4
+branb4
+sharib4
+johnb4
+dbanks4
+rayb4
+cbarb4
+waldb4
+larrba4
+jeremb4
+mimid4
+garyba4
+bpb4
+hughb4
+kenba4
+markba4
+alib4
+koyb4
+patb4
+dbaur4
+jerib4
+alexb4
+brubax4
+gregbe4
+laurib4
+annb4
+aarob4
+johnbe4
+karbec4
+davb4
+dbeers4
+bmbe4
+teresb4
+paulbe4
+aaronb4
+griogb4
+ericb4
+tracbe4
+janeb4
+bradb4
+leslib4
+tinab4
+dougbe4
+jonib4
+joelbe4
+sharb4
+dougb4
+andreb4
+kberg4
+ryanb4
+cynthb4
+dberry4
+cherbe4
+bmarcb4
+tamib4
+gileb4
+patbe4
+mikebi4
+bryab4
+kathb4
+jamebi4
+kenb4
+billbi4
+jennib4
+danbl4
+jasonb4
+philib4
+alisbl4
+amybl4
+camib4
+alexbl4
+patbl4
+carlab4
+aprilb4
+marcbl4
+brandb4
+kristb4
+jbolic4
+garybo4
+ellenb4
+eribo4
+courtb4
+coletb4
+larryb4
+marcb4
diff --git a/private/net/svcdlls/lls/test/ct/n5.dat b/private/net/svcdlls/lls/test/ct/n5.dat
new file mode 100644
index 000000000..47c9b3c3d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n5.dat
@@ -0,0 +1,1000 @@
+house5
+ronaar5
+rrja5
+ntxfa5
+ninaa5
+cabram5
+johnac5
+martad5
+acca5
+rrea5
+markad5
+acma5
+byrona5
+yosha5
+julioa5
+ccaa5
+bertag5
+ibmma5
+acmah5
+noriya5
+hakana5
+lisac5
+johnal5
+melisa5
+boba5
+rayall5
+deba5
+jeffal5
+rallen5
+laural5
+lallyn5
+venesa5
+annaal5
+nancya5
+dinoa5
+bain5
+marioa5
+raquea5
+vitora5
+scotam5
+renam5
+eglea5
+audrea5
+chrisa5
+ntxha5
+curta5
+davand5
+davean5
+ntxga5
+gavana5
+acja5
+jod5
+attma5
+naomia5
+royand5
+sallya5
+toman5
+matsan5
+jamiea5
+rickan5
+jana5
+jeffan5
+jiman5
+bevaok5
+takaya5
+yumia5
+carloa5
+takaar5
+shinar5
+yukaa5
+tonyar5
+hiroar5
+suza5
+kenar5
+eadarm5
+loria5
+ntxra5
+richar5
+carnst5
+arnulw5
+mayuma5
+hidas5
+richat5
+idoa5
+ntxbat5
+markat5
+kevana5
+gillea5
+micha5
+joset5
+ntxba5
+gma5
+saorib5
+vikkib5
+garyba5
+suzieb5
+michba5
+danbag5
+frankb5
+larryb5
+sbaird5
+rrkeb5
+jolynb5
+paulb5
+sbaker5
+valb5
+leeb5
+geoba5
+dougba5
+ctpsb5
+hughb5
+galb5
+leonib5
+patba5
+ntxjb5
+rrjba5
+donbar5
+jacbar5
+roberb5
+larrb5
+allpak5
+mosesb5
+donnab5
+cibelb5
+vspb5
+peteba5
+abates5
+marcb5
+pbcnz5
+jbattl5
+jimbau5
+cbaum5
+danbau5
+bilbax5
+kathbe5
+ntxab5
+celinb5
+karenb5
+philb5
+darryb5
+ctpdb5
+scre95
+ccsb5
+stepbe5
+ccjben5
+leneb5
+ntxnb5
+pbenoi5
+vscb5
+micheb5
+rrjimb5
+chadb5
+karib5
+nathb5
+dougbe5
+kimmob5
+amyb5
+decdb5
+ebenb5
+acabe5
+andb5
+ntxsbe5
+michbe5
+geralb5
+juditb5
+savlob5
+dsiwm5
+willbi5
+ellenb5
+joseb5
+blasb5
+thomb5
+annicb5
+marbin5
+ntxmb5
+daveb5
+dirkb5
+suebi5
+stefb5
+patbi5
+hilmib5
+heleb5
+rrcb5
+cblack5
+richbl5
+brynb5
+alanb5
+hyattb5
+donnbl5
+cblasi5
+sbliss5
+janebl5
+fransb5
+inface5
+lucibo5
+pboehm5
+joyb5
+pboffo5
+johnbo5
+airex5
+bobb5
+kierab5
+laurbo5
+danbol5
+cathab5
+kathbo5
+cathb5
+coreyb5
+garyb5
+dadcmb5
+nicolb5
+ismb5
+davidb5
+tomb5
+ntxsb5
+timbo5
+ronbo5
+benjab5
+toddb5
+cheryb5
+vskb5
+shellb5
+timboy5
+bboyle5
+jboyle5
+kaijab5
+myriab5
+mbrand5
+brianb5
+darreb5
+simonb5
+geneb5
+tracyb5
+lisbd5
+ibmdb5
+pbrenn5
+sineab5
+susb5
+bryanb5
+ebrew5
+donbr5
+noelb5
+elizbr5
+galeb5
+davb5
+billbr5
+bribro5
+sophib5
+henryb5
+philbr5
+donb5
+amybr5
+cbrown5
+dianab5
+acjb5
+kirstb5
+lesb5
+rbrown5
+ntxrbr5
+albru5
+sbrug5
+ludob5
+vssb5
+paulbr5
+ccrb5
+michbu5
+larrbu5
+lisab5
+carolb5
+peteb5
+actb5
+klausb5
+telen5
+sbuhr5
+louisb5
+miltb5
+heathb5
+helmb5
+bburke5
+catbu5
+billb5
+dburn5
+kaeb5
+ronb5
+eileeb5
+brettb5
+laurab5
+ccfb5
+gunteb5
+gregb5
+robbu5
+robut5
+acwb5
+faribu5
+noelby5
+ntxpbw5
+eddac5
+oscarc5
+scre525
+anneca5
+jocelc5
+barbca5
+janinc5
+jasonc5
+michca5
+edc5
+pedroc5
+suzc5
+wandac5
+davc5
+carenc5
+joanc5
+leeca5
+markca5
+pcamp5
+julioc5
+mikec5
+lucisc5
+georgc5
+tomasb5
+antonc5
+stevec5
+jimc5
+dencar5
+tomc5
+ntxpc5
+annsc5
+jcarne5
+nanc5
+patcar5
+loric5
+patca5
+davic5
+kimca5
+acmc5
+wilmac5
+maurc5
+kcart5
+gregco5
+ginnyc5
+iainc5
+philc5
+riccar5
+steca5
+ntxsc5
+louisc5
+gianca5
+annec5
+jjcav5
+kencav5
+ntxgc5
+jeffca5
+palmac5
+vsmc5
+forms5
+ntxfc5
+cchad5
+cchali5
+troyc5
+cecilc5
+carmc5
+jasch5
+sophic5
+abbotc5
+inac5
+lchang5
+nicolc5
+vchang5
+clintc5
+billch5
+kench5
+lojc5
+ntxac5
+johnch5
+sorlet5
+kimc5
+weiyc5
+rrycc5
+ritac5
+pierrc5
+tomch5
+henryc5
+willch5
+rrkc5
+ntxsch5
+daich5
+yukoc5
+abm5
+acgc5
+ntxjch5
+rrtcp5
+jojoc5
+krisch5
+ronch5
+javch5
+ntxrac5
+sebasc5
+andrwc5
+gkkc5
+yvonnc5
+kingsc5
+bibich5
+ntxkch5
+philci5
+ccipol5
+paulc5
+tomci5
+waltci5
+andyc5
+bencl5
+dclark5
+ntxjcl5
+johcl5
+kclark5
+vspc5
+rrsc5
+derekc5
+johncl5
+ntxdc5
+ibmrc5
+shimaa5
+silkea5
+davida5
+chrial5
+eallen5
+susana5
+joseal5
+stefam5
+paulam5
+mettea5
+stinev5
+henrik5
+ralpha5
+robann5
+csilla5
+erica5
+marcoa5
+malia5
+ericas5
+alexat5
+maubry5
+anab5
+alexib5
+nicolb5
+urib5
+shantb5
+mikeba5
+andbar5
+beckyb5
+scottb5
+leonib5
+mariab5
+reemb5
+maddib5
+marieb5
+romab5
+ericba5
+brianb5
+josefb5
+chadb5
+ronb5
+almub5
+tinab5
+monikb5
+joelb5
+yannb5
+tarunb5
+stephb5
+veronb5
+stefab5
+bernb5
+dougbl5
+boccom5
+laurib5
+johanb5
+erinb5
+sinbo5
+michab5
+markbo5
+julb5
+kamilb5
+laurb5
+colinb5
+taniab5
+pbrad5
+jonbr5
+edb5
+annieb5
+jmbrie5
+lisab5
+joakib5
+kbrost5
+aaronb5
+paulbr5
+seambr5
+elainb5
+kaib5
+alainb5
+courtb5
+richbu5
+liambu5
+ferbu5
+hanneb5
+felicb5
+seanb5
+maytec5
+nelsc5
+armanc5
+nicoc5
+camcar5
+juanjc5
+tiziac5
+marcoc5
+tonyc5
+jpcast5
+damiac5
+ignac5
+sabinc5
+gordc5
+jaruc5
+cbchae5
+ammonc5
+jaksch5
+stephc5
+jimc5
+normac5
+fredc5
+richch5
+cheric5
+ericc5
+dmchoi5
+gabc5
+plchua5
+josefc5
+crisci5
+mikecl5
+coracl5
+danco5
+antonc5
+maxinc5
+karenc5
+cecelw5
+alberc5
+claudc5
+ajc5
+valerc5
+maxicu5
+brenc5
+wolfz5
+maxic5
+peteda5
+jacqd5
+robd5
+bobk5
+hiltom5
+katjad5
+marnod5
+serged5
+antond5
+michde5
+freddv5
+hansd5
+tdiver5
+ylvad5
+emerd5
+chelod5
+julied5
+jdrage5
+markdr5
+chridu5
+yvand5
+delphd5
+leed5
+michd5
+betind5
+stephv5
+georgd5
+owenea5
+stepea5
+owene5
+kime5
+rone5
+helene5
+annete5
+marcee5
+gabreh5
+nathfa5
+helenf5
+masudf5
+jeruf5
+brunof5
+marif5
+debbif5
+jonf5
+joachf5
+cathf5
+pennyf5
+kimbef5
+andref5
+billf5
+damfin5
+solfo5
+jacquf5
+pacof5
+antonf5
+tomfr5
+bjornf5
+mickeg5
+werng5
+mariag5
+julieg5
+georgg5
+roxang5
+beng5
+mardcg5
+rafg5
+jimeg5
+maiog5
+laurga5
+giagat5
+mattga5
+laurg5
+bgenar5
+fredge5
+lidiag5
+stefag5
+michag5
+minah5
+mgietl5
+robg5
+juligm5
+stephg5
+dannyg5
+davidg5
+xavg5
+sophig5
+danieg5
+cgold5
+merisg5
+osnatg5
+josem5
+diman5
+fredgo5
+erendg5
+luisgo5
+rosag5
+grantg5
+andygo5
+sgort5
+jeanpg5
+geneg5
+scottg5
+chiarg5
+richgr5
+bruceg5
+adamg5
+avishg5
+sandg5
+sophg5
+andreg5
+ugrob5
+sissyg5
+guenth5
+jhaas5
+erezh5
+mariha5
+tommyh5
+ahaigh5
+svenh5
+jackih5
+johnha5
+perh5
+katreh5
+gerhar5
+charlh5
+sanjh5
+robhau5
+tomokh5
+erich5
+brenhe5
+andreh5
+markuh5
+raphh5
+bernh5
+brendh5
+olafh5
+klaush5
+enidh5
+ayolh5
+jorgeh5
+bobhe5
+adrih5
+loreth5
+juanjh5
+stefah5
+gerdh5
+kathah5
+maxh5
+micheh5
+juergh5
+scotth5
+torhol5
+toddh5
+szabh5
+martah5
+shood5
+jillh5
+shannh5
+clarah5
+liborh5
+jhosty5
+chanth5
+amyhs5
+helenh5
+chhsu5
+franh5
+janyh5
+tracyh5
+dusth5
+davhug5
+bradh5
+moyah5
+jormah5
+mustai5
+kevini5
+leei5
+junkoi5
+jamest5
+karenj5
+joej5
+davidj5
+haylj5
+rosej5
+gertj5
+lanaj5
+hollyj5
+jcjan5
+isek5
+alexka5
+giselk5
+mihok5
+annemk5
+peteke5
+lizak5
+hyeyk5
+hskim5
+bking5
+timk5
+cko5
+fannyk5
+davek5
+rolank5
+craigk5
+martik5
+kotay5
+fredk5
+carolk5
+georgk5
+chrisk
+geirk5
+juliak5
+andrk5
+mank5
+kerstk5
+tckuo5
+tomokk5
+skwan5
+tony5
+markul5
+johnla5
+drewl5
+darlan5
+kenl5
+oritl5
+benjla5
+klau5
+markl5
+mlaur5
+delph5
+pyledu5
+evinl5
+dlee5
+hjlee5
+hklee5
+ivyl5
+jonsul5
+kwlee5
+sjlee5
+wglee5
+ivole5
+peterl5
+steple5
+nikil5
+serenl5
+glinah5
+lsliau5
+hannal5
+rafael5
+angell5
+roblim5
+merjal5
+billli5
+avihal5
+birdlo5
+sergel5
+alberl5
+helenl5
+carlol5
+josel5
+juanl5
+annel5
+lilyl5
+gerryl5
+vicenl5
+nathl5
+edlf5
+editl5
+derekl5
+saml5
+alphal5
+kirbyl5
+luisl5
+dickl5
+torull5
+cristm5
+mmaerk5
+leonm5
+sergm5
+brendm5
+andrwm5
+tinym5
+magdem5
+franma5
+kaym5
+nichm5
+caswem5
+arnam5
+rickm5
+carlam5
+helenm5
+farish5
+majda5
+sarya5
+monaa5
+ahmada5
+tawfad5
+dach5
+krissa5
+krisa5
+paula5
+aleena5
+debraa5
+bja5
+radams5
+stepha5
+josepa5
+colbya5
+allana5
+shola5
+dawna5
+wahaba5
+conraa5
+paulal5
+ralexa5
+ricka5
+willal5
+beckya5
+darrea5
+bkena5
+saraha5
+trical5
+callis5
+bga5
+anasal5
+emada5
+marya5
+kamila5
+marca5
+amyan5
+ericaa5
+janeta5
+tinaa5
+garant5
+franka5
+joea5
+davapp5
+denisa5
+stevar5
+briana5
+mikea5
+randya5
+rafar5
+noela5
+michas5
+davea5
+kellas5
+petash5
+alexa5
+paulat5
+teresa5
+daveat5
+heidia5
+mikeau5
+amya5
+cloa5
+akilaz5
+aprib5
+tricib5
+bruceb5
+chrisb5
+ryanba5
+tomb5
+tohoub5
+pbain5
+bwb5
+gayleb5
+branb5
+sharib5
+johnb5
+dbanks5
+rayb5
+cbarb5
+waldb5
+larrba5
+jeremb5
+mimid5
+garyba5
+bpb5
+hughb5
+kenba5
+markba5
+alib5
+koyb5
+patb5
+dbaur5
+jerib5
+alexb5
+brubax5
+gregbe5
+laurib5
+annb5
+aarob5
+johnbe5
+karbec5
+davb5
+dbeers5
+bmbe5
+teresb5
+paulbe5
+aaronb5
+griogb5
+ericb5
+tracbe5
+janeb5
+bradb5
+leslib5
+tinab5
+dougbe5
+jonib5
+joelbe5
+sharb5
+dougb5
+andreb5
+kberg5
+ryanb5
+cynthb5
+dberry5
+cherbe5
+bmarcb5
+tamib5
+gileb5
+patbe5
+mikebi5
+bryab5
+kathb5
+jamebi5
+kenb5
+billbi5
+jennib5
+danbl5
+jasonb5
+philib5
+alisbl5
+amybl5
+camib5
+alexbl5
+patbl5
+carlab5
+aprilb5
+marcbl5
+brandb5
+kristb5
+jbolic5
+garybo5
+ellenb5
+eribo5
+courtb5
+coletb5
+larryb5
+marcb5
diff --git a/private/net/svcdlls/lls/test/ct/n6.dat b/private/net/svcdlls/lls/test/ct/n6.dat
new file mode 100644
index 000000000..15af57ae9
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n6.dat
@@ -0,0 +1,1000 @@
+house6
+ronaar6
+rrja6
+ntxfa6
+ninaa6
+cabram6
+johnac6
+martad6
+acca6
+rrea6
+markad6
+acma6
+byrona6
+yosha6
+julioa6
+ccaa6
+bertag6
+ibmma6
+acmah6
+noriya6
+hakana6
+lisac6
+johnal6
+melisa6
+boba6
+rayall6
+deba6
+jeffal6
+rallen6
+laural6
+lallyn6
+venesa6
+annaal6
+nancya6
+dinoa6
+bain6
+marioa6
+raquea6
+vitora6
+scotam6
+renam6
+eglea6
+audrea6
+chrisa6
+ntxha6
+curta6
+davand6
+davean6
+ntxga6
+gavana6
+acja6
+jod6
+attma6
+naomia6
+royand6
+sallya6
+toman6
+matsan6
+jamiea6
+rickan6
+jana6
+jeffan6
+jiman6
+bevaok6
+takaya6
+yumia6
+carloa6
+takaar6
+shinar6
+yukaa6
+tonyar6
+hiroar6
+suza6
+kenar6
+eadarm6
+loria6
+ntxra6
+richar6
+carnst6
+arnulw6
+mayuma6
+hidas6
+richat6
+idoa6
+ntxbat6
+markat6
+kevana6
+gillea6
+micha6
+joset6
+ntxba6
+gma6
+saorib6
+vikkib6
+garyba6
+suzieb6
+michba6
+danbag6
+frankb6
+larryb6
+sbaird6
+rrkeb6
+jolynb6
+paulb6
+sbaker6
+valb6
+leeb6
+geoba6
+dougba6
+ctpsb6
+hughb6
+galb6
+leonib6
+patba6
+ntxjb6
+rrjba6
+donbar6
+jacbar6
+roberb6
+larrb6
+allpak6
+mosesb6
+donnab6
+cibelb6
+vspb6
+peteba6
+abates6
+marcb6
+pbcnz6
+jbattl6
+jimbau6
+cbaum6
+danbau6
+bilbax6
+kathbe6
+ntxab6
+celinb6
+karenb6
+philb6
+darryb6
+ctpdb6
+scre96
+ccsb6
+stepbe6
+ccjben6
+leneb6
+ntxnb6
+pbenoi6
+vscb6
+micheb6
+rrjimb6
+chadb6
+karib6
+nathb6
+dougbe6
+kimmob6
+amyb6
+decdb6
+ebenb6
+acabe6
+andb6
+ntxsbe6
+michbe6
+geralb6
+juditb6
+savlob6
+dsiwm6
+willbi6
+ellenb6
+joseb6
+blasb6
+thomb6
+annicb6
+marbin6
+ntxmb6
+daveb6
+dirkb6
+suebi6
+stefb6
+patbi6
+hilmib6
+heleb6
+rrcb6
+cblack6
+richbl6
+brynb6
+alanb6
+hyattb6
+donnbl6
+cblasi6
+sbliss6
+janebl6
+fransb6
+inface6
+lucibo6
+pboehm6
+joyb6
+pboffo6
+johnbo6
+airex6
+bobb6
+kierab6
+laurbo6
+danbol6
+cathab6
+kathbo6
+cathb6
+coreyb6
+garyb6
+dadcmb6
+nicolb6
+ismb6
+davidb6
+tomb6
+ntxsb6
+timbo6
+ronbo6
+benjab6
+toddb6
+cheryb6
+vskb6
+shellb6
+timboy6
+bboyle6
+jboyle6
+kaijab6
+myriab6
+mbrand6
+brianb6
+darreb6
+simonb6
+geneb6
+tracyb6
+lisbd6
+ibmdb6
+pbrenn6
+sineab6
+susb6
+bryanb6
+ebrew6
+donbr6
+noelb6
+elizbr6
+galeb6
+davb6
+billbr6
+bribro6
+sophib6
+henryb6
+philbr6
+donb6
+amybr6
+cbrown6
+dianab6
+acjb6
+kirstb6
+lesb6
+rbrown6
+ntxrbr6
+albru6
+sbrug6
+ludob6
+vssb6
+paulbr6
+ccrb6
+michbu6
+larrbu6
+lisab6
+carolb6
+peteb6
+actb6
+klausb6
+telen6
+sbuhr6
+louisb6
+miltb6
+heathb6
+helmb6
+bburke6
+catbu6
+billb6
+dburn6
+kaeb6
+ronb6
+eileeb6
+brettb6
+laurab6
+ccfb6
+gunteb6
+gregb6
+robbu6
+robut6
+acwb6
+faribu6
+noelby6
+ntxpbw6
+eddac6
+oscarc6
+scre626
+anneca6
+jocelc6
+barbca6
+janinc6
+jasonc6
+michca6
+edc6
+pedroc6
+suzc6
+wandac6
+davc6
+carenc6
+joanc6
+leeca6
+markca6
+pcamp6
+julioc6
+mikec6
+lucisc6
+georgc6
+tomasb6
+antonc6
+stevec6
+jimc6
+dencar6
+tomc6
+ntxpc6
+annsc6
+jcarne6
+nanc6
+patcar6
+loric6
+patca6
+davic6
+kimca6
+acmc6
+wilmac6
+maurc6
+kcart6
+gregco6
+ginnyc6
+iainc6
+philc6
+riccar6
+steca6
+ntxsc6
+louisc6
+gianca6
+annec6
+jjcav6
+kencav6
+ntxgc6
+jeffca6
+palmac6
+vsmc6
+forms6
+ntxfc6
+cchad6
+cchali6
+troyc6
+cecilc6
+carmc6
+jasch6
+sophic6
+abbotc6
+inac6
+lchang6
+nicolc6
+vchang6
+clintc6
+billch6
+kench6
+lojc6
+ntxac6
+johnch6
+sorlet6
+kimc6
+weiyc6
+rrycc6
+ritac6
+pierrc6
+tomch6
+henryc6
+willch6
+rrkc6
+ntxsch6
+daich6
+yukoc6
+abm6
+acgc6
+ntxjch6
+rrtcp6
+jojoc6
+krisch6
+ronch6
+javch6
+ntxrac6
+sebasc6
+andrwc6
+gkkc6
+yvonnc6
+kingsc6
+bibich6
+ntxkch6
+philci6
+ccipol6
+paulc6
+tomci6
+waltci6
+andyc6
+bencl6
+dclark6
+ntxjcl6
+johcl6
+kclark6
+vspc6
+rrsc6
+derekc6
+johncl6
+ntxdc6
+ibmrc6
+shimaa6
+silkea6
+davida6
+chrial6
+eallen6
+susana6
+joseal6
+stefam6
+paulam6
+mettea6
+stinev6
+henrik6
+ralpha6
+robann6
+csilla6
+erica6
+marcoa6
+malia6
+ericas6
+alexat6
+maubry6
+anab6
+alexib6
+nicolb6
+urib6
+shantb6
+mikeba6
+andbar6
+beckyb6
+scottb6
+leonib6
+mariab6
+reemb6
+maddib6
+marieb6
+romab6
+ericba6
+brianb6
+josefb6
+chadb6
+ronb6
+almub6
+tinab6
+monikb6
+joelb6
+yannb6
+tarunb6
+stephb6
+veronb6
+stefab6
+bernb6
+dougbl6
+boccom6
+laurib6
+johanb6
+erinb6
+sinbo6
+michab6
+markbo6
+julb6
+kamilb6
+laurb6
+colinb6
+taniab6
+pbrad6
+jonbr6
+edb6
+annieb6
+jmbrie6
+lisab6
+joakib6
+kbrost6
+aaronb6
+paulbr6
+seambr6
+elainb6
+kaib6
+alainb6
+courtb6
+richbu6
+liambu6
+ferbu6
+hanneb6
+felicb6
+seanb6
+maytec6
+nelsc6
+armanc6
+nicoc6
+camcar6
+juanjc6
+tiziac6
+marcoc6
+tonyc6
+jpcast6
+damiac6
+ignac6
+sabinc6
+gordc6
+jaruc6
+cbchae6
+ammonc6
+jaksch6
+stephc6
+jimc6
+normac6
+fredc6
+richch6
+cheric6
+ericc6
+dmchoi6
+gabc6
+plchua6
+josefc6
+crisci6
+mikecl6
+coracl6
+danco6
+antonc6
+maxinc6
+karenc6
+cecelw6
+alberc6
+claudc6
+ajc6
+valerc6
+maxicu6
+brenc6
+wolfz6
+maxic6
+peteda6
+jacqd6
+robd6
+bobk6
+hiltom6
+katjad6
+marnod6
+serged6
+antond6
+michde6
+freddv6
+hansd6
+tdiver6
+ylvad6
+emerd6
+chelod6
+julied6
+jdrage6
+markdr6
+chridu6
+yvand6
+delphd6
+leed6
+michd6
+betind6
+stephv6
+georgd6
+owenea6
+stepea6
+owene6
+kime6
+rone6
+helene6
+annete6
+marcee6
+gabreh6
+nathfa6
+helenf6
+masudf6
+jeruf6
+brunof6
+marif6
+debbif6
+jonf6
+joachf6
+cathf6
+pennyf6
+kimbef6
+andref6
+billf6
+damfin6
+solfo6
+jacquf6
+pacof6
+antonf6
+tomfr6
+bjornf6
+mickeg6
+werng6
+mariag6
+julieg6
+georgg6
+roxang6
+beng6
+mardcg6
+rafg6
+jimeg6
+maiog6
+laurga6
+giagat6
+mattga6
+laurg6
+bgenar6
+fredge6
+lidiag6
+stefag6
+michag6
+minah6
+mgietl6
+robg6
+juligm6
+stephg6
+dannyg6
+davidg6
+xavg6
+sophig6
+danieg6
+cgold6
+merisg6
+osnatg6
+josem6
+diman6
+fredgo6
+erendg6
+luisgo6
+rosag6
+grantg6
+andygo6
+sgort6
+jeanpg6
+geneg6
+scottg6
+chiarg6
+richgr6
+bruceg6
+adamg6
+avishg6
+sandg6
+sophg6
+andreg6
+ugrob6
+sissyg6
+guenth6
+jhaas6
+erezh6
+mariha6
+tommyh6
+ahaigh6
+svenh6
+jackih6
+johnha6
+perh6
+katreh6
+gerhar6
+charlh6
+sanjh6
+robhau6
+tomokh6
+erich6
+brenhe6
+andreh6
+markuh6
+raphh6
+bernh6
+brendh6
+olafh6
+klaush6
+enidh6
+ayolh6
+jorgeh6
+bobhe6
+adrih6
+loreth6
+juanjh6
+stefah6
+gerdh6
+kathah6
+maxh6
+micheh6
+juergh6
+scotth6
+torhol6
+toddh6
+szabh6
+martah6
+shood6
+jillh6
+shannh6
+clarah6
+liborh6
+jhosty6
+chanth6
+amyhs6
+helenh6
+chhsu6
+franh6
+janyh6
+tracyh6
+dusth6
+davhug6
+bradh6
+moyah6
+jormah6
+mustai6
+kevini6
+leei6
+junkoi6
+jamest6
+karenj6
+joej6
+davidj6
+haylj6
+rosej6
+gertj6
+lanaj6
+hollyj6
+jcjan6
+isek6
+alexka6
+giselk6
+mihok6
+annemk6
+peteke6
+lizak6
+hyeyk6
+hskim6
+bking6
+timk6
+cko6
+fannyk6
+davek6
+rolank6
+craigk6
+martik6
+kotay6
+fredk6
+carolk6
+georgk6
+chrisk
+geirk6
+juliak6
+andrk6
+mank6
+kerstk6
+tckuo6
+tomokk6
+skwan6
+tony6
+markul6
+johnla6
+drewl6
+darlan6
+kenl6
+oritl6
+benjla6
+klau6
+markl6
+mlaur6
+delph6
+pyledu6
+evinl6
+dlee6
+hjlee6
+hklee6
+ivyl6
+jonsul6
+kwlee6
+sjlee6
+wglee6
+ivole6
+peterl6
+steple6
+nikil6
+serenl6
+glinah6
+lsliau6
+hannal6
+rafael6
+angell6
+roblim6
+merjal6
+billli6
+avihal6
+birdlo6
+sergel6
+alberl6
+helenl6
+carlol6
+josel6
+juanl6
+annel6
+lilyl6
+gerryl6
+vicenl6
+nathl6
+edlf6
+editl6
+derekl6
+saml6
+alphal6
+kirbyl6
+luisl6
+dickl6
+torull6
+cristm6
+mmaerk6
+leonm6
+sergm6
+brendm6
+andrwm6
+tinym6
+magdem6
+franma6
+kaym6
+nichm6
+caswem6
+arnam6
+rickm6
+carlam6
+helenm6
+farish6
+majda6
+sarya6
+monaa6
+ahmada6
+tawfad6
+dach6
+krissa6
+krisa6
+paula6
+aleena6
+debraa6
+bja6
+radams6
+stepha6
+josepa6
+colbya6
+allana6
+shola6
+dawna6
+wahaba6
+conraa6
+paulal6
+ralexa6
+ricka6
+willal6
+beckya6
+darrea6
+bkena6
+saraha6
+trical6
+callis6
+bga6
+anasal6
+emada6
+marya6
+kamila6
+marca6
+amyan6
+ericaa6
+janeta6
+tinaa6
+garant6
+franka6
+joea6
+davapp6
+denisa6
+stevar6
+briana6
+mikea6
+randya6
+rafar6
+noela6
+michas6
+davea6
+kellas6
+petash6
+alexa6
+paulat6
+teresa6
+daveat6
+heidia6
+mikeau6
+amya6
+cloa6
+akilaz6
+aprib6
+tricib6
+bruceb6
+chrisb6
+ryanba6
+tomb6
+tohoub6
+pbain6
+bwb6
+gayleb6
+branb6
+sharib6
+johnb6
+dbanks6
+rayb6
+cbarb6
+waldb6
+larrba6
+jeremb6
+mimid6
+garyba6
+bpb6
+hughb6
+kenba6
+markba6
+alib6
+koyb6
+patb6
+dbaur6
+jerib6
+alexb6
+brubax6
+gregbe6
+laurib6
+annb6
+aarob6
+johnbe6
+karbec6
+davb6
+dbeers6
+bmbe6
+teresb6
+paulbe6
+aaronb6
+griogb6
+ericb6
+tracbe6
+janeb6
+bradb6
+leslib6
+tinab6
+dougbe6
+jonib6
+joelbe6
+sharb6
+dougb6
+andreb6
+kberg6
+ryanb6
+cynthb6
+dberry6
+cherbe6
+bmarcb6
+tamib6
+gileb6
+patbe6
+mikebi6
+bryab6
+kathb6
+jamebi6
+kenb6
+billbi6
+jennib6
+danbl6
+jasonb6
+philib6
+alisbl6
+amybl6
+camib6
+alexbl6
+patbl6
+carlab6
+aprilb6
+marcbl6
+brandb6
+kristb6
+jbolic6
+garybo6
+ellenb6
+eribo6
+courtb6
+coletb6
+larryb6
+marcb6
diff --git a/private/net/svcdlls/lls/test/ct/n7.dat b/private/net/svcdlls/lls/test/ct/n7.dat
new file mode 100644
index 000000000..b6a9900c6
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n7.dat
@@ -0,0 +1,1000 @@
+house7
+ronaar7
+rrja7
+ntxfa7
+ninaa7
+cabram7
+johnac7
+martad7
+acca7
+rrea7
+markad7
+acma7
+byrona7
+yosha7
+julioa7
+ccaa7
+bertag7
+ibmma7
+acmah7
+noriya7
+hakana7
+lisac7
+johnal7
+melisa7
+boba7
+rayall7
+deba7
+jeffal7
+rallen7
+laural7
+lallyn7
+venesa7
+annaal7
+nancya7
+dinoa7
+bain7
+marioa7
+raquea7
+vitora7
+scotam7
+renam7
+eglea7
+audrea7
+chrisa7
+ntxha7
+curta7
+davand7
+davean7
+ntxga7
+gavana7
+acja7
+jod7
+attma7
+naomia7
+royand7
+sallya7
+toman7
+matsan7
+jamiea7
+rickan7
+jana7
+jeffan7
+jiman7
+bevaok7
+takaya7
+yumia7
+carloa7
+takaar7
+shinar7
+yukaa7
+tonyar7
+hiroar7
+suza7
+kenar7
+eadarm7
+loria7
+ntxra7
+richar7
+carnst7
+arnulw7
+mayuma7
+hidas7
+richat7
+idoa7
+ntxbat7
+markat7
+kevana7
+gillea7
+micha7
+joset7
+ntxba7
+gma7
+saorib7
+vikkib7
+garyba7
+suzieb7
+michba7
+danbag7
+frankb7
+larryb7
+sbaird7
+rrkeb7
+jolynb7
+paulb7
+sbaker7
+valb7
+leeb7
+geoba7
+dougba7
+ctpsb7
+hughb7
+galb7
+leonib7
+patba7
+ntxjb7
+rrjba7
+donbar7
+jacbar7
+roberb7
+larrb7
+allpak7
+mosesb7
+donnab7
+cibelb7
+vspb7
+peteba7
+abates7
+marcb7
+pbcnz7
+jbattl7
+jimbau7
+cbaum7
+danbau7
+bilbax7
+kathbe7
+ntxab7
+celinb7
+karenb7
+philb7
+darryb7
+ctpdb7
+scre97
+ccsb7
+stepbe7
+ccjben7
+leneb7
+ntxnb7
+pbenoi7
+vscb7
+micheb7
+rrjimb7
+chadb7
+karib7
+nathb7
+dougbe7
+kimmob7
+amyb7
+decdb7
+ebenb7
+acabe7
+andb7
+ntxsbe7
+michbe7
+geralb7
+juditb7
+savlob7
+dsiwm7
+willbi7
+ellenb7
+joseb7
+blasb7
+thomb7
+annicb7
+marbin7
+ntxmb7
+daveb7
+dirkb7
+suebi7
+stefb7
+patbi7
+hilmib7
+heleb7
+rrcb7
+cblack7
+richbl7
+brynb7
+alanb7
+hyattb7
+donnbl7
+cblasi7
+sbliss7
+janebl7
+fransb7
+inface7
+lucibo7
+pboehm7
+joyb7
+pboffo7
+johnbo7
+airex7
+bobb7
+kierab7
+laurbo7
+danbol7
+cathab7
+kathbo7
+cathb7
+coreyb7
+garyb7
+dadcmb7
+nicolb7
+ismb7
+davidb7
+tomb7
+ntxsb7
+timbo7
+ronbo7
+benjab7
+toddb7
+cheryb7
+vskb7
+shellb7
+timboy7
+bboyle7
+jboyle7
+kaijab7
+myriab7
+mbrand7
+brianb7
+darreb7
+simonb7
+geneb7
+tracyb7
+lisbd7
+ibmdb7
+pbrenn7
+sineab7
+susb7
+bryanb7
+ebrew7
+donbr7
+noelb7
+elizbr7
+galeb7
+davb7
+billbr7
+bribro7
+sophib7
+henryb7
+philbr7
+donb7
+amybr7
+cbrown7
+dianab7
+acjb7
+kirstb7
+lesb7
+rbrown7
+ntxrbr7
+albru7
+sbrug7
+ludob7
+vssb7
+paulbr7
+ccrb7
+michbu7
+larrbu7
+lisab7
+carolb7
+peteb7
+actb7
+klausb7
+telen7
+sbuhr7
+louisb7
+miltb7
+heathb7
+helmb7
+bburke7
+catbu7
+billb7
+dburn7
+kaeb7
+ronb7
+eileeb7
+brettb7
+laurab7
+ccfb7
+gunteb7
+gregb7
+robbu7
+robut7
+acwb7
+faribu7
+noelby7
+ntxpbw7
+eddac7
+oscarc7
+scre727
+anneca7
+jocelc7
+barbca7
+janinc7
+jasonc7
+michca7
+edc7
+pedroc7
+suzc7
+wandac7
+davc7
+carenc7
+joanc7
+leeca7
+markca7
+pcamp7
+julioc7
+mikec7
+lucisc7
+georgc7
+tomasb7
+antonc7
+stevec7
+jimc7
+dencar7
+tomc7
+ntxpc7
+annsc7
+jcarne7
+nanc7
+patcar7
+loric7
+patca7
+davic7
+kimca7
+acmc7
+wilmac7
+maurc7
+kcart7
+gregco7
+ginnyc7
+iainc7
+philc7
+riccar7
+steca7
+ntxsc7
+louisc7
+gianca7
+annec7
+jjcav7
+kencav7
+ntxgc7
+jeffca7
+palmac7
+vsmc7
+forms7
+ntxfc7
+cchad7
+cchali7
+troyc7
+cecilc7
+carmc7
+jasch7
+sophic7
+abbotc7
+inac7
+lchang7
+nicolc7
+vchang7
+clintc7
+billch7
+kench7
+lojc7
+ntxac7
+johnch7
+sorlet7
+kimc7
+weiyc7
+rrycc7
+ritac7
+pierrc7
+tomch7
+henryc7
+willch7
+rrkc7
+ntxsch7
+daich7
+yukoc7
+abm7
+acgc7
+ntxjch7
+rrtcp7
+jojoc7
+krisch7
+ronch7
+javch7
+ntxrac7
+sebasc7
+andrwc7
+gkkc7
+yvonnc7
+kingsc7
+bibich7
+ntxkch7
+philci7
+ccipol7
+paulc7
+tomci7
+waltci7
+andyc7
+bencl7
+dclark7
+ntxjcl7
+johcl7
+kclark7
+vspc7
+rrsc7
+derekc7
+johncl7
+ntxdc7
+ibmrc7
+shimaa7
+silkea7
+davida7
+chrial7
+eallen7
+susana7
+joseal7
+stefam7
+paulam7
+mettea7
+stinev7
+henrik7
+ralpha7
+robann7
+csilla7
+erica7
+marcoa7
+malia7
+ericas7
+alexat7
+maubry7
+anab7
+alexib7
+nicolb7
+urib7
+shantb7
+mikeba7
+andbar7
+beckyb7
+scottb7
+leonib7
+mariab7
+reemb7
+maddib7
+marieb7
+romab7
+ericba7
+brianb7
+josefb7
+chadb7
+ronb7
+almub7
+tinab7
+monikb7
+joelb7
+yannb7
+tarunb7
+stephb7
+veronb7
+stefab7
+bernb7
+dougbl7
+boccom7
+laurib7
+johanb7
+erinb7
+sinbo7
+michab7
+markbo7
+julb7
+kamilb7
+laurb7
+colinb7
+taniab7
+pbrad7
+jonbr7
+edb7
+annieb7
+jmbrie7
+lisab7
+joakib7
+kbrost7
+aaronb7
+paulbr7
+seambr7
+elainb7
+kaib7
+alainb7
+courtb7
+richbu7
+liambu7
+ferbu7
+hanneb7
+felicb7
+seanb7
+maytec7
+nelsc7
+armanc7
+nicoc7
+camcar7
+juanjc7
+tiziac7
+marcoc7
+tonyc7
+jpcast7
+damiac7
+ignac7
+sabinc7
+gordc7
+jaruc7
+cbchae7
+ammonc7
+jaksch7
+stephc7
+jimc7
+normac7
+fredc7
+richch7
+cheric7
+ericc7
+dmchoi7
+gabc7
+plchua7
+josefc7
+crisci7
+mikecl7
+coracl7
+danco7
+antonc7
+maxinc7
+karenc7
+cecelw7
+alberc7
+claudc7
+ajc7
+valerc7
+maxicu7
+brenc7
+wolfz7
+maxic7
+peteda7
+jacqd7
+robd7
+bobk7
+hiltom7
+katjad7
+marnod7
+serged7
+antond7
+michde7
+freddv7
+hansd7
+tdiver7
+ylvad7
+emerd7
+chelod7
+julied7
+jdrage7
+markdr7
+chridu7
+yvand7
+delphd7
+leed7
+michd7
+betind7
+stephv7
+georgd7
+owenea7
+stepea7
+owene7
+kime7
+rone7
+helene7
+annete7
+marcee7
+gabreh7
+nathfa7
+helenf7
+masudf7
+jeruf7
+brunof7
+marif7
+debbif7
+jonf7
+joachf7
+cathf7
+pennyf7
+kimbef7
+andref7
+billf7
+damfin7
+solfo7
+jacquf7
+pacof7
+antonf7
+tomfr7
+bjornf7
+mickeg7
+werng7
+mariag7
+julieg7
+georgg7
+roxang7
+beng7
+mardcg7
+rafg7
+jimeg7
+maiog7
+laurga7
+giagat7
+mattga7
+laurg7
+bgenar7
+fredge7
+lidiag7
+stefag7
+michag7
+minah7
+mgietl7
+robg7
+juligm7
+stephg7
+dannyg7
+davidg7
+xavg7
+sophig7
+danieg7
+cgold7
+merisg7
+osnatg7
+josem7
+diman7
+fredgo7
+erendg7
+luisgo7
+rosag7
+grantg7
+andygo7
+sgort7
+jeanpg7
+geneg7
+scottg7
+chiarg7
+richgr7
+bruceg7
+adamg7
+avishg7
+sandg7
+sophg7
+andreg7
+ugrob7
+sissyg7
+guenth7
+jhaas7
+erezh7
+mariha7
+tommyh7
+ahaigh7
+svenh7
+jackih7
+johnha7
+perh7
+katreh7
+gerhar7
+charlh7
+sanjh7
+robhau7
+tomokh7
+erich7
+brenhe7
+andreh7
+markuh7
+raphh7
+bernh7
+brendh7
+olafh7
+klaush7
+enidh7
+ayolh7
+jorgeh7
+bobhe7
+adrih7
+loreth7
+juanjh7
+stefah7
+gerdh7
+kathah7
+maxh7
+micheh7
+juergh7
+scotth7
+torhol7
+toddh7
+szabh7
+martah7
+shood7
+jillh7
+shannh7
+clarah7
+liborh7
+jhosty7
+chanth7
+amyhs7
+helenh7
+chhsu7
+franh7
+janyh7
+tracyh7
+dusth7
+davhug7
+bradh7
+moyah7
+jormah7
+mustai7
+kevini7
+leei7
+junkoi7
+jamest7
+karenj7
+joej7
+davidj7
+haylj7
+rosej7
+gertj7
+lanaj7
+hollyj7
+jcjan7
+isek7
+alexka7
+giselk7
+mihok7
+annemk7
+peteke7
+lizak7
+hyeyk7
+hskim7
+bking7
+timk7
+cko7
+fannyk7
+davek7
+rolank7
+craigk7
+martik7
+kotay7
+fredk7
+carolk7
+georgk7
+chrisk
+geirk7
+juliak7
+andrk7
+mank7
+kerstk7
+tckuo7
+tomokk7
+skwan7
+tony7
+markul7
+johnla7
+drewl7
+darlan7
+kenl7
+oritl7
+benjla7
+klau7
+markl7
+mlaur7
+delph7
+pyledu7
+evinl7
+dlee7
+hjlee7
+hklee7
+ivyl7
+jonsul7
+kwlee7
+sjlee7
+wglee7
+ivole7
+peterl7
+steple7
+nikil7
+serenl7
+glinah7
+lsliau7
+hannal7
+rafael7
+angell7
+roblim7
+merjal7
+billli7
+avihal7
+birdlo7
+sergel7
+alberl7
+helenl7
+carlol7
+josel7
+juanl7
+annel7
+lilyl7
+gerryl7
+vicenl7
+nathl7
+edlf7
+editl7
+derekl7
+saml7
+alphal7
+kirbyl7
+luisl7
+dickl7
+torull7
+cristm7
+mmaerk7
+leonm7
+sergm7
+brendm7
+andrwm7
+tinym7
+magdem7
+franma7
+kaym7
+nichm7
+caswem7
+arnam7
+rickm7
+carlam7
+helenm7
+farish7
+majda7
+sarya7
+monaa7
+ahmada7
+tawfad7
+dach7
+krissa7
+krisa7
+paula7
+aleena7
+debraa7
+bja7
+radams7
+stepha7
+josepa7
+colbya7
+allana7
+shola7
+dawna7
+wahaba7
+conraa7
+paulal7
+ralexa7
+ricka7
+willal7
+beckya7
+darrea7
+bkena7
+saraha7
+trical7
+callis7
+bga7
+anasal7
+emada7
+marya7
+kamila7
+marca7
+amyan7
+ericaa7
+janeta7
+tinaa7
+garant7
+franka7
+joea7
+davapp7
+denisa7
+stevar7
+briana7
+mikea7
+randya7
+rafar7
+noela7
+michas7
+davea7
+kellas7
+petash7
+alexa7
+paulat7
+teresa7
+daveat7
+heidia7
+mikeau7
+amya7
+cloa7
+akilaz7
+aprib7
+tricib7
+bruceb7
+chrisb7
+ryanba7
+tomb7
+tohoub7
+pbain7
+bwb7
+gayleb7
+branb7
+sharib7
+johnb7
+dbanks7
+rayb7
+cbarb7
+waldb7
+larrba7
+jeremb7
+mimid7
+garyba7
+bpb7
+hughb7
+kenba7
+markba7
+alib7
+koyb7
+patb7
+dbaur7
+jerib7
+alexb7
+brubax7
+gregbe7
+laurib7
+annb7
+aarob7
+johnbe7
+karbec7
+davb7
+dbeers7
+bmbe7
+teresb7
+paulbe7
+aaronb7
+griogb7
+ericb7
+tracbe7
+janeb7
+bradb7
+leslib7
+tinab7
+dougbe7
+jonib7
+joelbe7
+sharb7
+dougb7
+andreb7
+kberg7
+ryanb7
+cynthb7
+dberry7
+cherbe7
+bmarcb7
+tamib7
+gileb7
+patbe7
+mikebi7
+bryab7
+kathb7
+jamebi7
+kenb7
+billbi7
+jennib7
+danbl7
+jasonb7
+philib7
+alisbl7
+amybl7
+camib7
+alexbl7
+patbl7
+carlab7
+aprilb7
+marcbl7
+brandb7
+kristb7
+jbolic7
+garybo7
+ellenb7
+eribo7
+courtb7
+coletb7
+larryb7
+marcb7
diff --git a/private/net/svcdlls/lls/test/ct/n8.dat b/private/net/svcdlls/lls/test/ct/n8.dat
new file mode 100644
index 000000000..97a4aa494
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n8.dat
@@ -0,0 +1,1000 @@
+house8
+ronaar8
+rrja8
+ntxfa8
+ninaa8
+cabram8
+johnac8
+martad8
+acca8
+rrea8
+markad8
+acma8
+byrona8
+yosha8
+julioa8
+ccaa8
+bertag8
+ibmma8
+acmah8
+noriya8
+hakana8
+lisac8
+johnal8
+melisa8
+boba8
+rayall8
+deba8
+jeffal8
+rallen8
+laural8
+lallyn8
+venesa8
+annaal8
+nancya8
+dinoa8
+bain8
+marioa8
+raquea8
+vitora8
+scotam8
+renam8
+eglea8
+audrea8
+chrisa8
+ntxha8
+curta8
+davand8
+davean8
+ntxga8
+gavana8
+acja8
+jod8
+attma8
+naomia8
+royand8
+sallya8
+toman8
+matsan8
+jamiea8
+rickan8
+jana8
+jeffan8
+jiman8
+bevaok8
+takaya8
+yumia8
+carloa8
+takaar8
+shinar8
+yukaa8
+tonyar8
+hiroar8
+suza8
+kenar8
+eadarm8
+loria8
+ntxra8
+richar8
+carnst8
+arnulw8
+mayuma8
+hidas8
+richat8
+idoa8
+ntxbat8
+markat8
+kevana8
+gillea8
+micha8
+joset8
+ntxba8
+gma8
+saorib8
+vikkib8
+garyba8
+suzieb8
+michba8
+danbag8
+frankb8
+larryb8
+sbaird8
+rrkeb8
+jolynb8
+paulb8
+sbaker8
+valb8
+leeb8
+geoba8
+dougba8
+ctpsb8
+hughb8
+galb8
+leonib8
+patba8
+ntxjb8
+rrjba8
+donbar8
+jacbar8
+roberb8
+larrb8
+allpak8
+mosesb8
+donnab8
+cibelb8
+vspb8
+peteba8
+abates8
+marcb8
+pbcnz8
+jbattl8
+jimbau8
+cbaum8
+danbau8
+bilbax8
+kathbe8
+ntxab8
+celinb8
+karenb8
+philb8
+darryb8
+ctpdb8
+scre98
+ccsb8
+stepbe8
+ccjben8
+leneb8
+ntxnb8
+pbenoi8
+vscb8
+micheb8
+rrjimb8
+chadb8
+karib8
+nathb8
+dougbe8
+kimmob8
+amyb8
+decdb8
+ebenb8
+acabe8
+andb8
+ntxsbe8
+michbe8
+geralb8
+juditb8
+savlob8
+dsiwm8
+willbi8
+ellenb8
+joseb8
+blasb8
+thomb8
+annicb8
+marbin8
+ntxmb8
+daveb8
+dirkb8
+suebi8
+stefb8
+patbi8
+hilmib8
+heleb8
+rrcb8
+cblack8
+richbl8
+brynb8
+alanb8
+hyattb8
+donnbl8
+cblasi8
+sbliss8
+janebl8
+fransb8
+inface8
+lucibo8
+pboehm8
+joyb8
+pboffo8
+johnbo8
+airex8
+bobb8
+kierab8
+laurbo8
+danbol8
+cathab8
+kathbo8
+cathb8
+coreyb8
+garyb8
+dadcmb8
+nicolb8
+ismb8
+davidb8
+tomb8
+ntxsb8
+timbo8
+ronbo8
+benjab8
+toddb8
+cheryb8
+vskb8
+shellb8
+timboy8
+bboyle8
+jboyle8
+kaijab8
+myriab8
+mbrand8
+brianb8
+darreb8
+simonb8
+geneb8
+tracyb8
+lisbd8
+ibmdb8
+pbrenn8
+sineab8
+susb8
+bryanb8
+ebrew8
+donbr8
+noelb8
+elizbr8
+galeb8
+davb8
+billbr8
+bribro8
+sophib8
+henryb8
+philbr8
+donb8
+amybr8
+cbrown8
+dianab8
+acjb8
+kirstb8
+lesb8
+rbrown8
+ntxrbr8
+albru8
+sbrug8
+ludob8
+vssb8
+paulbr8
+ccrb8
+michbu8
+larrbu8
+lisab8
+carolb8
+peteb8
+actb8
+klausb8
+telen8
+sbuhr8
+louisb8
+miltb8
+heathb8
+helmb8
+bburke8
+catbu8
+billb8
+dburn8
+kaeb8
+ronb8
+eileeb8
+brettb8
+laurab8
+ccfb8
+gunteb8
+gregb8
+robbu8
+robut8
+acwb8
+faribu8
+noelby8
+ntxpbw8
+eddac8
+oscarc8
+scre828
+anneca8
+jocelc8
+barbca8
+janinc8
+jasonc8
+michca8
+edc8
+pedroc8
+suzc8
+wandac8
+davc8
+carenc8
+joanc8
+leeca8
+markca8
+pcamp8
+julioc8
+mikec8
+lucisc8
+georgc8
+tomasb8
+antonc8
+stevec8
+jimc8
+dencar8
+tomc8
+ntxpc8
+annsc8
+jcarne8
+nanc8
+patcar8
+loric8
+patca8
+davic8
+kimca8
+acmc8
+wilmac8
+maurc8
+kcart8
+gregco8
+ginnyc8
+iainc8
+philc8
+riccar8
+steca8
+ntxsc8
+louisc8
+gianca8
+annec8
+jjcav8
+kencav8
+ntxgc8
+jeffca8
+palmac8
+vsmc8
+forms8
+ntxfc8
+cchad8
+cchali8
+troyc8
+cecilc8
+carmc8
+jasch8
+sophic8
+abbotc8
+inac8
+lchang8
+nicolc8
+vchang8
+clintc8
+billch8
+kench8
+lojc8
+ntxac8
+johnch8
+sorlet8
+kimc8
+weiyc8
+rrycc8
+ritac8
+pierrc8
+tomch8
+henryc8
+willch8
+rrkc8
+ntxsch8
+daich8
+yukoc8
+abm8
+acgc8
+ntxjch8
+rrtcp8
+jojoc8
+krisch8
+ronch8
+javch8
+ntxrac8
+sebasc8
+andrwc8
+gkkc8
+yvonnc8
+kingsc8
+bibich8
+ntxkch8
+philci8
+ccipol8
+paulc8
+tomci8
+waltci8
+andyc8
+bencl8
+dclark8
+ntxjcl8
+johcl8
+kclark8
+vspc8
+rrsc8
+derekc8
+johncl8
+ntxdc8
+ibmrc8
+shimaa8
+silkea8
+davida8
+chrial8
+eallen8
+susana8
+joseal8
+stefam8
+paulam8
+mettea8
+stinev8
+henrik8
+ralpha8
+robann8
+csilla8
+erica8
+marcoa8
+malia8
+ericas8
+alexat8
+maubry8
+anab8
+alexib8
+nicolb8
+urib8
+shantb8
+mikeba8
+andbar8
+beckyb8
+scottb8
+leonib8
+mariab8
+reemb8
+maddib8
+marieb8
+romab8
+ericba8
+brianb8
+josefb8
+chadb8
+ronb8
+almub8
+tinab8
+monikb8
+joelb8
+yannb8
+tarunb8
+stephb8
+veronb8
+stefab8
+bernb8
+dougbl8
+boccom8
+laurib8
+johanb8
+erinb8
+sinbo8
+michab8
+markbo8
+julb8
+kamilb8
+laurb8
+colinb8
+taniab8
+pbrad8
+jonbr8
+edb8
+annieb8
+jmbrie8
+lisab8
+joakib8
+kbrost8
+aaronb8
+paulbr8
+seambr8
+elainb8
+kaib8
+alainb8
+courtb8
+richbu8
+liambu8
+ferbu8
+hanneb8
+felicb8
+seanb8
+maytec8
+nelsc8
+armanc8
+nicoc8
+camcar8
+juanjc8
+tiziac8
+marcoc8
+tonyc8
+jpcast8
+damiac8
+ignac8
+sabinc8
+gordc8
+jaruc8
+cbchae8
+ammonc8
+jaksch8
+stephc8
+jimc8
+normac8
+fredc8
+richch8
+cheric8
+ericc8
+dmchoi8
+gabc8
+plchua8
+josefc8
+crisci8
+mikecl8
+coracl8
+danco8
+antonc8
+maxinc8
+karenc8
+cecelw8
+alberc8
+claudc8
+ajc8
+valerc8
+maxicu8
+brenc8
+wolfz8
+maxic8
+peteda8
+jacqd8
+robd8
+bobk8
+hiltom8
+katjad8
+marnod8
+serged8
+antond8
+michde8
+freddv8
+hansd8
+tdiver8
+ylvad8
+emerd8
+chelod8
+julied8
+jdrage8
+markdr8
+chridu8
+yvand8
+delphd8
+leed8
+michd8
+betind8
+stephv8
+georgd8
+owenea8
+stepea8
+owene8
+kime8
+rone8
+helene8
+annete8
+marcee8
+gabreh8
+nathfa8
+helenf8
+masudf8
+jeruf8
+brunof8
+marif8
+debbif8
+jonf8
+joachf8
+cathf8
+pennyf8
+kimbef8
+andref8
+billf8
+damfin8
+solfo8
+jacquf8
+pacof8
+antonf8
+tomfr8
+bjornf8
+mickeg8
+werng8
+mariag8
+julieg8
+georgg8
+roxang8
+beng8
+mardcg8
+rafg8
+jimeg8
+maiog8
+laurga8
+giagat8
+mattga8
+laurg8
+bgenar8
+fredge8
+lidiag8
+stefag8
+michag8
+minah8
+mgietl8
+robg8
+juligm8
+stephg8
+dannyg8
+davidg8
+xavg8
+sophig8
+danieg8
+cgold8
+merisg8
+osnatg8
+josem8
+diman8
+fredgo8
+erendg8
+luisgo8
+rosag8
+grantg8
+andygo8
+sgort8
+jeanpg8
+geneg8
+scottg8
+chiarg8
+richgr8
+bruceg8
+adamg8
+avishg8
+sandg8
+sophg8
+andreg8
+ugrob8
+sissyg8
+guenth8
+jhaas8
+erezh8
+mariha8
+tommyh8
+ahaigh8
+svenh8
+jackih8
+johnha8
+perh8
+katreh8
+gerhar8
+charlh8
+sanjh8
+robhau8
+tomokh8
+erich8
+brenhe8
+andreh8
+markuh8
+raphh8
+bernh8
+brendh8
+olafh8
+klaush8
+enidh8
+ayolh8
+jorgeh8
+bobhe8
+adrih8
+loreth8
+juanjh8
+stefah8
+gerdh8
+kathah8
+maxh8
+micheh8
+juergh8
+scotth8
+torhol8
+toddh8
+szabh8
+martah8
+shood8
+jillh8
+shannh8
+clarah8
+liborh8
+jhosty8
+chanth8
+amyhs8
+helenh8
+chhsu8
+franh8
+janyh8
+tracyh8
+dusth8
+davhug8
+bradh8
+moyah8
+jormah8
+mustai8
+kevini8
+leei8
+junkoi8
+jamest8
+karenj8
+joej8
+davidj8
+haylj8
+rosej8
+gertj8
+lanaj8
+hollyj8
+jcjan8
+isek8
+alexka8
+giselk8
+mihok8
+annemk8
+peteke8
+lizak8
+hyeyk8
+hskim8
+bking8
+timk8
+cko8
+fannyk8
+davek8
+rolank8
+craigk8
+martik8
+kotay8
+fredk8
+carolk8
+georgk8
+chrisk
+geirk8
+juliak8
+andrk8
+mank8
+kerstk8
+tckuo8
+tomokk8
+skwan8
+tony8
+markul8
+johnla8
+drewl8
+darlan8
+kenl8
+oritl8
+benjla8
+klau8
+markl8
+mlaur8
+delph8
+pyledu8
+evinl8
+dlee8
+hjlee8
+hklee8
+ivyl8
+jonsul8
+kwlee8
+sjlee8
+wglee8
+ivole8
+peterl8
+steple8
+nikil8
+serenl8
+glinah8
+lsliau8
+hannal8
+rafael8
+angell8
+roblim8
+merjal8
+billli8
+avihal8
+birdlo8
+sergel8
+alberl8
+helenl8
+carlol8
+josel8
+juanl8
+annel8
+lilyl8
+gerryl8
+vicenl8
+nathl8
+edlf8
+editl8
+derekl8
+saml8
+alphal8
+kirbyl8
+luisl8
+dickl8
+torull8
+cristm8
+mmaerk8
+leonm8
+sergm8
+brendm8
+andrwm8
+tinym8
+magdem8
+franma8
+kaym8
+nichm8
+caswem8
+arnam8
+rickm8
+carlam8
+helenm8
+farish8
+majda8
+sarya8
+monaa8
+ahmada8
+tawfad8
+dach8
+krissa8
+krisa8
+paula8
+aleena8
+debraa8
+bja8
+radams8
+stepha8
+josepa8
+colbya8
+allana8
+shola8
+dawna8
+wahaba8
+conraa8
+paulal8
+ralexa8
+ricka8
+willal8
+beckya8
+darrea8
+bkena8
+saraha8
+trical8
+callis8
+bga8
+anasal8
+emada8
+marya8
+kamila8
+marca8
+amyan8
+ericaa8
+janeta8
+tinaa8
+garant8
+franka8
+joea8
+davapp8
+denisa8
+stevar8
+briana8
+mikea8
+randya8
+rafar8
+noela8
+michas8
+davea8
+kellas8
+petash8
+alexa8
+paulat8
+teresa8
+daveat8
+heidia8
+mikeau8
+amya8
+cloa8
+akilaz8
+aprib8
+tricib8
+bruceb8
+chrisb8
+ryanba8
+tomb8
+tohoub8
+pbain8
+bwb8
+gayleb8
+branb8
+sharib8
+johnb8
+dbanks8
+rayb8
+cbarb8
+waldb8
+larrba8
+jeremb8
+mimid8
+garyba8
+bpb8
+hughb8
+kenba8
+markba8
+alib8
+koyb8
+patb8
+dbaur8
+jerib8
+alexb8
+brubax8
+gregbe8
+laurib8
+annb8
+aarob8
+johnbe8
+karbec8
+davb8
+dbeers8
+bmbe8
+teresb8
+paulbe8
+aaronb8
+griogb8
+ericb8
+tracbe8
+janeb8
+bradb8
+leslib8
+tinab8
+dougbe8
+jonib8
+joelbe8
+sharb8
+dougb8
+andreb8
+kberg8
+ryanb8
+cynthb8
+dberry8
+cherbe8
+bmarcb8
+tamib8
+gileb8
+patbe8
+mikebi8
+bryab8
+kathb8
+jamebi8
+kenb8
+billbi8
+jennib8
+danbl8
+jasonb8
+philib8
+alisbl8
+amybl8
+camib8
+alexbl8
+patbl8
+carlab8
+aprilb8
+marcbl8
+brandb8
+kristb8
+jbolic8
+garybo8
+ellenb8
+eribo8
+courtb8
+coletb8
+larryb8
+marcb8
diff --git a/private/net/svcdlls/lls/test/ct/n9.dat b/private/net/svcdlls/lls/test/ct/n9.dat
new file mode 100644
index 000000000..11f601fd5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/n9.dat
@@ -0,0 +1,1000 @@
+house9
+ronaar9
+rrja9
+ntxfa9
+ninaa9
+cabram9
+johnac9
+martad9
+acca9
+rrea9
+markad9
+acma9
+byrona9
+yosha9
+julioa9
+ccaa9
+bertag9
+ibmma9
+acmah9
+noriya9
+hakana9
+lisac9
+johnal9
+melisa9
+boba9
+rayall9
+deba9
+jeffal9
+rallen9
+laural9
+lallyn9
+venesa9
+annaal9
+nancya9
+dinoa9
+bain9
+marioa9
+raquea9
+vitora9
+scotam9
+renam9
+eglea9
+audrea9
+chrisa9
+ntxha9
+curta9
+davand9
+davean9
+ntxga9
+gavana9
+acja9
+jod9
+attma9
+naomia9
+royand9
+sallya9
+toman9
+matsan9
+jamiea9
+rickan9
+jana9
+jeffan9
+jiman9
+bevaok9
+takaya9
+yumia9
+carloa9
+takaar9
+shinar9
+yukaa9
+tonyar9
+hiroar9
+suza9
+kenar9
+eadarm9
+loria9
+ntxra9
+richar9
+carnst9
+arnulw9
+mayuma9
+hidas9
+richat9
+idoa9
+ntxbat9
+markat9
+kevana9
+gillea9
+micha9
+joset9
+ntxba9
+gma9
+saorib9
+vikkib9
+garyba9
+suzieb9
+michba9
+danbag9
+frankb9
+larryb9
+sbaird9
+rrkeb9
+jolynb9
+paulb9
+sbaker9
+valb9
+leeb9
+geoba9
+dougba9
+ctpsb9
+hughb9
+galb9
+leonib9
+patba9
+ntxjb9
+rrjba9
+donbar9
+jacbar9
+roberb9
+larrb9
+allpak9
+mosesb9
+donnab9
+cibelb9
+vspb9
+peteba9
+abates9
+marcb9
+pbcnz9
+jbattl9
+jimbau9
+cbaum9
+danbau9
+bilbax9
+kathbe9
+ntxab9
+celinb9
+karenb9
+philb9
+darryb9
+ctpdb9
+scre99
+ccsb9
+stepbe9
+ccjben9
+leneb9
+ntxnb9
+pbenoi9
+vscb9
+micheb9
+rrjimb9
+chadb9
+karib9
+nathb9
+dougbe9
+kimmob9
+amyb9
+decdb9
+ebenb9
+acabe9
+andb9
+ntxsbe9
+michbe9
+geralb9
+juditb9
+savlob9
+dsiwm9
+willbi9
+ellenb9
+joseb9
+blasb9
+thomb9
+annicb9
+marbin9
+ntxmb9
+daveb9
+dirkb9
+suebi9
+stefb9
+patbi9
+hilmib9
+heleb9
+rrcb9
+cblack9
+richbl9
+brynb9
+alanb9
+hyattb9
+donnbl9
+cblasi9
+sbliss9
+janebl9
+fransb9
+inface9
+lucibo9
+pboehm9
+joyb9
+pboffo9
+johnbo9
+airex9
+bobb9
+kierab9
+laurbo9
+danbol9
+cathab9
+kathbo9
+cathb9
+coreyb9
+garyb9
+dadcmb9
+nicolb9
+ismb9
+davidb9
+tomb9
+ntxsb9
+timbo9
+ronbo9
+benjab9
+toddb9
+cheryb9
+vskb9
+shellb9
+timboy9
+bboyle9
+jboyle9
+kaijab9
+myriab9
+mbrand9
+brianb9
+darreb9
+simonb9
+geneb9
+tracyb9
+lisbd9
+ibmdb9
+pbrenn9
+sineab9
+susb9
+bryanb9
+ebrew9
+donbr9
+noelb9
+elizbr9
+galeb9
+davb9
+billbr9
+bribro9
+sophib9
+henryb9
+philbr9
+donb9
+amybr9
+cbrown9
+dianab9
+acjb9
+kirstb9
+lesb9
+rbrown9
+ntxrbr9
+albru9
+sbrug9
+ludob9
+vssb9
+paulbr9
+ccrb9
+michbu9
+larrbu9
+lisab9
+carolb9
+peteb9
+actb9
+klausb9
+telen9
+sbuhr9
+louisb9
+miltb9
+heathb9
+helmb9
+bburke9
+catbu9
+billb9
+dburn9
+kaeb9
+ronb9
+eileeb9
+brettb9
+laurab9
+ccfb9
+gunteb9
+gregb9
+robbu9
+robut9
+acwb9
+faribu9
+noelby9
+ntxpbw9
+eddac9
+oscarc9
+scre929
+anneca9
+jocelc9
+barbca9
+janinc9
+jasonc9
+michca9
+edc9
+pedroc9
+suzc9
+wandac9
+davc9
+carenc9
+joanc9
+leeca9
+markca9
+pcamp9
+julioc9
+mikec9
+lucisc9
+georgc9
+tomasb9
+antonc9
+stevec9
+jimc9
+dencar9
+tomc9
+ntxpc9
+annsc9
+jcarne9
+nanc9
+patcar9
+loric9
+patca9
+davic9
+kimca9
+acmc9
+wilmac9
+maurc9
+kcart9
+gregco9
+ginnyc9
+iainc9
+philc9
+riccar9
+steca9
+ntxsc9
+louisc9
+gianca9
+annec9
+jjcav9
+kencav9
+ntxgc9
+jeffca9
+palmac9
+vsmc9
+forms9
+ntxfc9
+cchad9
+cchali9
+troyc9
+cecilc9
+carmc9
+jasch9
+sophic9
+abbotc9
+inac9
+lchang9
+nicolc9
+vchang9
+clintc9
+billch9
+kench9
+lojc9
+ntxac9
+johnch9
+sorlet9
+kimc9
+weiyc9
+rrycc9
+ritac9
+pierrc9
+tomch9
+henryc9
+willch9
+rrkc9
+ntxsch9
+daich9
+yukoc9
+abm9
+acgc9
+ntxjch9
+rrtcp9
+jojoc9
+krisch9
+ronch9
+javch9
+ntxrac9
+sebasc9
+andrwc9
+gkkc9
+yvonnc9
+kingsc9
+bibich9
+ntxkch9
+philci9
+ccipol9
+paulc9
+tomci9
+waltci9
+andyc9
+bencl9
+dclark9
+ntxjcl9
+johcl9
+kclark9
+vspc9
+rrsc9
+derekc9
+johncl9
+ntxdc9
+ibmrc9
+shimaa9
+silkea9
+davida9
+chrial9
+eallen9
+susana9
+joseal9
+stefam9
+paulam9
+mettea9
+stinev9
+henrik9
+ralpha9
+robann9
+csilla9
+erica9
+marcoa9
+malia9
+ericas9
+alexat9
+maubry9
+anab9
+alexib9
+nicolb9
+urib9
+shantb9
+mikeba9
+andbar9
+beckyb9
+scottb9
+leonib9
+mariab9
+reemb9
+maddib9
+marieb9
+romab9
+ericba9
+brianb9
+josefb9
+chadb9
+ronb9
+almub9
+tinab9
+monikb9
+joelb9
+yannb9
+tarunb9
+stephb9
+veronb9
+stefab9
+bernb9
+dougbl9
+boccom9
+laurib9
+johanb9
+erinb9
+sinbo9
+michab9
+markbo9
+julb9
+kamilb9
+laurb9
+colinb9
+taniab9
+pbrad9
+jonbr9
+edb9
+annieb9
+jmbrie9
+lisab9
+joakib9
+kbrost9
+aaronb9
+paulbr9
+seambr9
+elainb9
+kaib9
+alainb9
+courtb9
+richbu9
+liambu9
+ferbu9
+hanneb9
+felicb9
+seanb9
+maytec9
+nelsc9
+armanc9
+nicoc9
+camcar9
+juanjc9
+tiziac9
+marcoc9
+tonyc9
+jpcast9
+damiac9
+ignac9
+sabinc9
+gordc9
+jaruc9
+cbchae9
+ammonc9
+jaksch9
+stephc9
+jimc9
+normac9
+fredc9
+richch9
+cheric9
+ericc9
+dmchoi9
+gabc9
+plchua9
+josefc9
+crisci9
+mikecl9
+coracl9
+danco9
+antonc9
+maxinc9
+karenc9
+cecelw9
+alberc9
+claudc9
+ajc9
+valerc9
+maxicu9
+brenc9
+wolfz9
+maxic9
+peteda9
+jacqd9
+robd9
+bobk9
+hiltom9
+katjad9
+marnod9
+serged9
+antond9
+michde9
+freddv9
+hansd9
+tdiver9
+ylvad9
+emerd9
+chelod9
+julied9
+jdrage9
+markdr9
+chridu9
+yvand9
+delphd9
+leed9
+michd9
+betind9
+stephv9
+georgd9
+owenea9
+stepea9
+owene9
+kime9
+rone9
+helene9
+annete9
+marcee9
+gabreh9
+nathfa9
+helenf9
+masudf9
+jeruf9
+brunof9
+marif9
+debbif9
+jonf9
+joachf9
+cathf9
+pennyf9
+kimbef9
+andref9
+billf9
+damfin9
+solfo9
+jacquf9
+pacof9
+antonf9
+tomfr9
+bjornf9
+mickeg9
+werng9
+mariag9
+julieg9
+georgg9
+roxang9
+beng9
+mardcg9
+rafg9
+jimeg9
+maiog9
+laurga9
+giagat9
+mattga9
+laurg9
+bgenar9
+fredge9
+lidiag9
+stefag9
+michag9
+minah9
+mgietl9
+robg9
+juligm9
+stephg9
+dannyg9
+davidg9
+xavg9
+sophig9
+danieg9
+cgold9
+merisg9
+osnatg9
+josem9
+diman9
+fredgo9
+erendg9
+luisgo9
+rosag9
+grantg9
+andygo9
+sgort9
+jeanpg9
+geneg9
+scottg9
+chiarg9
+richgr9
+bruceg9
+adamg9
+avishg9
+sandg9
+sophg9
+andreg9
+ugrob9
+sissyg9
+guenth9
+jhaas9
+erezh9
+mariha9
+tommyh9
+ahaigh9
+svenh9
+jackih9
+johnha9
+perh9
+katreh9
+gerhar9
+charlh9
+sanjh9
+robhau9
+tomokh9
+erich9
+brenhe9
+andreh9
+markuh9
+raphh9
+bernh9
+brendh9
+olafh9
+klaush9
+enidh9
+ayolh9
+jorgeh9
+bobhe9
+adrih9
+loreth9
+juanjh9
+stefah9
+gerdh9
+kathah9
+maxh9
+micheh9
+juergh9
+scotth9
+torhol9
+toddh9
+szabh9
+martah9
+shood9
+jillh9
+shannh9
+clarah9
+liborh9
+jhosty9
+chanth9
+amyhs9
+helenh9
+chhsu9
+franh9
+janyh9
+tracyh9
+dusth9
+davhug9
+bradh9
+moyah9
+jormah9
+mustai9
+kevini9
+leei9
+junkoi9
+jamest9
+karenj9
+joej9
+davidj9
+haylj9
+rosej9
+gertj9
+lanaj9
+hollyj9
+jcjan9
+isek9
+alexka9
+giselk9
+mihok9
+annemk9
+peteke9
+lizak9
+hyeyk9
+hskim9
+bking9
+timk9
+cko9
+fannyk9
+davek9
+rolank9
+craigk9
+martik9
+kotay9
+fredk9
+carolk9
+georgk9
+chrisk
+geirk9
+juliak9
+andrk9
+mank9
+kerstk9
+tckuo9
+tomokk9
+skwan9
+tony9
+markul9
+johnla9
+drewl9
+darlan9
+kenl9
+oritl9
+benjla9
+klau9
+markl9
+mlaur9
+delph9
+pyledu9
+evinl9
+dlee9
+hjlee9
+hklee9
+ivyl9
+jonsul9
+kwlee9
+sjlee9
+wglee9
+ivole9
+peterl9
+steple9
+nikil9
+serenl9
+glinah9
+lsliau9
+hannal9
+rafael9
+angell9
+roblim9
+merjal9
+billli9
+avihal9
+birdlo9
+sergel9
+alberl9
+helenl9
+carlol9
+josel9
+juanl9
+annel9
+lilyl9
+gerryl9
+vicenl9
+nathl9
+edlf9
+editl9
+derekl9
+saml9
+alphal9
+kirbyl9
+luisl9
+dickl9
+torull9
+cristm9
+mmaerk9
+leonm9
+sergm9
+brendm9
+andrwm9
+tinym9
+magdem9
+franma9
+kaym9
+nichm9
+caswem9
+arnam9
+rickm9
+carlam9
+helenm9
+farish9
+majda9
+sarya9
+monaa9
+ahmada9
+tawfad9
+dach9
+krissa9
+krisa9
+paula9
+aleena9
+debraa9
+bja9
+radams9
+stepha9
+josepa9
+colbya9
+allana9
+shola9
+dawna9
+wahaba9
+conraa9
+paulal9
+ralexa9
+ricka9
+willal9
+beckya9
+darrea9
+bkena9
+saraha9
+trical9
+callis9
+bga9
+anasal9
+emada9
+marya9
+kamila9
+marca9
+amyan9
+ericaa9
+janeta9
+tinaa9
+garant9
+franka9
+joea9
+davapp9
+denisa9
+stevar9
+briana9
+mikea9
+randya9
+rafar9
+noela9
+michas9
+davea9
+kellas9
+petash9
+alexa9
+paulat9
+teresa9
+daveat9
+heidia9
+mikeau9
+amya9
+cloa9
+akilaz9
+aprib9
+tricib9
+bruceb9
+chrisb9
+ryanba9
+tomb9
+tohoub9
+pbain9
+bwb9
+gayleb9
+branb9
+sharib9
+johnb9
+dbanks9
+rayb9
+cbarb9
+waldb9
+larrba9
+jeremb9
+mimid9
+garyba9
+bpb9
+hughb9
+kenba9
+markba9
+alib9
+koyb9
+patb9
+dbaur9
+jerib9
+alexb9
+brubax9
+gregbe9
+laurib9
+annb9
+aarob9
+johnbe9
+karbec9
+davb9
+dbeers9
+bmbe9
+teresb9
+paulbe9
+aaronb9
+griogb9
+ericb9
+tracbe9
+janeb9
+bradb9
+leslib9
+tinab9
+dougbe9
+jonib9
+joelbe9
+sharb9
+dougb9
+andreb9
+kberg9
+ryanb9
+cynthb9
+dberry9
+cherbe9
+bmarcb9
+tamib9
+gileb9
+patbe9
+mikebi9
+bryab9
+kathb9
+jamebi9
+kenb9
+billbi9
+jennib9
+danbl9
+jasonb9
+philib9
+alisbl9
+amybl9
+camib9
+alexbl9
+patbl9
+carlab9
+aprilb9
+marcbl9
+brandb9
+kristb9
+jbolic9
+garybo9
+ellenb9
+eribo9
+courtb9
+coletb9
+larryb9
+marcb9
diff --git a/private/net/svcdlls/lls/test/ct/p1.bat b/private/net/svcdlls/lls/test/ct/p1.bat
new file mode 100644
index 000000000..cd59f24c0
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p1.bat
@@ -0,0 +1,198 @@
+llscmd add user 4user FORT510A.DOS 1.0
+llscmd add user 4user FORTRN32.NT 1.0
+llscmd add user 4user VC21.NT 1.0
+llscmd add user 4user cdex 1.0
+llscmd add user 4user CLIENTS.NET 1.0
+llscmd add user 4user CMSRVWS.11 1.0
+llscmd add user 1user wlo09 1.0
+llscmd add user 2user wlo10 1.0
+llscmd add user 2user ACCSOLPK.WIN 1.0
+llscmd add user 2user ACCUPTLS.WIN 1.0
+llscmd add user 2user ADT20.WIN 1.0
+llscmd add user 1user Money 1.0
+llscmd add user 2user FOXPR26A.WIN 1.0
+llscmd add user 2user FOXPRO26.UNX 1.0
+llscmd add user 2user WKGRP311.DOS 1.0
+llscmd add user 4user DRAW10.WIN 1.0
+llscmd add user 4user COBOL500 1.0
+llscmd add user 4user FORT510A.DOS 1.0
+llscmd add user 4user FORTRN32.NT 1.0
+llscmd add user 4user VC21.NT 1.0
+llscmd add user 4user cdex 1.0
+llscmd add user 4user CLIENTS.NET 1.0
+llscmd add user 4user CMSRVWS.11 1.0
+llscmd add user 1user Mail 1.0
+llscmd add user 1user MONEY30.WIN 1.0
+llscmd add user 1user MP420A.DOS 1.0
+llscmd add user 1user MSMAIL.ALL 1.0
+llscmd add user 1user Win32 1.0
+llscmd add user 1user Win32s 1.0
+llscmd add user 1user Excel 1.0
+llscmd add user 1user Word 1.0
+llscmd add user 1user ARCADE10.WIN 1.0
+llscmd add user 1user ARTGALL.WIN 1.0
+llscmd add user 1user VCP100.WIN 1.0
+llscmd add user 1user WINLIB 1.0
+llscmd add user 1user ACCESS20.WIN 1.0
+llscmd add user 1user FORTRN32.WIN 1.0
+llscmd add user 1user IMSL.NT 1.0
+llscmd add user 1user IMSL32.DOS 1.0
+llscmd add user 1user IMSL51.DOS 1.0
+llscmd add user 1user Chicago 1.0
+llscmd add user 1user OFFDK10.WIN 1.0
+llscmd add user 5user TCPIP.ALL 1.0
+llscmd add user 5user TCPUTIL1.0 1.0
+llscmd add user 5user VFW11.WIN 1.0
+llscmd add user 5user WRESKIT2.00 1.0
+llscmd add user 5user WSS20.WIN 1.0
+llscmd add user 5user MacWord 1.0
+llscmd add user 5user PowerPoint 1.0
+llscmd add user 1user FONTPACK.WIN 1.0
+llscmd add user 1user FONTPAK2.WIN 1.0
+llscmd add user 1user Windows95 1.0
+llscmd add user 1user Access 1.0
+llscmd add user 1user MASM611 1.0
+llscmd add user 1user MFCKIT 1.0
+llscmd add user 1user mt300a.all 1.0
+llscmd add user 1user QWGRAPH 1.0
+llscmd add user 1user VB10.DOS 1.0
+llscmd add user 1user CBT 1.0
+llscmd add user 1user EISPAK11.WIN 1.0
+llscmd add user 1user ENCART95.WIN 1.0
+llscmd add user 1user EXCEL30.PM 1.0
+llscmd add user 1user VB30.WIN 1.0
+llscmd add user 1user MASM 1.0
+llscmd add user 2user OLE 1.0
+llscmd add user 2user Chart 1.0
+llscmd add user 2user Encarta 1.0
+llscmd add user 2user FlightSim 1.0
+llscmd add user 2user Paradox 1.0
+llscmd add user 2user VC150.WIN 1.0
+llscmd add user 2user VC20.MAC 1.0
+llscmd add user 2user IMAGER10.WIN 1.0
+llscmd add user 2user LRND300.DOS 1.0
+llscmd add user 2user MMGOLF10.WIN 1.0
+llscmd add user 2user MOM.NT 1.0
+llscmd add user 2user MSDOS622.DOS 1.0
+llscmd add user 2user MSKBD1.ALL 1.0
+llscmd add user 2user MSLOGO.TTF 1.0
+llscmd add user 2user MSSMS10 1.0
+llscmd add user 2user MOM42.WIN 1.0
+llscmd add user 2user SNA 1.0
+llscmd add user 1user Exchange 1.0
+llscmd add user 2user MS_EFORM.WIN 1.0
+llscmd add user 1user ODAKIT 1.0
+llscmd add user 2user ODBC210.WIN 1.0
+llscmd add user 1user VC20.NT 1.0
+llscmd add user 2user BOB.WIN 1.0
+llscmd add user 1user BOOK94.WIN 1.0
+llscmd add user 2user BOWEP.WIN 1.0
+llscmd add user 2user EXCEL50.NT 1.0
+llscmd add user 2user EXCEL50C.WIN 1.0
+llscmd add user 2user FLTSIM5a.DOS 1.0
+llscmd add user 2user FOXPR26A.DOS 1.0
+llscmd add user 2user GS100.DOS 1.0
+llscmd add user 2user ODBCDDP2.NT 1.0
+llscmd add user 2user ODBCDDP2.WIN 1.0
+llscmd add user 2user OFF42C.WIN 1.0
+llscmd add user 2user OFF43c.WIN 1.0
+llscmd add user 2user OFFAST10.WIN 1.0
+llscmd add user 1user HPFNTSET.WIN 1.0
+llscmd add user 1user hyper.100 1.0
+llscmd add user 2user IMSL.NT 1.0
+llscmd add user 3user IMSL32.DOS 1.0
+llscmd add user 3user LM22 1.0
+llscmd add user 3user LMRES10.OS2 1.0
+llscmd add user 3user LMSFMAC1.0A 1.0
+llscmd add user 3user OFFICE.NT 1.0
+llscmd add user 3user OLE202.WIN 1.0
+llscmd add user 3user ONLN200.WIN 1.0
+llscmd add user 3user PROJ40.WIN 1.0
+llscmd add user 3user MSTCPIP.ALL 1.0
+llscmd add user 3user MVWR200.WIN 1.0
+llscmd add user 3user NETMON 1.0
+llscmd add user 3user OS2-1.31 1.0
+llscmd add user 3user PMSUB35.NT 1.0
+llscmd add user 3user RAS11.LM 1.0
+llscmd add user 3user PUB20a.WIN 1.0
+llscmd add user 3user PUBDSIGN.WIN 1.0
+llscmd add user 3user SAMPLER 1.0
+llscmd add user 3user SCENES20.WIN 1.0
+llscmd add user 3user SCHED10A.WIN 1.0
+llscmd add user 3user SGML10.WIN 1.0
+llscmd add user 3user SLM.ALL 1.0
+llscmd add user 3user SNDBIT10.WIN 1.0
+llscmd add user 3user SPACE10.DOS 1.0
+llscmd add user 3user SS30.DOS 1.0
+llscmd add user 3user ss31std.win 1.0
+llscmd add user 3user WA100.WIN 1.0
+llscmd add user 5user WMDLRSDK.WIN 1.0
+llscmd add user 5user GOLF20.WIN 1.0
+llscmd add user 5user GRAPH50.WIN 1.0
+llscmd add user 5user GREETING.WIN 1.0
+llscmd add user 5user VC20A.NT 1.0
+llscmd add user 3user WEP10B.WIN 1.0
+llscmd add user 3user WEP20.WIN 1.0
+llscmd add user 3user WEP30.WIN 1.0
+llscmd add user 3user WEP40.WIN 1.0
+llscmd add user 3user WGTPLT11.WIN 1.0
+llscmd add user 3user WORD11B.PM 1.0
+llscmd add user 3user WORD6.DOS 1.0
+llscmd add user 3user WORD60.NT 1.0
+llscmd add user 3user WORD60C.WIN 1.0
+llscmd add user 3user word6cnv.nt 1.0
+llscmd add user 3user WORD6CNV.WIN 1.0
+llscmd add user 3user WORDVW60.WIN 1.0
+llscmd add user 3user WORKS30.DOS 1.0
+llscmd add user 4user DOS 1.0
+llscmd add user 4user C 1.0
+llscmd add user 4user CHART300.DOS 1.0
+llscmd add user 4user CINEMA94.WIN 1.0
+llscmd add user 4user DELTA10A.WIN 1.0
+llscmd add user 4user DINOSAUR.WIN 1.0
+llscmd add user 4user DCIDDK10 1.0
+llscmd add user 4user PPT40c.WIN 1.0
+llscmd add user 4user PROFIT1B.WIN 1.0
+llscmd add user 4user PROJ40.DOS 1.0
+llscmd add user 4user LMUNIX 1.0
+llscmd add user 4user MOUSE10a.ALL 1.0
+llscmd add user 4user MSD211.DOS 1.0
+llscmd add user 4user RESKIT.NT 1.0
+llscmd add user 4user SNA21.NT 1.0
+llscmd add user 4user SQL.NT 1.0
+llscmd add user 4user SQL42B.OS2 1.0
+llscmd add user 4user SQLRES10.OS2 1.0
+llscmd add user 4user WFW311.WIN 1.0
+llscmd add user 3user WORKS30B.WIN 1.0
+llscmd add user 3user WPP310.WIN 1.0
+llscmd add user 3user WRITER10.WIN 1.0
+llscmd add user 4user 3com 1.0
+llscmd add user 4user BLPNT901.ALL 1.0
+llscmd add user 4user ARTIST10.WIN 1.0
+llscmd add user 4user BASEBL94.WIN 1.0
+llscmd add user 4user BA331.DOS 1.0
+llscmd add user 4user OS/2 1.0
+llscmd add user 4user NetWare 1.0
+llscmd add user 4user WFWCONN.WIN 1.0
+llscmd add user 4user WIN31 1.0
+llscmd add user 4user WING10.WIN 1.0
+llscmd add user 4user winlogin.win 1.0
+llscmd add user 5user WINNT.NT 1.0
+llscmd add user 5user WINNT35.SRV 1.0
+llscmd add user 5user WINNT35.WKS 1.0
+llscmd add user 5user wpen100a.win 1.0
+llscmd add user 5user WPS.WIN 1.0
+llscmd add user 1user wlo09 1.0
+llscmd add user 2user wlo10 1.0
+llscmd add user 2user ACCSOLPK.WIN 1.0
+llscmd add user 2user ACCUPTLS.WIN 1.0
+llscmd add user 2user ADT20.WIN 1.0
+llscmd add user 1user Money 1.0
+llscmd add user 1user Schedule+ 1.0
+llscmd add user 5user TAPI10.WIN 1.0
+llscmd add user 5user BoundsChecker 1.0
+llscmd add user 5user Quattro 1.0
+llscmd add user 5user WordPerfect 1.0
+llscmd add user 5user Office 1.0
+llscmd add user 5user PerfectOffice 1.0
+llscmd add user 5user SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/p2.bat b/private/net/svcdlls/lls/test/ct/p2.bat
new file mode 100644
index 000000000..83f3fa676
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p2.bat
@@ -0,0 +1,186 @@
+llscmd add user 1user Money 1.3
+llscmd add user 1user Schedule+ 1.3
+llscmd add user 1user Mail 1.3
+llscmd add user 1user MONEY30.WIN 1.3
+llscmd add user 1user MP420A.DOS 1.3
+llscmd add user 1user MSMAIL.ALL 1.3
+llscmd add user 1user Win32 1.3
+llscmd add user 1user Win32s 1.3
+llscmd add user 1user Excel 1.3
+llscmd add user 1user Word 1.3
+llscmd add user 1user ARCADE10.WIN 1.3
+llscmd add user 1user ARTGALL.WIN 1.3
+llscmd add user 1user VCP100.WIN 1.3
+llscmd add user 2user WINLIB 1.3
+llscmd add user 2user ACCESS20.WIN 1.3
+llscmd add user 2user FORTRN32.WIN 1.3
+llscmd add user 2user IMSL.NT 1.3
+llscmd add user 2user IMSL32.DOS 1.3
+llscmd add user 2user IMSL51.DOS 1.3
+llscmd add user 2user Chicago 1.3
+llscmd add user 2user Windows95 1.3
+llscmd add user 2user Access 1.3
+llscmd add user 2user MASM611 1.3
+llscmd add user 2user MFCKIT 1.3
+llscmd add user 2user mt300a.all 1.3
+llscmd add user 2user QWGRAPH 1.3
+llscmd add user 2user VB10.DOS 1.3
+llscmd add user 2user CBT 1.3
+llscmd add user 2user EISPAK11.WIN 1.3
+llscmd add user 2user ENCART95.WIN 1.3
+llscmd add user 2user EXCEL30.PM 1.3
+llscmd add user 2user VB30.WIN 1.3
+llscmd add user 2user MASM 1.3
+llscmd add user 2user OLE 1.3
+llscmd add user 2user Chart 1.3
+llscmd add user 2user Encarta 1.3
+llscmd add user 2user FlightSim 1.3
+llscmd add user 2user Paradox 1.3
+llscmd add user 2user VC150.WIN 1.3
+llscmd add user 2user VC20.MAC 1.3
+llscmd add user 2user IMAGER10.WIN 1.3
+llscmd add user 2user LRND300.DOS 1.3
+llscmd add user 2user MMGOLF10.WIN 1.3
+llscmd add user 3user MOM.NT 1.3
+llscmd add user 3user MSDOS622.DOS 1.3
+llscmd add user 3user MSKBD1.ALL 1.3
+llscmd add user 3user MSLOGO.TTF 1.3
+llscmd add user 3user MSSMS10 1.3
+llscmd add user 3user MOM42.WIN 1.3
+llscmd add user 3user SNA 1.3
+llscmd add user 3user Exchange 1.3
+llscmd add user 3user MS_EFORM.WIN 1.3
+llscmd add user 4user ODAKIT 1.3
+llscmd add user 4user ODBC210.WIN 1.3
+llscmd add user 4user VC20.NT 1.3
+llscmd add user 4user BOB.WIN 1.3
+llscmd add user 4user BOOK94.WIN 1.3
+llscmd add user 4user BOWEP.WIN 1.3
+llscmd add user 4user EXCEL50.NT 1.3
+llscmd add user 4user EXCEL50C.WIN 1.3
+llscmd add user 4user FLTSIM5a.DOS 1.3
+llscmd add user 4user FOXPR26A.DOS 1.3
+llscmd add user 4user FOXPR26A.WIN 1.3
+llscmd add user 4user FOXPRO26.UNX 1.3
+llscmd add user 4user WKGRP311.DOS 1.3
+llscmd add user 5user wlo09 1.3
+llscmd add user 5user wlo10 1.3
+llscmd add user 5user ACCSOLPK.WIN 1.3
+llscmd add user 5user ACCUPTLS.WIN 1.3
+llscmd add user 5user ADT20.WIN 1.3
+llscmd add user 5user GS100.DOS 1.3
+llscmd add user 5user ODBCDDP2.NT 1.3
+llscmd add user 5user ODBCDDP2.WIN 1.3
+llscmd add user 5user OFF42C.WIN 1.3
+llscmd add user 5user OFF43c.WIN 1.3
+llscmd add user 5user OFFAST10.WIN 1.3
+llscmd add user 5user OFFDK10.WIN 1.3
+llscmd add user 5user FONTPACK.WIN 1.3
+llscmd add user 5user FONTPAK2.WIN 1.3
+llscmd add user 5user HPFNTSET.WIN 1.3
+llscmd add user 5user hyper.100 1.3
+llscmd add user 5user IMSL.NT 1.3
+llscmd add user 5user IMSL32.DOS 1.3
+llscmd add user 5user LM22 1.3
+llscmd add user 5user LMRES10.OS2 1.3
+llscmd add user 5user LMSFMAC1.3A 1.3
+llscmd add user 5user OFFICE.NT 1.3
+llscmd add user 5user OLE202.WIN 1.3
+llscmd add user 5user ONLN200.WIN 1.3
+llscmd add user 5user PROJ40.WIN 1.3
+llscmd add user 5user MSTCPIP.ALL 1.3
+llscmd add user 5user MVWR200.WIN 1.3
+llscmd add user 5user NETMON 1.3
+llscmd add user 5user OS2-1.31 1.3
+llscmd add user 5user PMSUB35.NT 1.3
+llscmd add user 5user RAS11.LM 1.3
+llscmd add user 5user PUB20a.WIN 1.3
+llscmd add user 5user PUBDSIGN.WIN 1.3
+llscmd add user 5user SAMPLER 1.3
+llscmd add user 5user SCENES20.WIN 1.3
+llscmd add user 5user SCHED10A.WIN 1.3
+llscmd add user 5user SGML10.WIN 1.3
+llscmd add user 5user SLM.ALL 1.3
+llscmd add user 5user SNDBIT10.WIN 1.3
+llscmd add user 5user SPACE10.DOS 1.3
+llscmd add user 5user SS30.DOS 1.3
+llscmd add user 4user ss31std.win 1.3
+llscmd add user 4user WA100.WIN 1.3
+llscmd add user 4user WEP10B.WIN 1.3
+llscmd add user 1user WEP20.WIN 1.3
+llscmd add user 1user WEP30.WIN 1.3
+llscmd add user 1user WEP40.WIN 1.3
+llscmd add user 1user WGTPLT11.WIN 1.3
+llscmd add user 1user WORD11B.PM 1.3
+llscmd add user 1user WORD6.DOS 1.3
+llscmd add user 1user WORD60.NT 1.3
+llscmd add user 1user WORD60C.WIN 1.3
+llscmd add user 1user word6cnv.nt 1.3
+llscmd add user 1user WORD6CNV.WIN 1.3
+llscmd add user 1user WORDVW60.WIN 1.3
+llscmd add user 1user WORKS30.DOS 1.3
+llscmd add user 1user WORKS30B.WIN 1.3
+llscmd add user 1user WPP310.WIN 1.3
+llscmd add user 1user WRITER10.WIN 1.3
+llscmd add user 1user 3com 1.3
+llscmd add user 3user BLPNT901.ALL 1.3
+llscmd add user 3user ARTIST10.WIN 1.3
+llscmd add user 3user BASEBL94.WIN 1.3
+llscmd add user 3user BA331.DOS 1.3
+llscmd add user 3user OS/2 1.3
+llscmd add user 3user NetWare 1.3
+llscmd add user 4user DOS 1.3
+llscmd add user 4user C 1.3
+llscmd add user 4user CHART300.DOS 1.3
+llscmd add user 4user CINEMA94.WIN 1.3
+llscmd add user 4user DELTA10A.WIN 1.3
+llscmd add user 4user DINOSAUR.WIN 1.3
+llscmd add user 4user DRAW10.WIN 1.3
+llscmd add user 5user COBOL500 1.3
+llscmd add user 5user FORT510A.DOS 1.3
+llscmd add user 5user FORTRN32.NT 1.3
+llscmd add user 5user VC21.NT 1.3
+llscmd add user 1user cdex 1.3
+llscmd add user 1user CLIENTS.NET 1.3
+llscmd add user 1user CMSRVWS.11 1.3
+llscmd add user 1user DCIDDK10 1.3
+llscmd add user 1user PPT40c.WIN 1.3
+llscmd add user 1user PROFIT1B.WIN 1.3
+llscmd add user 1user PROJ40.DOS 1.3
+llscmd add user 1user LMUNIX 1.3
+llscmd add user 1user MOUSE10a.ALL 1.3
+llscmd add user 1user MSD211.DOS 1.3
+llscmd add user 1user RESKIT.NT 1.3
+llscmd add user 1user SNA21.NT 1.3
+llscmd add user 1user SQL.NT 1.3
+llscmd add user 1user SQL42B.OS2 1.3
+llscmd add user 1user SQLRES10.OS2 1.3
+llscmd add user 1user WFW311.WIN 1.3
+llscmd add user 1user WFWCONN.WIN 1.3
+llscmd add user 1user WIN31 1.3
+llscmd add user 2user WING10.WIN 1.3
+llscmd add user 2user winlogin.win 1.3
+llscmd add user 2user WINNT.NT 1.3
+llscmd add user 2user WINNT35.SRV 1.3
+llscmd add user 2user WMDLRSDK.WIN 1.3
+llscmd add user 2user GOLF20.WIN 1.3
+llscmd add user 2user GRAPH50.WIN 1.3
+llscmd add user 2user GREETING.WIN 1.3
+llscmd add user 2user VC20A.NT 1.3
+llscmd add user 2user WINNT35.WKS 1.3
+llscmd add user 2user wpen100a.win 1.3
+llscmd add user 2user WPS.WIN 1.3
+llscmd add user 2user TAPI10.WIN 1.3
+llscmd add user 2user TCPIP.ALL 1.3
+llscmd add user 2user TCPUTIL1.3 1.3
+llscmd add user 2user VFW11.WIN 1.3
+llscmd add user 2user WRESKIT2.00 1.3
+llscmd add user 2user WSS20.WIN 1.3
+llscmd add user 2user MacWord 1.3
+llscmd add user 2user PowerPoint 1.3
+llscmd add user 2user BoundsChecker 1.3
+llscmd add user 2user Quattro 1.3
+llscmd add user 2user WordPerfect 1.3
+llscmd add user 2user Office 1.3
+llscmd add user 2user PerfectOffice 1.3
+llscmd add user 2user SQL 1.3
diff --git a/private/net/svcdlls/lls/test/ct/p3.bat b/private/net/svcdlls/lls/test/ct/p3.bat
new file mode 100644
index 000000000..99b5cd9e1
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p3.bat
@@ -0,0 +1,186 @@
+llscmd add user 1user Money 3.1
+llscmd add user 1user Schedule+ 3.1
+llscmd add user 1user Mail 3.1
+llscmd add user 1user MONEY30.WIN 3.1
+llscmd add user 1user MP420A.DOS 3.1
+llscmd add user 1user MSMAIL.ALL 3.1
+llscmd add user 1user Win32 3.1
+llscmd add user 1user Win32s 3.1
+llscmd add user 1user Excel 3.1
+llscmd add user 1user Word 3.1
+llscmd add user 1user ARCADE10.WIN 3.1
+llscmd add user 1user ARTGALL.WIN 3.1
+llscmd add user 1user VCP100.WIN 3.1
+llscmd add user 1user WINLIB 3.1
+llscmd add user 1user ACCESS20.WIN 3.1
+llscmd add user 1user FORTRN32.WIN 3.1
+llscmd add user 1user IMSL.NT 3.1
+llscmd add user 1user IMSL32.DOS 3.1
+llscmd add user 1user IMSL51.DOS 3.1
+llscmd add user 1user Chicago 3.1
+llscmd add user 1user Windows95 3.1
+llscmd add user 1user Access 3.1
+llscmd add user 1user MASM611 3.1
+llscmd add user 1user MFCKIT 3.1
+llscmd add user 2user mt300a.all 3.1
+llscmd add user 2user QWGRAPH 3.1
+llscmd add user 2user VB10.DOS 3.1
+llscmd add user 2user CBT 3.1
+llscmd add user 2user EISPAK11.WIN 3.1
+llscmd add user 2user ENCART95.WIN 3.1
+llscmd add user 2user EXCEL30.PM 3.1
+llscmd add user 2user VB30.WIN 3.1
+llscmd add user 2user MASM 3.1
+llscmd add user 2user OLE 3.1
+llscmd add user 2user Chart 3.1
+llscmd add user 2user Encarta 3.1
+llscmd add user 3user FlightSim 3.1
+llscmd add user 3user Paradox 3.1
+llscmd add user 3user VC150.WIN 3.1
+llscmd add user 3user VC20.MAC 3.1
+llscmd add user 3user IMAGER10.WIN 3.1
+llscmd add user 3user LRND300.DOS 3.1
+llscmd add user 3user MMGOLF10.WIN 3.1
+llscmd add user 3user MOM.NT 3.1
+llscmd add user 3user MSDOS622.DOS 3.1
+llscmd add user 3user MSKBD1.ALL 3.1
+llscmd add user 3user MSLOGO.TTF 3.1
+llscmd add user 3user MSSMS10 3.1
+llscmd add user 3user MOM42.WIN 3.1
+llscmd add user 3user SNA 3.1
+llscmd add user 3user Exchange 3.1
+llscmd add user 3user MS_EFORM.WIN 3.1
+llscmd add user 3user ODAKIT 3.1
+llscmd add user 3user ODBC210.WIN 3.1
+llscmd add user 3user VC20.NT 3.1
+llscmd add user 3user BOB.WIN 3.1
+llscmd add user 3user BOOK94.WIN 3.1
+llscmd add user 3user BOWEP.WIN 3.1
+llscmd add user 3user EXCEL50.NT 3.1
+llscmd add user 3user EXCEL50C.WIN 3.1
+llscmd add user 3user FLTSIM5a.DOS 3.1
+llscmd add user 3user FOXPR26A.DOS 3.1
+llscmd add user 3user FOXPR26A.WIN 3.1
+llscmd add user 3user FOXPRO26.UNX 3.1
+llscmd add user 3user WKGRP311.DOS 3.1
+llscmd add user 3user wlo09 3.1
+llscmd add user 3user wlo10 3.1
+llscmd add user 3user ACCSOLPK.WIN 3.1
+llscmd add user 3user ACCUPTLS.WIN 3.1
+llscmd add user 3user ADT20.WIN 3.1
+llscmd add user 3user GS100.DOS 3.1
+llscmd add user 1user ODBCDDP2.NT 3.1
+llscmd add user 1user ODBCDDP2.WIN 3.1
+llscmd add user 1user OFF42C.WIN 3.1
+llscmd add user 1user OFF43c.WIN 3.1
+llscmd add user 1user OFFAST10.WIN 3.1
+llscmd add user 1user OFFDK10.WIN 3.1
+llscmd add user 1user FONTPACK.WIN 3.1
+llscmd add user 1user FONTPAK2.WIN 3.1
+llscmd add user 1user HPFNTSET.WIN 3.1
+llscmd add user 1user hyper.100 3.1
+llscmd add user 1user IMSL.NT 3.1
+llscmd add user 1user IMSL32.DOS 3.1
+llscmd add user 1user LM22 3.1
+llscmd add user 1user LMRES10.OS2 3.1
+llscmd add user 1user LMSFMAC3.1A 3.1
+llscmd add user 1user OFFICE.NT 3.1
+llscmd add user 1user OLE202.WIN 3.1
+llscmd add user 3user ONLN200.WIN 3.1
+llscmd add user 3user PROJ40.WIN 3.1
+llscmd add user 3user MSTCPIP.ALL 3.1
+llscmd add user 3user MVWR200.WIN 3.1
+llscmd add user 3user NETMON 3.1
+llscmd add user 3user OS2-1.31 3.1
+llscmd add user 3user PMSUB35.NT 3.1
+llscmd add user 3user RAS11.LM 3.1
+llscmd add user 3user PUB20a.WIN 3.1
+llscmd add user 3user PUBDSIGN.WIN 3.1
+llscmd add user 3user SAMPLER 3.1
+llscmd add user 3user SCENES20.WIN 3.1
+llscmd add user 3user SCHED10A.WIN 3.1
+llscmd add user 3user SGML10.WIN 3.1
+llscmd add user 3user SLM.ALL 3.1
+llscmd add user 4user SNDBIT10.WIN 3.1
+llscmd add user 4user SPACE10.DOS 3.1
+llscmd add user 4user SS30.DOS 3.1
+llscmd add user 4user ss31std.win 3.1
+llscmd add user 4user WA100.WIN 3.1
+llscmd add user 4user WEP10B.WIN 3.1
+llscmd add user 4user WEP20.WIN 3.1
+llscmd add user 4user WEP30.WIN 3.1
+llscmd add user 4user WEP40.WIN 3.1
+llscmd add user 4user WGTPLT11.WIN 3.1
+llscmd add user 4user WORD11B.PM 3.1
+llscmd add user 4user WORD6.DOS 3.1
+llscmd add user 4user WORD60.NT 3.1
+llscmd add user 4user WORD60C.WIN 3.1
+llscmd add user 4user word6cnv.nt 3.1
+llscmd add user 4user WORD6CNV.WIN 3.1
+llscmd add user 4user WORDVW60.WIN 3.1
+llscmd add user 4user WORKS30.DOS 3.1
+llscmd add user 4user WORKS30B.WIN 3.1
+llscmd add user 4user WPP310.WIN 3.1
+llscmd add user 4user WRITER10.WIN 3.1
+llscmd add user 4user 3com 3.1
+llscmd add user 4user BLPNT901.ALL 3.1
+llscmd add user 4user ARTIST10.WIN 3.1
+llscmd add user 4user BASEBL94.WIN 3.1
+llscmd add user 4user BA331.DOS 3.1
+llscmd add user 4user OS/2 3.1
+llscmd add user 4user NetWare 3.1
+llscmd add user 4user DOS 3.1
+llscmd add user 4user C 3.1
+llscmd add user 2user CHART300.DOS 3.1
+llscmd add user 2user CINEMA94.WIN 3.1
+llscmd add user 2user DELTA10A.WIN 3.1
+llscmd add user 2user DINOSAUR.WIN 3.1
+llscmd add user 2user DRAW10.WIN 3.1
+llscmd add user 2user COBOL500 3.1
+llscmd add user 2user FORT510A.DOS 3.1
+llscmd add user 2user FORTRN32.NT 3.1
+llscmd add user 2user VC21.NT 3.1
+llscmd add user 1user cdex 3.1
+llscmd add user 1user CLIENTS.NET 3.1
+llscmd add user 1user CMSRVWS.11 3.1
+llscmd add user 1user DCIDDK10 3.1
+llscmd add user 1user PPT40c.WIN 3.1
+llscmd add user 1user PROFIT1B.WIN 3.1
+llscmd add user 1user PROJ40.DOS 3.1
+llscmd add user 1user LMUNIX 3.1
+llscmd add user 1user MOUSE10a.ALL 3.1
+llscmd add user 1user MSD211.DOS 3.1
+llscmd add user 1user RESKIT.NT 3.1
+llscmd add user 3user SNA21.NT 3.1
+llscmd add user 3user SQL.NT 3.1
+llscmd add user 3user SQL42B.OS2 3.1
+llscmd add user 3user SQLRES10.OS2 3.1
+llscmd add user 3user WFW311.WIN 3.1
+llscmd add user 3user WFWCONN.WIN 3.1
+llscmd add user 3user WIN31 3.1
+llscmd add user 3user WING10.WIN 3.1
+llscmd add user 3user winlogin.win 3.1
+llscmd add user 1user WINNT.NT 3.1
+llscmd add user 1user WINNT35.SRV 3.1
+llscmd add user 1user WMDLRSDK.WIN 3.1
+llscmd add user 1user GOLF20.WIN 3.1
+llscmd add user 1user GRAPH50.WIN 3.1
+llscmd add user 1user GREETING.WIN 3.1
+llscmd add user 1user VC20A.NT 3.1
+llscmd add user 1user WINNT35.WKS 3.1
+llscmd add user 1user wpen100a.win 3.1
+llscmd add user 1user WPS.WIN 3.1
+llscmd add user 1user TAPI10.WIN 3.1
+llscmd add user 1user TCPIP.ALL 3.1
+llscmd add user 1user TCPUTIL3.1 3.1
+llscmd add user 1user VFW11.WIN 3.1
+llscmd add user 2user WRESKIT2.00 3.1
+llscmd add user 2user WSS20.WIN 3.1
+llscmd add user 2user MacWord 3.1
+llscmd add user 2user PowerPoint 3.1
+llscmd add user 2user BoundsChecker 3.1
+llscmd add user 2user Quattro 3.1
+llscmd add user 2user WordPerfect 3.1
+llscmd add user 2user Office 3.1
+llscmd add user 2user PerfectOffice 3.1
+llscmd add user 2user SQL 3.1
diff --git a/private/net/svcdlls/lls/test/ct/p4.bat b/private/net/svcdlls/lls/test/ct/p4.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p4.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/p5.bat b/private/net/svcdlls/lls/test/ct/p5.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/p5.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/prod.bat b/private/net/svcdlls/lls/test/ct/prod.bat
new file mode 100644
index 000000000..0b5f8ef26
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/prod.bat
@@ -0,0 +1,186 @@
+Money 1.0
+Schedule+ 1.0
+Mail 1.0
+MONEY30.WIN 1.0
+MP420A.DOS 1.0
+MSMAIL.ALL 1.0
+Win32 1.0
+Win32s 1.0
+Excel 1.0
+Word 1.0
+ARCADE10.WIN 1.0
+ARTGALL.WIN 1.0
+VCP100.WIN 1.0
+WINLIB 1.0
+ACCESS20.WIN 1.0
+FORTRN32.WIN 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+IMSL51.DOS 1.0
+Chicago 1.0
+Windows95 1.0
+Access 1.0
+MASM611 1.0
+MFCKIT 1.0
+mt300a.all 1.0
+QWGRAPH 1.0
+VB10.DOS 1.0
+CBT 1.0
+EISPAK11.WIN 1.0
+ENCART95.WIN 1.0
+EXCEL30.PM 1.0
+VB30.WIN 1.0
+MASM 1.0
+OLE 1.0
+Chart 1.0
+Encarta 1.0
+FlightSim 1.0
+Paradox 1.0
+VC150.WIN 1.0
+VC20.MAC 1.0
+IMAGER10.WIN 1.0
+LRND300.DOS 1.0
+MMGOLF10.WIN 1.0
+MOM.NT 1.0
+MSDOS622.DOS 1.0
+MSKBD1.ALL 1.0
+MSLOGO.TTF 1.0
+MSSMS10 1.0
+MOM42.WIN 1.0
+SNA 1.0
+Exchange 1.0
+MS_EFORM.WIN 1.0
+ODAKIT 1.0
+ODBC210.WIN 1.0
+VC20.NT 1.0
+BOB.WIN 1.0
+BOOK94.WIN 1.0
+BOWEP.WIN 1.0
+EXCEL50.NT 1.0
+EXCEL50C.WIN 1.0
+FLTSIM5a.DOS 1.0
+FOXPR26A.DOS 1.0
+FOXPR26A.WIN 1.0
+FOXPRO26.UNX 1.0
+WKGRP311.DOS 1.0
+wlo09 1.0
+wlo10 1.0
+ACCSOLPK.WIN 1.0
+ACCUPTLS.WIN 1.0
+ADT20.WIN 1.0
+GS100.DOS 1.0
+ODBCDDP2.NT 1.0
+ODBCDDP2.WIN 1.0
+OFF42C.WIN 1.0
+OFF43c.WIN 1.0
+OFFAST10.WIN 1.0
+OFFDK10.WIN 1.0
+FONTPACK.WIN 1.0
+FONTPAK2.WIN 1.0
+HPFNTSET.WIN 1.0
+hyper.100 1.0
+IMSL.NT 1.0
+IMSL32.DOS 1.0
+LM22 1.0
+LMRES10.OS2 1.0
+LMSFMAC1.0A 1.0
+OFFICE.NT 1.0
+OLE202.WIN 1.0
+ONLN200.WIN 1.0
+PROJ40.WIN 1.0
+MSTCPIP.ALL 1.0
+MVWR200.WIN 1.0
+NETMON 1.0
+OS2-1.31 1.0
+PMSUB35.NT 1.0
+RAS11.LM 1.0
+PUB20a.WIN 1.0
+PUBDSIGN.WIN 1.0
+SAMPLER 1.0
+SCENES20.WIN 1.0
+SCHED10A.WIN 1.0
+SGML10.WIN 1.0
+SLM.ALL 1.0
+SNDBIT10.WIN 1.0
+SPACE10.DOS 1.0
+SS30.DOS 1.0
+ss31std.win 1.0
+WA100.WIN 1.0
+WEP10B.WIN 1.0
+WEP20.WIN 1.0
+WEP30.WIN 1.0
+WEP40.WIN 1.0
+WGTPLT11.WIN 1.0
+WORD11B.PM 1.0
+WORD6.DOS 1.0
+WORD60.NT 1.0
+WORD60C.WIN 1.0
+word6cnv.nt 1.0
+WORD6CNV.WIN 1.0
+WORDVW60.WIN 1.0
+WORKS30.DOS 1.0
+WORKS30B.WIN 1.0
+WPP310.WIN 1.0
+WRITER10.WIN 1.0
+3com 1.0
+BLPNT901.ALL 1.0
+ARTIST10.WIN 1.0
+BASEBL94.WIN 1.0
+BA331.DOS 1.0
+OS/2 1.0
+NetWare 1.0
+DOS 1.0
+C 1.0
+CHART300.DOS 1.0
+CINEMA94.WIN 1.0
+DELTA10A.WIN 1.0
+DINOSAUR.WIN 1.0
+DRAW10.WIN 1.0
+COBOL500 1.0
+FORT510A.DOS 1.0
+FORTRN32.NT 1.0
+VC21.NT 1.0
+cdex 1.0
+CLIENTS.NET 1.0
+CMSRVWS.11 1.0
+DCIDDK10 1.0
+PPT40c.WIN 1.0
+PROFIT1B.WIN 1.0
+PROJ40.DOS 1.0
+LMUNIX 1.0
+MOUSE10a.ALL 1.0
+MSD211.DOS 1.0
+RESKIT.NT 1.0
+SNA21.NT 1.0
+SQL.NT 1.0
+SQL42B.OS2 1.0
+SQLRES10.OS2 1.0
+WFW311.WIN 1.0
+WFWCONN.WIN 1.0
+WIN31 1.0
+WING10.WIN 1.0
+winlogin.win 1.0
+WINNT.NT 1.0
+WINNT35.SRV 1.0
+WMDLRSDK.WIN 1.0
+GOLF20.WIN 1.0
+GRAPH50.WIN 1.0
+GREETING.WIN 1.0
+VC20A.NT 1.0
+WINNT35.WKS 1.0
+wpen100a.win 1.0
+WPS.WIN 1.0
+TAPI10.WIN 1.0
+TCPIP.ALL 1.0
+TCPUTIL1.0 1.0
+VFW11.WIN 1.0
+WRESKIT2.00 1.0
+WSS20.WIN 1.0
+MacWord 1.0
+PowerPoint 1.0
+BoundsChecker 1.0
+Quattro 1.0
+WordPerfect 1.0
+Office 1.0
+PerfectOffice 1.0
+SQL 1.0
diff --git a/private/net/svcdlls/lls/test/ct/prod.dat b/private/net/svcdlls/lls/test/ct/prod.dat
new file mode 100644
index 000000000..e0edb458c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/prod.dat
@@ -0,0 +1,186 @@
+Money
+Schedule+
+Mail
+MONEY30.WIN
+MP420A.DOS
+MSMAIL.ALL
+Win32
+Win32s
+Excel
+Word
+ARCADE10.WIN
+ARTGALL.WIN
+VCP100.WIN
+WINLIB
+ACCESS20.WIN
+FORTRN32.WIN
+IMSL.NT
+IMSL32.DOS
+IMSL51.DOS
+Chicago
+Windows95
+Access
+MASM611
+MFCKIT
+mt300a.all
+QWGRAPH
+VB10.DOS
+CBT
+EISPAK11.WIN
+ENCART95.WIN
+EXCEL30.PM
+VB30.WIN
+MASM
+OLE
+Chart
+Encarta
+FlightSim
+Paradox
+VC150.WIN
+VC20.MAC
+IMAGER10.WIN
+LRND300.DOS
+MMGOLF10.WIN
+MOM.NT
+MSDOS622.DOS
+MSKBD1.ALL
+MSLOGO.TTF
+MSSMS10
+MOM42.WIN
+SNA
+Exchange
+MS_EFORM.WIN
+ODAKIT
+ODBC210.WIN
+VC20.NT
+BOB.WIN
+BOOK94.WIN
+BOWEP.WIN
+EXCEL50.NT
+EXCEL50C.WIN
+FLTSIM5a.DOS
+FOXPR26A.DOS
+FOXPR26A.WIN
+FOXPRO26.UNX
+WKGRP311.DOS
+wlo09
+wlo10
+ACCSOLPK.WIN
+ACCUPTLS.WIN
+ADT20.WIN
+GS100.DOS
+ODBCDDP2.NT
+ODBCDDP2.WIN
+OFF42C.WIN
+OFF43c.WIN
+OFFAST10.WIN
+OFFDK10.WIN
+FONTPACK.WIN
+FONTPAK2.WIN
+HPFNTSET.WIN
+hyper.100
+IMSL.NT
+IMSL32.DOS
+LM22
+LMRES10.OS2
+LMSFMAC1.0A
+OFFICE.NT
+OLE202.WIN
+ONLN200.WIN
+PROJ40.WIN
+MSTCPIP.ALL
+MVWR200.WIN
+NETMON
+OS2-1.31
+PMSUB35.NT
+RAS11.LM
+PUB20a.WIN
+PUBDSIGN.WIN
+SAMPLER
+SCENES20.WIN
+SCHED10A.WIN
+SGML10.WIN
+SLM.ALL
+SNDBIT10.WIN
+SPACE10.DOS
+SS30.DOS
+ss31std.win
+WA100.WIN
+WEP10B.WIN
+WEP20.WIN
+WEP30.WIN
+WEP40.WIN
+WGTPLT11.WIN
+WORD11B.PM
+WORD6.DOS
+WORD60.NT
+WORD60C.WIN
+word6cnv.nt
+WORD6CNV.WIN
+WORDVW60.WIN
+WORKS30.DOS
+WORKS30B.WIN
+WPP310.WIN
+WRITER10.WIN
+3com
+BLPNT901.ALL
+ARTIST10.WIN
+BASEBL94.WIN
+BA331.DOS
+OS/2
+NetWare
+DOS
+C
+CHART300.DOS
+CINEMA94.WIN
+DELTA10A.WIN
+DINOSAUR.WIN
+DRAW10.WIN
+COBOL500
+FORT510A.DOS
+FORTRN32.NT
+VC21.NT
+cdex
+CLIENTS.NET
+CMSRVWS.11
+DCIDDK10
+PPT40c.WIN
+PROFIT1B.WIN
+PROJ40.DOS
+LMUNIX
+MOUSE10a.ALL
+MSD211.DOS
+RESKIT.NT
+SNA21.NT
+SQL.NT
+SQL42B.OS2
+SQLRES10.OS2
+WFW311.WIN
+WFWCONN.WIN
+WIN31
+WING10.WIN
+winlogin.win
+WINNT.NT
+WINNT35.SRV
+WMDLRSDK.WIN
+GOLF20.WIN
+GRAPH50.WIN
+GREETING.WIN
+VC20A.NT
+WINNT35.WKS
+wpen100a.win
+WPS.WIN
+TAPI10.WIN
+TCPIP.ALL
+TCPUTIL1.0
+VFW11.WIN
+WRESKIT2.00
+WSS20.WIN
+MacWord
+PowerPoint
+BoundsChecker
+Quattro
+WordPerfect
+Office
+PerfectOffice
+SQL
diff --git a/private/net/svcdlls/lls/test/ct/rpc.bat b/private/net/svcdlls/lls/test/ct/rpc.bat
new file mode 100644
index 000000000..3f4a2d01c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/ct/rpc.bat
@@ -0,0 +1,139 @@
+
+if NOT '%1' == '' set srv=%1
+if NOT '%2' == '' set user=%2
+if NOT '%3' == '' set domain=%3
+
+if '%srv%' == '' goto usage
+if '%user%' == '' goto usage
+if '%domain%' == '' goto usage
+goto init
+
+:usage
+echo Usage: rpc.bat servername Username
+goto done
+
+
+:init
+
+llscmd rpc %srv% llslicenseadd "SQL 4.2" "Mr. Slate" 5 "New employees"
+llscmd rpc %srv% llslicenseadd "SQL 4.2" "Betty Rubble" 2 "Authoried by Mr. Slate"
+llscmd rpc %srv% llslicenseadd "SNA 3.0" "Fred Flinstone" 1 "Who let me in"
+llscmd rpc %srv% llslicenseadd "Exchange 1.0" "Mr. Slate" 99 "On the beta"
+
+llscmd rpc %srv% llsmappingadd Lab2 2 "Computer Lab 2/2222"
+llscmd rpc %srv% llsmappingadd Lab1 1 "Computer Lab 1/1111"
+llscmd rpc %srv% llsmappingadd Lab3 3 "Computer Lab 3/3333"
+
+llscmd rpc %srv% llsmappinguseradd Lab1 %domain%\Fred
+
+llscmd rpc %srv% llsmappinguseradd Lab2 %domain%\Barney
+llscmd rpc %srv% llsmappinguseradd Lab2 %domain%\Wilma
+
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\Pebbles
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\BamBam
+llscmd rpc %srv% llsmappinguseradd Lab3 %domain%\Mr.Slate
+
+echo off
+rem llscmd add user %domain%\\"Fred" SQL 4.2
+rem llscmd add user %domain%\\"Barney" SQL 4.2
+rem llscmd add user %domain%\\"Wilma" SQL 4.2
+rem llscmd add user %domain%\\"Pebbles" SQL 4.2
+rem llscmd add user %domain%\\"BamBam" SQL 4.2
+rem llscmd add user %domain%\\"Mr.Slate" SNA 3.0
+rem llscmd add user %domain%\\"GeorgeJ" SNA 3.0
+rem llscmd add user %domain%\\"AstroJ" SNA 3.0
+
+rem llscmd add user %domain%\\"Fred" Exchange 1.0
+rem llscmd add user %domain%\\"Barney" Exchange 1.0
+rem llscmd add user %domain%\\"Wilma" Exchange 1.0
+rem llscmd add user %domain%\\"Pebbles" Exchange 1.0
+rem llscmd add user %domain%\\"BamBam" Exchange 1.0
+rem llscmd add user %domain%\\"Mr.Slate" Exchange 1.0
+rem llscmd add user %domain%\\"GeorgeJ" Exchange 1.0
+
+
+
+
+
+rem llscmd rpc [\\server] LlsLicenseEnum
+llscmd rpc %srv% LlsLicenseEnum
+
+
+Rem llscmd rpc [\\server] LlsLicenseAdd product admin quantity comment
+llscmd rpc %srv% LlsLicenseAdd myproduct %user% 1 "test commnet"
+
+
+Rem llscmd rpc [\\server] LlsProductEnum Level
+llscmd rpc %srv% LlsProductEnum 0
+
+
+Rem llscmd rpc [\\server] LlsProductUserEnum Level Product
+llscmd rpc %srv% LlsProductUserEnum 0 myproduct
+
+
+Rem llscmd rpc [\\server] LlsProductLicenseEnum level product version
+llscmd rpc %srv% LlsProductLicenseEnum 0 myproduct 1.0
+
+
+Rem llscmd rpc [\\server] LlsUserEnum Level
+llscmd rpc %srv% LlsUserEnum 0
+
+
+Rem llscmd rpc [\\server] LlsUserProductEnum Level user
+llscmd rpc %srv% LlsUserProductEnum 0 %user%
+
+
+Rem llscmd rpc [\\server] LlsUserInfoGet Level User
+llscmd rpc %srv% LlsUserInfoGet 0 %user%
+
+
+Rem llscmd rpc [\\server] LlsUserInfoSet Level User Mapping [BACKOFFICE]
+llscmd rpc %srv% LlsUserInfoSet 0 %user% XXX
+
+
+Rem llscmd rpc [\\server] LlsUserDelete User
+llscmd rpc %srv% LlsUserDelete %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingEnum Level
+llscmd rpc %srv% LlsMappingEnum 0
+
+
+Rem llscmd rpc [\\server] LlsMappingInfoGet Level Mapping
+llscmd rpc %srv% LlsMappingInfoGet 0 XXX
+
+
+Rem llscmd rpc [\\server] LlsMappingInfoSet Level Mapping Licenses Comment
+llscmd rpc %srv% LlsMappingInfoSet 0 XXX 2 "test comment 2"
+
+
+Rem llscmd rpc [\\server] LlsMappingUserEnum Level Mapping
+llscmd rpc %srv% LlsMappingUserEnum 0 XXX
+
+
+Rem llscmd rpc [\\server] LlsMappingUserAdd Mapping User
+llscmd rpc %srv% LlsMappingUserAdd XXX %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingUserDelete Mapping User
+llscmd rpc %srv% LlsMappingUserDelete XXX %user%
+
+
+Rem llscmd rpc [\\server] LlsMappingAdd Product admin quantity comment
+llscmd rpc %srv% LlsMappingAdd myproduct %user% 3 "test comment 3"
+
+
+Rem llscmd rpc [\\server] LlsMappingDelete Mapping
+llscmd rpc %srv% LlsMappingDelete XXX
+
+
+Rem llscmd rpc [\\server] LlsServiceInfoGet
+llscmd rpc %srv% LlsServiceInfoGet
+
+
+Rem llscmd rpc [\\server] LlsServiceInfoSet repl-mode time enterprise
+rem repl-mode 0=replicate every..(time) 1=replicat @...(time)
+llscmd rpc %srv% LlsServiceInfoSet 0 12:00
+
+done:
+rpc.bat
diff --git a/private/net/svcdlls/lls/test/dirs b/private/net/svcdlls/lls/test/dirs
new file mode 100644
index 000000000..3a980091d
--- /dev/null
+++ b/private/net/svcdlls/lls/test/dirs
@@ -0,0 +1 @@
+DIRS=common llscmd llsdbg
diff --git a/private/net/svcdlls/lls/test/llscmd/llscmd.c b/private/net/svcdlls/lls/test/llscmd/llscmd.c
new file mode 100644
index 000000000..a3e24abb5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/llscmd.c
@@ -0,0 +1,1616 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ llscmd.c
+
+Abstract:
+
+ License Logging Service test program.
+
+Author:
+
+ Arthur Hanson (arth) 1-26-95
+
+Environment:
+
+Revision History:
+
+--*/
+
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <lmcons.h>
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ntlsapi.h>
+#include "..\..\inc\llsapi.h"
+#include "..\..\inc\debug.h"
+#include "..\common\llsdbg.h"
+
+#define MAX_CHAR_LEN 100
+#define MAX_WSTR_LEN 100
+
+char *ProgramName;
+
+//
+// This handles arguments from command line or from a file - Un-Marshall all
+// the commands into this array and then process the array so we can use
+// a common processing routine.
+//
+// Array allows up to 10 command line arguments, each 100 chars long.
+//
+ULONG NumberArguments = 0;
+ULONG Index = 0;
+char Arguments[10][MAX_CHAR_LEN];
+LS_STATUS_CODE RetCode;
+
+char tmpStr[MAX_CHAR_LEN];
+TCHAR UserName[MAX_CHAR_LEN];
+TCHAR ProductName[MAX_CHAR_LEN];
+TCHAR Version[MAX_CHAR_LEN];
+
+BOOLEAN IsAdmin = FALSE;
+BOOL NoFree = FALSE;
+
+BYTE bufSID[1024];
+DWORD cbbufSid = sizeof(bufSID);
+
+HANDLE LlsDllHandle = NULL;
+FARPROC LicenseRequest;
+FARPROC LicenseFree;
+NT_LS_DATA Data;
+
+BOOL LocalRPC = TRUE;
+LLS_HANDLE RPCHandle = NULL;
+TCHAR Server[MAX_CHAR_LEN];
+
+
+HANDLE LlsRPCHandle = NULL;
+FARPROC pLlsConnectEnterprise;
+FARPROC pLlsClose;
+FARPROC pLlsFreeMemory;
+FARPROC pLlsLicenseEnum;
+FARPROC pLlsLicenseAdd;
+FARPROC pLlsProductEnum;
+FARPROC pLlsProductUserEnum;
+FARPROC pLlsProductLicenseEnum;
+FARPROC pLlsUserEnum;
+FARPROC pLlsUserProductEnum;
+FARPROC pLlsUserProductDelete;
+FARPROC pLlsUserInfoGet;
+FARPROC pLlsUserInfoSet;
+FARPROC pLlsUserDelete;
+FARPROC pLlsMappingEnum;
+FARPROC pLlsMappingInfoGet;
+FARPROC pLlsMappingInfoSet;
+FARPROC pLlsMappingUserEnum;
+FARPROC pLlsMappingUserAdd;
+FARPROC pLlsMappingUserDelete;
+FARPROC pLlsMappingAdd;
+FARPROC pLlsMappingDelete;
+FARPROC pLlsServiceInfoGet;
+FARPROC pLlsServiceInfoSet;
+
+
+#define DEFAULT_PRODUCT "Test Product"
+#define DEFAULT_USER ""
+#define DEFAULT_VERSION "12.345"
+
+
+#ifdef xxxx
+/////////////////////////////////////////////////////////////////////////
+void ParseWord(char **lpch, LPSTR tmpStr) {
+ char *ch;
+ char *lch;
+ char *pch;
+
+ ch = *lpch;
+ lch = pch = tmpStr;
+
+ // remove any leading whitespace
+ while(*ch && ((*ch == ' ') || (*ch == '\t')))
+ ch++;
+
+ // transfer it to tmpStr (via pch pointer)
+ while (*ch && (*ch != ',')) {
+ // keep track of last non-whitespace
+ if ((*ch != ' ') && (*ch != '\t')) {
+ lch = pch;
+ lch++;
+ }
+
+ *pch++ = *ch++;
+ }
+
+ if (*ch == ',')
+ ch++;
+
+ // NULL terminate before last section of whitespace
+ *lch = '\0';
+ *lpch = ch;
+
+} // ParseWord
+
+
+/////////////////////////////////////////////////////////////////////////
+void map_ParseUser(LPTSTR Line, LPTSTR Name, LPTSTR NewName, LPTSTR Password) {
+ TCHAR *pch = Line;
+
+ lstrcpy(Name, TEXT(""));
+ lstrcpy(NewName, TEXT(""));
+ lstrcpy(Password, TEXT(""));
+
+ if (Line == NULL)
+ return;
+
+ ParseWord(&pch, Name);
+ if (lstrlen(Name) >= MAX_USER_NAME_LEN)
+ lstrcpy(Name, TEXT(""));
+
+ ParseWord(&pch, NewName);
+ if (lstrlen(NewName) >= MAX_USER_NAME_LEN)
+ lstrcpy(NewName, TEXT(""));
+
+ ParseWord(&pch, Password);
+ if (lstrlen(Password) > MAX_PW_LEN)
+ lstrcpy(Password, TEXT(""));
+
+} // map_ParseUser
+#endif
+
+
+void ReadLicenses(char *FileName, int NumLines) {
+ char line[MAX_CHAR_LEN], *result;
+ FILE *stream;
+ TCHAR wline[MAX_CHAR_LEN];
+ ULONG lHandle;
+ PULONG plHandle;
+ int i = 0;
+
+ plHandle = &lHandle;
+
+ if ((stream = fopen(FileName, "r")) != NULL) {
+ while ( ((i == 0) || (i < NumLines)) && (fgets(line, 100, stream) != NULL)) {
+ // get rid of ending newline
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ // convert to Unicode
+ OemToCharBuff(line, wline, strlen(line) + 1);
+ Data.DataType = 0;
+ Data.Data = (PVOID) wline;
+ Data.IsAdmin = IsAdmin;
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+
+ if (!NoFree) {
+ RetCode = (*LicenseFree) ( *plHandle );
+ printf("LicenseFree Return Code: 0X%XH\n",RetCode);
+ }
+
+ if (NumLines)
+ i++;
+
+ }
+ }
+
+ fclose(stream);
+
+} // ReadLicenses
+
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL NameToSid(WCHAR *pszServer, WCHAR *pszName, BYTE *buffer, DWORD *bufsiz) {
+ WCHAR szDomain[DNLEN+1];
+ DWORD cchDomain = sizeof(szDomain)/sizeof(szDomain[0]);
+ SID_NAME_USE snu;
+
+ return (LookupAccountNameW( pszServer,
+ pszName,
+ buffer,
+ bufsiz,
+ (unsigned short *) szDomain,
+ &cchDomain,
+ &snu));
+} // NameToSid
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s ADD [\n", ProgramName);
+ printf("\tUSER [NOFREE] [ADMIN] username [ product version ] |\n");
+ printf("\tSID [NOFREE] [ADMIN] username [ product version ] |\n");
+ printf("\tFILE filename #lines [NOFREE] [ADMIN] [ product version ] ]\n");
+
+} // ShowUsageAdd
+
+
+VOID DispatchAdd(int argc, char **argv) {
+ int Index = 3;
+ char *aProduct, *aVersion, *aUser, *FileName;
+ BOOL IsSid = FALSE;
+ BOOL IsFile = FALSE;
+ ULONG lHandle;
+ PULONG plHandle;
+ UNICODE_STRING UString;
+ char tUser[MAX_CHAR_LEN];
+ char tProduct[MAX_CHAR_LEN];
+ int FileCount = 0;
+
+ plHandle = &lHandle;
+
+ if (argc < 4) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ //
+ // Make sure the first param is correct.
+ //
+ if ( (!_strcmpi(argv[2], "USER")) || (!_strcmpi(argv[2], "SID")) ||
+ (!_strcmpi(argv[2], "FILE")) ) {
+
+ if (!_strcmpi(argv[2], "SID"))
+ IsSid = TRUE;
+
+ if (!_strcmpi(argv[2], "FILE")) {
+ if (argc < 5) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ FileName = argv[Index];
+ Index++;
+ FileCount = atoi(argv[Index]);
+ Index++;
+ IsFile = TRUE;
+
+ if (argc > Index + 1)
+ // Save off argv so if it is productname we don't ucase it
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ lstrcpy(UserName, TEXT(DEFAULT_USER));
+ aUser = DEFAULT_USER;
+
+ lstrcpy(ProductName, TEXT(DEFAULT_PRODUCT));
+ aProduct = DEFAULT_PRODUCT;
+
+ lstrcpy(Version, TEXT(DEFAULT_VERSION));
+ aVersion = DEFAULT_VERSION;
+
+ // Save off argv so if it is username so we don't ucase it
+ if (argc > Index)
+ strcpy(tUser, argv[Index]);
+
+ if ((argc > Index + 1) && (!_strcmpi(argv[Index], "NOFREE"))) {
+ if (argc < Index + 2) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ Index++;
+ strcpy(tUser, argv[Index]);
+ NoFree = TRUE;
+
+ if (IsFile)
+ if (argc > Index + 1)
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ if ((argc > Index + 1) && (!_strcmpi(argv[Index], "ADMIN"))) {
+ if (argc < Index + 2) {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+ Index++;
+ strcpy(tUser, argv[Index]);
+ IsAdmin = TRUE;
+
+ if (IsFile)
+ if (argc > Index + 1)
+ strcpy(tProduct, argv[Index]);
+
+ }
+
+ //
+ // If this is "USER" or "SID" then the next parm is the username, else
+ // if "FILE" then it can only be the product and version.
+ //
+ if (!IsFile) {
+ // Convert UserName to Unicode
+ aUser = tUser;
+ OemToCharBuff(aUser, UserName, strlen(aUser) + 1);
+ Index++;
+ }
+
+ // Check for product and version
+ if (argc > Index + 1) {
+ // Convert ProductName to Unicode
+ if (IsFile)
+ aProduct = tProduct;
+ else
+ aProduct = argv[Index];
+
+ OemToCharBuff(aProduct, ProductName, strlen(aProduct) + 1);
+ Index++;
+
+ // Convert Version to Unicode
+ aVersion = argv[Index];
+ OemToCharBuff(aVersion, Version, strlen(aVersion) + 1);
+ Index++;
+ }
+
+ //
+ // If "FILE" then branch off to file reading sub-routines now that
+ // we have all the defaults from the command line.
+ //
+ if (IsFile) {
+ ReadLicenses(FileName, FileCount);
+ return;
+ }
+
+ //
+ // Handle "SID" and "USER"
+ //
+ if (IsSid) {
+
+ if(NameToSid(TEXT(""), UserName, bufSID, &cbbufSid)) {
+ RtlConvertSidToUnicodeString(&UString, (PSID) bufSID, TRUE);
+ WideCharToMultiByte(CP_ACP, 0, UString.Buffer, -1, tmpStr, 100, NULL, NULL);
+ printf("Added Prod: %s Ver: %s User: %s\n", aProduct, aVersion, tmpStr);
+ RtlFreeUnicodeString(&UString);
+
+ Data.DataType = 1;
+ Data.Data = (PVOID) bufSID;
+ Data.IsAdmin = IsAdmin;
+
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+ } else {
+ printf("\nWARNING! SID Not Converted\n");
+ return;
+ }
+
+ } else {
+ Data.DataType = 0;
+ Data.Data = (PVOID) UserName;
+ Data.IsAdmin = IsAdmin;
+
+ RetCode = (*LicenseRequest) ( ProductName, Version, plHandle, &Data);
+ printf("Added Prod: %s Ver: %s User: %s\n", aProduct, aVersion, aUser);
+ printf("LicenseRequest Return Code: 0X%XH\n",RetCode);
+ }
+
+ if (!NoFree) {
+ RetCode = (*LicenseFree) ( *plHandle );
+ printf("LicenseFree Return Code: 0X%XH\n",RetCode);
+ }
+ } else {
+ ShowUsageAdd();
+ exit(0);
+ }
+
+} // DispatchAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageFile( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s FILE [filename]\n", ProgramName);
+
+} // ShowUsageFile
+
+
+VOID DispatchFile( ) {
+} // DispatchFile
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageReplicate( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s REPLICATE FORCE\n", ProgramName);
+
+} // ShowUsageReplicate
+
+
+VOID DispatchReplicate() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageReplicate();
+ exit(0);
+ }
+
+ if (!_strcmpi(Arguments[Index], "FORCE")) {
+ LlsDbgReplicationForce( );
+ }
+
+} // DispatchReplicate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID DispatchFlush() {
+ Index++;
+
+ LlsDbgDatabaseFlush( );
+
+} // DispatchReplicate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID InitRpcFunctions() {
+
+ LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
+ if (LlsRPCHandle == NULL) {
+ printf("Error loading LLSRPC.DLL\n");
+ exit(1);
+ }
+
+ pLlsConnectEnterprise = GetProcAddress(LlsRPCHandle, ("LlsConnectEnterpriseW"));
+ pLlsClose = GetProcAddress(LlsRPCHandle, ("LlsClose"));
+ pLlsFreeMemory = GetProcAddress(LlsRPCHandle, ("LlsFreeMemory"));
+ pLlsLicenseEnum = GetProcAddress(LlsRPCHandle, ("LlsLicenseEnumW"));
+ pLlsLicenseAdd = GetProcAddress(LlsRPCHandle, ("LlsLicenseAddW"));
+ pLlsProductEnum = GetProcAddress(LlsRPCHandle, ("LlsProductEnumW"));
+ pLlsProductUserEnum = GetProcAddress(LlsRPCHandle, ("LlsProductUserEnumW"));
+ pLlsProductLicenseEnum = GetProcAddress(LlsRPCHandle, ("LlsProductLicenseEnumW"));
+ pLlsUserEnum = GetProcAddress(LlsRPCHandle, ("LlsUserEnumW"));
+ pLlsUserProductEnum = GetProcAddress(LlsRPCHandle, ("LlsUserProductEnumW"));
+ pLlsUserProductDelete = GetProcAddress(LlsRPCHandle, ("LlsUserProductDeleteW"));
+ pLlsUserInfoGet = GetProcAddress(LlsRPCHandle, ("LlsUserInfoGetW"));
+ pLlsUserInfoSet = GetProcAddress(LlsRPCHandle, ("LlsUserInfoSetW"));
+ pLlsUserDelete = GetProcAddress(LlsRPCHandle, ("LlsUserDeleteW"));
+ pLlsMappingEnum = GetProcAddress(LlsRPCHandle, ("LlsGroupEnumW"));
+ pLlsMappingInfoGet = GetProcAddress(LlsRPCHandle, ("LlsGroupInfoGetW"));
+ pLlsMappingInfoSet = GetProcAddress(LlsRPCHandle, ("LlsGroupInfoSetW"));
+ pLlsMappingUserEnum = GetProcAddress(LlsRPCHandle, ("LlsGroupUserEnumW"));
+ pLlsMappingUserAdd = GetProcAddress(LlsRPCHandle, ("LlsGroupUserAddW"));
+ pLlsMappingUserDelete = GetProcAddress(LlsRPCHandle, ("LlsGroupUserDeleteW"));
+ pLlsMappingAdd = GetProcAddress(LlsRPCHandle, ("LlsGroupAddW"));
+ pLlsMappingDelete = GetProcAddress(LlsRPCHandle, ("LlsGroupDeleteW"));
+ pLlsServiceInfoGet = GetProcAddress(LlsRPCHandle, ("LlsServiceInfoGetW"));
+ pLlsServiceInfoSet = GetProcAddress(LlsRPCHandle, ("LlsServiceInfoSetW"));
+
+ if ( (pLlsConnectEnterprise == NULL) || (pLlsClose == NULL) || (pLlsFreeMemory == NULL) ||
+ (pLlsLicenseEnum == NULL) || (pLlsLicenseAdd == NULL) || (pLlsProductEnum == NULL) ||
+ (pLlsProductUserEnum == NULL) || (pLlsProductLicenseEnum == NULL) ||
+ (pLlsUserEnum == NULL) || (pLlsUserProductEnum == NULL) || (pLlsUserInfoGet == NULL) ||
+ (pLlsUserInfoSet == NULL) || (pLlsUserDelete == NULL) || (pLlsMappingEnum == NULL) ||
+ (pLlsMappingInfoGet == NULL) || (pLlsMappingInfoSet == NULL) || (pLlsMappingUserEnum == NULL) ||
+ (pLlsMappingUserAdd == NULL) || (pLlsMappingUserDelete == NULL) ||
+ (pLlsMappingAdd == NULL) || (pLlsMappingDelete == NULL) || (pLlsServiceInfoGet == NULL) ||
+ (pLlsServiceInfoSet == NULL) ) {
+
+ printf("Error loading functions from LLSRPC.DLL\n");
+ exit(1);
+ }
+
+} // InitRpcFunctions
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsLicenseEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsLicenseEnum\n", ProgramName);
+} // ShowUsageLlsLicenseEnum
+
+
+VOID Do_LlsLicenseEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_LICENSE_INFO_0 LicenseInfo;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+ static char Product[MAX_CHAR_LEN];
+ static char Admin[MAX_CHAR_LEN];
+ static char Comment[MAX_CHAR_LEN];
+
+ Level = 0;
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+
+ printf("UserEnum Level 0\n");
+ Status = (*pLlsLicenseEnum) ( RPCHandle, Level, &LicenseInfo, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Product, -1, Product, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Admin, -1, Admin, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, LicenseInfo[i].Comment, -1, Comment, 100, NULL, NULL);
+ printf(" Prod: %s Qty: %lu Admin: %s Comment: %s\n", Product, LicenseInfo[i].Quantity, Admin, Comment);
+ }
+ }
+
+} // Do_LlsLicenseEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsLicenseAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsLicenseAdd product admin quantity comment\n", ProgramName);
+} // ShowUsageLlsLicenseAdd
+
+
+VOID Do_LlsLicenseAdd() {
+ NTSTATUS Status;
+ DWORD Level = 0;
+ LONG Quantity;
+ LLS_LICENSE_INFO_0 LicenseInfo;
+ static TCHAR Admin[MAX_CHAR_LEN];
+ static TCHAR Comment[MAX_CHAR_LEN];
+ static TCHAR Product[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsLicenseAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Product, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Admin, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Quantity = (LONG) atoi(Arguments[Index]);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Comment, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ LicenseInfo.Product = Product;
+ LicenseInfo.Quantity = Quantity;
+ LicenseInfo.Date = 0;
+ LicenseInfo.Admin = Admin;
+ LicenseInfo.Comment = Comment;
+
+ Status = (*pLlsLicenseAdd) ( RPCHandle, Level, (LPBYTE) &LicenseInfo );
+
+} // Do_LlsLicenseAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductEnum level\n", ProgramName);
+} // ShowUsageLlsProductEnum
+
+
+VOID Do_LlsProductEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_PRODUCT_INFO_0 ProductInfo0;
+ PLLS_PRODUCT_INFO_1 ProductInfo1;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("ProductEnum Level 0\n");
+ Status = (*pLlsProductEnum) ( RPCHandle, Level, &ProductInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo0[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("ProductEnum Level 1\n");
+ Status = (*pLlsProductEnum) ( RPCHandle, Level, &ProductInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo1[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else {
+ printf("LlsProductEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsProductEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductUserEnum level product\n", ProgramName);
+} // ShowUsageLlsProductUserEnum
+
+
+VOID Do_LlsProductUserEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductUserEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsProductUserEnum) ( RPCHandle );
+
+} // Do_LlsProductUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsProductLicenseEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsProductLicenseEnum level product version\n", ProgramName);
+} // ShowUsageLlsProductLicenseEnum
+
+
+VOID Do_LlsProductLicenseEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsProductLicenseEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsProductLicenseEnum) ( RPCHandle );
+
+} // Do_LlsProductLicenseEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserEnum level\n", ProgramName);
+} // ShowUsageLlsUserEnum
+
+
+VOID Do_LlsUserEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_USER_INFO_0 UserInfo0;
+ PLLS_USER_INFO_1 UserInfo1;
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("UserEnum Level 0\n");
+ Status = (*pLlsUserEnum) ( RPCHandle, Level, &UserInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, UserInfo0[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("UserEnum Level 1\n");
+ Status = (*pLlsUserEnum) ( RPCHandle, Level, &UserInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, UserInfo1[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else {
+ printf("LlsUserEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserProductEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserProductEnum level user\n", ProgramName);
+} // ShowUsageLlsUserProductEnum
+
+
+VOID Do_LlsUserProductEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_USER_PRODUCT_INFO_0 ProductInfo0;
+ PLLS_USER_PRODUCT_INFO_1 ProductInfo1;
+ TCHAR User[MAX_CHAR_LEN];
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ Index++;
+
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+
+ OemToCharBuff(Arguments[Index], User, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ if (Level == 0) {
+ printf("UserProductEnum Level 0\n");
+ Status = (*pLlsUserProductEnum) ( RPCHandle, User, Level, &ProductInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo0[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("UserProductEnum Level 1\n");
+ Status = (*pLlsUserProductEnum) ( RPCHandle, User, Level, &ProductInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, ProductInfo1[i].Product, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s LastUsed: %lu Useage: %lu\n", tmpStr, ProductInfo1[i].LastUsed, ProductInfo1[i].UsageCount);
+ }
+ }
+ } else {
+ printf("LlsUserProductEnum Incorrect Level\n");
+ exit(0);
+ }
+
+} // Do_LlsUserProductEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserProductDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserProductDelete user product\n", ProgramName);
+} // ShowUsageLlsUserProductDelete
+
+
+VOID Do_LlsUserProductDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+ WCHAR wPrName[MAX_WSTR_LEN];
+ CHAR lpUser[MAX_CHAR_LEN];
+ CHAR lpProduct[MAX_CHAR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserProductDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wPrName, MAX_WSTR_LEN);
+
+
+ Status = (*pLlsUserProductDelete) ( RPCHandle, wUsName, wPrName);
+
+ WideCharToMultiByte(CP_ACP, 0, wUsName, -1, lpUser, MAX_CHAR_LEN, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, wPrName, -1, lpProduct, MAX_CHAR_LEN, NULL, NULL);
+ printf("Status= %ld, UserName= %s, Product= %s\n", Status, lpUser, lpProduct);
+
+} // Do_LlsUserProductDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserInfoGet level user\n", ProgramName);
+} // ShowUsageLlsUserInfoGet
+
+
+VOID Do_LlsUserInfoGet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserInfoGet();
+ exit(0);
+ }
+
+// Status = (*pLlsUserInfoGet) ( RPCHandle );
+
+} // Do_LlsUserInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserInfoSet User [BACKOFFICE]\n", ProgramName);
+ printf(" if BACKOFFICE is specified flag is set to TRUE, else FALSE\n");
+} // ShowUsageLlsUserInfoSet
+
+
+VOID Do_LlsUserInfoSet() {
+ NTSTATUS Status;
+ DWORD dwLevel, dwFlags;
+ LLS_USER_INFO_1 LlsUserInfo1;
+ CHAR lpUser[MAX_CHAR_LEN];
+ WCHAR wUsName[MAX_WSTR_LEN];
+
+ memset(&LlsUserInfo1, 0, sizeof(LLS_USER_INFO_1));
+
+ dwFlags = 0;
+ dwLevel = 1;
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserInfoSet();
+ exit(0);
+ }
+
+ strcpy(lpUser, Arguments[Index]);
+ Index++;
+
+ if (NumberArguments > Index) {
+
+ if(!_strcmpi("BACKOFFICE", Arguments[Index])){
+ dwFlags = LLS_FLAG_SUITE_USE;
+
+ } else {
+ ShowUsageLlsUserInfoSet();
+ exit(0);
+ }
+ } else {
+ //
+ // Take away the BackOffice status
+ //
+ dwFlags = 0;
+
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpUser, -1, wUsName, MAX_WSTR_LEN);
+ LlsUserInfo1.Name = wUsName;
+ LlsUserInfo1.Flags = dwFlags;
+
+
+ printf("\nUser=%s, dwFlags=%ld", lpUser, dwFlags);
+
+ Status = (*pLlsUserInfoSet) (RPCHandle, wUsName, dwLevel, (LPBYTE)(&LlsUserInfo1));
+
+ printf("\nStatus = %ld\n", Status);
+
+} // Do_LlsUserInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsUserDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsUserDelete user\n", ProgramName);
+} // ShowUsageLlsUserDelete
+
+
+VOID Do_LlsUserDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+
+ Status = (*pLlsUserDelete) ( RPCHandle, wUsName);
+ printf("\nStatus = %ld", Status);
+
+} // Do_LlsUserDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupEnum level\n", ProgramName);
+} // ShowUsageLlsMappingEnum
+
+
+VOID Do_LlsMappingEnum() {
+ NTSTATUS Status;
+ ULONG i;
+ DWORD Level;
+ PLLS_GROUP_INFO_0 MappingInfo0;
+ PLLS_GROUP_INFO_1 MappingInfo1;
+ char MappingName[MAX_CHAR_LEN];
+ char Comment[MAX_CHAR_LEN];
+ DWORD MaxLength, EntriesRead, TotalEntries, ResumeHandle;
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsUserEnum();
+ exit(0);
+ }
+
+ Level = (DWORD) atoi(Arguments[Index]);
+ MaxLength = MAXULONG;
+ EntriesRead = TotalEntries = ResumeHandle = 0;
+ if (Level == 0) {
+ printf("GroupEnum Level 0\n");
+ Status = (*pLlsMappingEnum) ( RPCHandle, Level, &MappingInfo0, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo0[i].Name, -1, tmpStr, 100, NULL, NULL);
+ printf(" Name: %s\n", tmpStr);
+ }
+ }
+ } else if (Level == 1) {
+ printf("GroupEnum Level 1\n");
+ Status = (*pLlsMappingEnum) ( RPCHandle, Level, &MappingInfo1, MaxLength, &EntriesRead, &TotalEntries, &ResumeHandle );
+
+ if (Status == ERROR_SUCCESS) {
+ for (i = 0; i < EntriesRead; i++) {
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo1[i].Name, -1, MappingName, 100, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, MappingInfo1[i].Comment, -1, Comment, 100, NULL, NULL);
+ printf(" Name: %s Comment: %s Licenses: %li\n", MappingName, Comment, MappingInfo1[i].Licenses);
+ }
+ }
+ } else {
+ printf("LlsGroupEnum Incorrect Level\n");
+ exit(0);
+ }
+
+
+} // Do_LlsMappingEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupInfoGet level LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingInfoGet
+
+
+VOID Do_LlsMappingInfoGet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoGet();
+ exit(0);
+ }
+
+// Status = (*pLlsMappingInfoGet) ( RPCHandle );
+
+} // Do_LlsMappingInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupInfoSet Level LicenseGroup licenses comment\n", ProgramName);
+} // ShowUsageLlsMappingInfoSet
+
+
+VOID Do_LlsMappingInfoSet() {
+ NTSTATUS Status;
+ DWORD dwLevel, dwLicenses;
+ LLS_GROUP_INFO_1 GpInfo;
+ WCHAR wGpName[MAX_WSTR_LEN];
+ WCHAR wComment[MAX_WSTR_LEN];
+
+ memset(&GpInfo, 0, sizeof(LLS_GROUP_INFO_1));
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ dwLevel = (DWORD)atoi(Arguments[Index]);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingInfoSet();
+ exit(0);
+ }
+ dwLicenses = (DWORD)atoi(Arguments[Index]);
+
+ Index++;
+ if (NumberArguments > Index)
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wComment, MAX_WSTR_LEN);
+ else
+ lstrcpy(wComment, L"");
+
+ GpInfo.Name = wGpName;
+ GpInfo.Comment = wComment;
+ GpInfo.Licenses = dwLicenses;
+
+ Status = (*pLlsMappingInfoSet) ( RPCHandle, wGpName, dwLevel,(LPBYTE)(&GpInfo));
+ printf("\nStatus = %ld\n",Status);
+} // Do_LlsMappingInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserEnum( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserEnum level LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingUserEnum
+
+
+VOID Do_LlsMappingUserEnum() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserEnum();
+ exit(0);
+ }
+
+// Status = (*pLlsMappingUserEnum) ( RPCHandle );
+
+} // Do_LlsMappingUserEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserAdd LicenseGroup user\n", ProgramName);
+} // ShowUsageLlsMappingUserAdd
+
+
+VOID Do_LlsMappingUserAdd() {
+ NTSTATUS Status;
+ static TCHAR Mapping[MAX_CHAR_LEN];
+ static TCHAR User[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Mapping, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], User, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Status = (*pLlsMappingUserAdd) ( RPCHandle, Mapping, User );
+
+} // Do_LlsMappingUserAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingUserDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupUserDelete LicenseGroup User\n", ProgramName);
+} // ShowUsageLlsMappingUserDelete
+
+
+VOID Do_LlsMappingUserDelete() {
+ NTSTATUS Status;
+ WCHAR wUsName[MAX_WSTR_LEN];
+ WCHAR wGpName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Index++;
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingUserDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wUsName, MAX_WSTR_LEN);
+
+ Status = (*pLlsMappingUserDelete) ( RPCHandle, wGpName, wUsName);
+ printf("\nStatus = %ld\n",Status);
+
+} // Do_LlsMappingUserDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingAdd( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupAdd LicenseGroup licenses comment\n", ProgramName);
+} // ShowUsageLlsMappingAdd
+
+
+VOID Do_LlsMappingAdd() {
+ NTSTATUS Status;
+ DWORD Level = 1;
+ LONG Quantity;
+ LLS_GROUP_INFO_1 MappingInfo;
+ static TCHAR Name[MAX_CHAR_LEN];
+ static TCHAR Comment[MAX_CHAR_LEN];
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsLicenseAdd();
+ exit(0);
+ }
+
+ OemToCharBuff(Arguments[Index], Name, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ Quantity = (LONG) atoi(Arguments[Index]);
+ Index++;
+
+ OemToCharBuff(Arguments[Index], Comment, strlen(Arguments[Index]) + 1);
+ Index++;
+
+ MappingInfo.Name = Name;
+ MappingInfo.Comment = Comment;
+ MappingInfo.Licenses = Quantity;
+
+ Status = (*pLlsMappingAdd) ( RPCHandle, Level, (LPBYTE) &MappingInfo );
+
+} // Do_LlsMappingAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsMappingDelete( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsGroupDelete LicenseGroup\n", ProgramName);
+} // ShowUsageLlsMappingDelete
+
+
+VOID Do_LlsMappingDelete() {
+ NTSTATUS Status;
+ WCHAR wGpName[MAX_WSTR_LEN];
+
+ if (NumberArguments <= Index) {
+ ShowUsageLlsMappingDelete();
+ exit(0);
+ }
+
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, Arguments[Index], -1, wGpName, MAX_WSTR_LEN);
+
+ Status = (*pLlsMappingDelete) ( RPCHandle, wGpName);
+ printf("\nStatus = %ld\n",Status);
+
+} // Do_LlsMappingDelete
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsServiceInfoGet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsServiceInfoGet\n", ProgramName);
+} // ShowUsageLlsServiceInfoGet
+
+
+VOID Do_LlsServiceInfoGet() {
+
+// Status = (*pLlsServiceInfoGet) ( RPCHandle );
+
+} // Do_LlsServiceInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageLlsServiceInfoSet( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] LlsServiceInfoGet repl-mode time enterprise\n", ProgramName);
+ printf(" repl-mode: 0 = Replicate Every...(time)\n");
+ printf(" 1 = Replicate @... (time)\n");
+} // ShowUsageLlsServiceInfoSet
+
+
+VOID Do_LlsServiceInfoSet() {
+
+ //
+ // Check for level
+ //
+ if (NumberArguments <= Index) {
+ ShowUsageLlsServiceInfoSet();
+ exit(0);
+ }
+
+// Status = (*pLlsServiceInfoSet) ( RPCHandle );
+
+} // Do_LlsServiceInfoSet
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageRpc( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s RPC [\\\\server] [\n", ProgramName);
+ printf("\tLlsLicenseEnum | LlsLicenseAdd | LlsProductEnum |\n");
+ printf("\tLlsProductUserEnum | LlsProductLicenseEnum | LlsUserEnum |\n");
+ printf("\tLlsUserProductEnum | LlsUserInfoGet | LlsUserInfoSet |\n");
+ printf("\tLlsUserDelete | LlsGroupEnum | LlsGroupInfoGet |\n");
+ printf("\tLlsGroupInfoSet | LlsGroupUserEnum | LlsGroupUserAdd |\n");
+ printf("\tLlsGroupUserDelete | LlsGroupAdd | LlsGroupDelete |\n");
+ printf("\tLlsServiceInfoGet | LlsServiceInfoSet ]\n");
+
+} // ShowUsageRpc
+
+
+VOID DispatchRpc() {
+ NTSTATUS Status;
+ PLLS_CONNECT_INFO_0 pConnectInfo;
+
+ Index++;
+
+ if ((NumberArguments <= Index) || (strlen(Arguments[Index]) < 3) ) {
+ ShowUsageRpc();
+ exit(0);
+ }
+
+ //
+ // Check if a server has been specified (we already did length check
+ // above to prevent access violation.
+ //
+ if ((Arguments[Index][0] == '\\') && (Arguments[Index][1] == '\\')) {
+ LocalRPC = FALSE;
+ OemToCharBuff(Arguments[Index], Server, strlen(Arguments[Index]) + 1);
+ printf("RPC to Server: %s\n", Arguments[Index]);
+ Index++;
+ }
+
+ if (LocalRPC)
+ Status = (*pLlsConnectEnterprise) ( NULL, &RPCHandle, 0, (LPBYTE *) &pConnectInfo );
+ else
+ Status = (*pLlsConnectEnterprise) ( Server, &RPCHandle, 0, (LPBYTE *) &pConnectInfo );
+
+ if (Status != STATUS_SUCCESS) {
+ printf("Error: LlsConnectEnterprise failed: 0x%lX\n", Status);
+ exit(1);
+ }
+
+ if (RPCHandle == NULL) {
+ printf("Error: RPCHandle NULL\n");
+ exit(1);
+ }
+
+ //
+ // The API's (LlsConnectEnterprise, LlsClose, LlsFreeMemory) are used with all the
+ // other API's, so no provision is made for them.
+ //
+
+ Index++; // +1 to take care of actual RPC command
+ if (NumberArguments <= Index) {
+ ShowUsageRpc();
+ exit(0);
+ }
+
+ // LlsLicenseEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSLICENSEENUM"))
+ Do_LlsLicenseEnum();
+
+ // LlsLicenseAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSLICENSEADD"))
+ Do_LlsLicenseAdd();
+
+ // LlsProductEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTENUM"))
+ Do_LlsProductEnum();
+
+ // LlsProductUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTUSERENUM"))
+ Do_LlsProductUserEnum();
+
+ // LlsProductLicenseEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSPRODUCTLICENSEENUM"))
+ Do_LlsProductLicenseEnum();
+
+ // LlsUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERENUM"))
+ Do_LlsUserEnum();
+
+ // LlsUserProductEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERPRODUCTENUM"))
+ Do_LlsUserProductEnum();
+
+ // LlsUserInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERINFOGET"))
+ Do_LlsUserInfoGet();
+
+ // LlsUserInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERINFOSET"))
+ Do_LlsUserInfoSet();
+
+ // LlsUserDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSUSERDELETE"))
+ Do_LlsUserDelete();
+
+ // LlsMappingEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPENUM"))
+ Do_LlsMappingEnum();
+
+ // LlsMappingInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPINFOGET"))
+ Do_LlsMappingInfoGet();
+
+ // LlsMappingInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPINFOSET"))
+ Do_LlsMappingInfoSet();
+
+ // LlsMappingUserEnum
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERENUM"))
+ Do_LlsMappingUserEnum();
+
+ // LlsMappingUserAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERADD"))
+ Do_LlsMappingUserAdd();
+
+ // LlsMappingUserDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPUSERDELETE"))
+ Do_LlsMappingUserDelete();
+
+ // LlsMappingAdd
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPADD"))
+ Do_LlsMappingAdd();
+
+ // LlsMappingDelete
+ if (!_strcmpi(Arguments[Index - 1], "LLSGROUPDELETE"))
+ Do_LlsMappingDelete();
+
+ // LlsServiceInfoGet
+ if (!_strcmpi(Arguments[Index - 1], "LLSSERVICEINFOGET"))
+ Do_LlsServiceInfoGet();
+
+ // LlsServiceInfoSet
+ if (!_strcmpi(Arguments[Index - 1], "LLSSERVICEINFOSET"))
+ Do_LlsServiceInfoSet();
+
+ Status = (*pLlsClose) ( RPCHandle );
+
+} // DispatchRpc
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageTrace( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s TRACE level\n\n", ProgramName);
+ printf(" level can be a combination of:\n\n");
+
+ printf(" TRACE_FUNCTION_TRACE 0x0001 ( 1)\n");
+ printf(" TRACE_WARNINGS 0x0002 ( 2)\n");
+ printf(" TRACE_PACK 0x0004 ( 4)\n");
+ printf(" TRACE_LICENSE_REQUEST 0x0008 ( 8)\n");
+ printf(" TRACE_LICENSE_FREE 0x0010 ( 16)\n");
+ printf(" TRACE_REGISTRY 0x0020 ( 32)\n");
+ printf(" TRACE_REPLICATION 0x0040 ( 64)\n");
+ printf(" TRACE_LPC 0x0080 ( 128)\n");
+ printf(" TRACE_RPC 0x0100 ( 256)\n");
+ printf(" TRACE_INIT 0x0200 ( 512)\n");
+ printf(" TRACE_DATABASE 0x0400 (1024)\n");
+} // ShowUsageTrace
+
+
+VOID DispatchTrace() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageTrace();
+ exit(0);
+ }
+
+ LlsDbgTraceSet( atoi(Arguments[Index]) );
+} // DispatchTrace
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsageHelp( ) {
+ printf("The syntax of this command is:\n\n");
+ printf("%s HELP [ ADD | FILE | HELP | REPLICATE | RPC | TRACE ]\n", ProgramName);
+ exit(0);
+
+} // ShowUsageHelp
+
+
+VOID DispatchHelp() {
+ Index++;
+
+ if (NumberArguments <= Index) {
+ ShowUsageHelp();
+ exit(0);
+ }
+
+ if (!_strcmpi(Arguments[Index], "ADD")) {
+ ShowUsageAdd();
+ printf("\nGenerates license requests with usernames or SID's. The usernames\n");
+ printf("can be from the command line or a file.\n");
+ printf("\nUSER is for adding username\n");
+ printf("SID will convert the given username into a SID in the local domain\n");
+ printf(" if [NOFREE] is specified the license is not freed after the call\n");
+ printf(" if [ADMIN] is specified then the the user is considered an admim\n\n");
+ printf("FILE #lines will read from a file in format:\n");
+ printf("[NOFREE] [ADMIN] username [product version]\n");
+ printf(" quotes may be used to embed spaces in names and commas or\n");
+ printf(" spaces can be used to delimit the fields.\n");
+ printf("#lines gives the # of lines will be read, if 0 all the file is used\n");
+
+ } else if (!_strcmpi(Arguments[Index], "FILE")) {
+ ShowUsageFile();
+ printf("\nParses a file executing the commands in it - this works alot better\n");
+ printf("then putting llscmd's in a batch file since the DLL's and such don't\n");
+ printf("have to be continually re-loaded.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "HELP")) {
+ ShowUsageHelp();
+ printf("\nWhat do you think? Gives help on the commands\n");
+
+ } else if (!_strcmpi(Arguments[Index], "REPLICATE")) {
+ ShowUsageReplicate();
+ printf("\nTells the service to allow or disallow replication requests and\n");
+ printf("can be used to force the service to replicate it's information up.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "RPC")) {
+ ShowUsageRpc();
+ printf("\nAllows the issuing of RPC commands that the UI uses.\n");
+
+ } else if (!_strcmpi(Arguments[Index], "TRACE")) {
+ ShowUsageTrace();
+ printf("\nSets the level of debug output the service spits out to the debugger\n");
+ printf("Can be used to turn on API level tracing, or function entry/exit tracing\n");
+ printf("\n");
+ printf(" *** Note: LLSTRACE is much easier to use for setting tracing levels.\n");
+
+ } else
+ ShowUsageHelp();
+
+} // DispatchHelp
+
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ShowUsage( ) {
+ printf("\n");
+ printf("***************************************************************\n");
+ printf("* License Logging Service (LLS) Debugging Program - Version 0.6\n");
+ printf("* Copyright (c) Microsoft Corp 1995 - All rights reserved\n");
+ printf("\n");
+
+ printf("The syntax of this command is:\n\n");
+ printf("%s [ ADD | FILE | HELP | REPLICATE | RPC | TRACE ]\n", ProgramName);
+ exit(0);
+
+} // ShowUsage
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID ArgvToArguments(int argc, char **argv) {
+ LONG i;
+
+ NumberArguments = 0;
+ Index = 0;
+
+ if (argc > 10)
+ return;
+
+ //
+ // Ignore argv[0] (program name) - otherwise loop through and copy
+ // parms to our array, converting to Unicode along the way...
+ //
+ for (i = 1; i < argc; i++) {
+ strcpy(Arguments[i-1], argv[i]);
+ NumberArguments++;
+ }
+
+} // ArgvToArguments
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID _CRTAPI1 main(int argc, char **argv) {
+ int i;
+
+ ProgramName = _strupr(argv[0]);
+ ArgvToArguments(argc, argv);
+
+ //
+ // If no command line arguments then we can't do much
+ //
+ if (NumberArguments == 0)
+ ShowUsage();
+
+ //
+ // Don't LLSInitLPC for Add so it works like regular program and you
+ // don't have the LPC port initialized twice by the same process...
+ //
+ if (!_strcmpi(Arguments[Index], "ADD")) {
+ LlsDllHandle = LoadLibrary(TEXT("NtLSAPI.DLL"));
+ if (LlsDllHandle == NULL) {
+ printf("Error loading NtLSAPI.DLL\n");
+ exit(1);
+ }
+
+ LicenseRequest = GetProcAddress(LlsDllHandle, "NtLicenseRequestW");
+ LicenseFree = GetProcAddress(LlsDllHandle, "NtLSFreeHandle");
+
+ if ((LicenseRequest == NULL) || (LicenseFree == NULL)) {
+ printf("Error loading LicenseRequest or LicenseFree\n");
+ exit(1);
+ }
+
+ DispatchAdd(argc, argv);
+ FreeLibrary(LlsDllHandle);
+
+ } else {
+
+ LlsDebugInit( );
+
+ if (!_strcmpi(Arguments[Index], "FILE")) {
+ InitRpcFunctions();
+ DispatchFile();
+ } else if (!_strcmpi(Arguments[Index], "HELP"))
+ DispatchHelp();
+
+ else if (!_strcmpi(Arguments[Index], "REPLICATE"))
+ DispatchReplicate();
+
+ else if (!_strcmpi(Arguments[Index], "RPC")) {
+ InitRpcFunctions();
+ DispatchRpc();
+ } else if (!_strcmpi(Arguments[Index], "TRACE"))
+ DispatchTrace();
+ else if (!_strcmpi(Arguments[Index], "FLUSH"))
+ DispatchFlush();
+ else
+ ShowUsage();
+ }
+
+} // main()
diff --git a/private/net/svcdlls/lls/test/llscmd/llscmd.rc b/private/net/svcdlls/lls/test/llscmd/llscmd.rc
new file mode 100644
index 000000000..81787e030
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/llscmd.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server Cmd Test Utility"
+
+#define VER_INTERNALNAME_STR "llscmd.exe"
+#define VER_ORIGINALFILENAME_STR "llscmd.exe"
+
+#include <common.ver>
+
diff --git a/private/net/svcdlls/lls/test/llscmd/makefile b/private/net/svcdlls/lls/test/llscmd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/llscmd/sources b/private/net/svcdlls/lls/test/llscmd/sources
new file mode 100644
index 000000000..44d5bc90a
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llscmd/sources
@@ -0,0 +1,72 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=lls
+MINORCOMP=server
+
+TARGETNAME=llscmd
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+INCLUDES=$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc;..\..\inc
+
+
+
+SOURCES=llscmd.c llscmd.rc
+
+UMTYPE=console
+UMLIBS= \
+ ..\..\common\obj\*\llscomm.lib \
+ ..\common\obj\*\llsdbg.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntlsapi.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\crtdll.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+
+TARGETLIBS= \
+ ..\..\common\obj\*\llscomm.lib \
+ ..\common\obj\*\llsdbg.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samsrv.lib \
+ $(BASEDIR)\public\sdk\lib\*\nlrepl.lib \
+ $(BASEDIR)\public\sdk\lib\*\crtdll.lib \
+ $(BASEDIR)\Public\sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\user32.lib
+
+C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE
+USE_CRTDLL=1
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.c b/private/net/svcdlls/lls/test/llsdbg/llsdbg.c
new file mode 100644
index 000000000..7750f03f4
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.c
@@ -0,0 +1,448 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ LlsDbg.c
+
+Abstract:
+
+Author:
+
+ Arthur Hanson (arth) Dec 07, 1994
+
+Environment:
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include "resource.h"
+#include "..\..\inc\debug.h"
+#include "..\common\llsdbg.h"
+
+
+HINSTANCE hInst; // current instance
+
+TCHAR szAppName[] = TEXT("LlsDbg"); // The name of this application
+TCHAR ProgPath[MAX_PATH + 1];
+
+HICON MyIcon;
+HWND hDlgMain;
+
+
+DWORD DebugFlags = 0;
+LRESULT CALLBACK DlgMain( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
+
+#define ITEM_DATA_SIZE 60
+TCHAR ItemData[ITEM_DATA_SIZE + 1];
+
+/////////////////////////////////////////////////////////////////////////
+int APIENTRY
+WinMain(
+ HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPTSTR ptr;
+ DLGPROC lpproc;
+ HACCEL haccel;
+ MSG msg;
+
+ hInst = hInstance;
+
+ MyIcon = LoadIcon(hInst, szAppName);
+
+ if (!hPrevInstance) {
+ }
+
+ lpproc = MakeProcInstance((DLGPROC) DlgMain, hInst);
+ hDlgMain = CreateDialog(hInst, szAppName, NULL, lpproc);
+
+ ShowWindow(hDlgMain, nCmdShow);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if ((hDlgMain == 0) || !IsDialogMessage(hDlgMain, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ FreeProcInstance(lpproc);
+
+ DestroyIcon(MyIcon);
+
+ return msg.wParam;
+
+} // WinMain
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgTrace(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int wmId, wmEvent;
+ PAINTSTRUCT ps;
+ HDC hDC;
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ break;
+
+ case WM_ERASEBKGND:
+
+ // Process so icon background isn't painted grey by CTL3D - main dlg
+ // can't be DS_MODALFRAME either, or else a frame is painted around
+ // the icon.
+ if (IsIconic(hDlg))
+ return TRUE;
+
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case ID_INIT:
+
+ case ID_UPDATE:
+ if (DebugFlags & TRACE_FUNCTION_TRACE)
+ SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_WARNINGS)
+ SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_PACK)
+ SendDlgItemMessage(hDlg, IDC_PACK, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_PACK, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_LICENSE_REQUEST)
+ SendDlgItemMessage(hDlg, IDC_REQUEST, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REQUEST, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_LICENSE_FREE)
+ SendDlgItemMessage(hDlg, IDC_FREE, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_FREE, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_REGISTRY)
+ SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_REPLICATION)
+ SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_RPC)
+ SendDlgItemMessage(hDlg, IDC_RPC, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_RPC, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_INIT)
+ SendDlgItemMessage(hDlg, IDC_INIT, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_INIT, BM_SETCHECK, 0, 0);
+
+ if (DebugFlags & TRACE_DATABASE)
+ SendDlgItemMessage(hDlg, IDC_DATABASE, BM_SETCHECK, 1, 0);
+ else
+ SendDlgItemMessage(hDlg, IDC_DATABASE, BM_SETCHECK, 0, 0);
+
+ break;
+
+ case IDC_SET:
+ DebugFlags = 0;
+
+ if (SendDlgItemMessage(hDlg, IDC_FUNCTION, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_FUNCTION_TRACE;
+
+ if (SendDlgItemMessage(hDlg, IDC_WARNINGS, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_WARNINGS;
+
+ if (SendDlgItemMessage(hDlg, IDC_PACK, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_PACK;
+
+ if (SendDlgItemMessage(hDlg, IDC_REQUEST, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_LICENSE_REQUEST;
+
+ if (SendDlgItemMessage(hDlg, IDC_FREE, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_LICENSE_FREE;
+
+ if (SendDlgItemMessage(hDlg, IDC_REGISTRY, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_REGISTRY;
+
+ if (SendDlgItemMessage(hDlg, IDC_REPLICATION, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_REPLICATION;
+
+ if (SendDlgItemMessage(hDlg, IDC_RPC, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_RPC;
+
+ if (SendDlgItemMessage(hDlg, IDC_INIT, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_INIT;
+
+ if (SendDlgItemMessage(hDlg, IDC_DATABASE, BM_GETCHECK, 0, 0) == 1)
+ DebugFlags |= TRACE_DATABASE;
+
+ LlsDbgTraceSet( DebugFlags );
+ break;
+
+ case IDC_RESET:
+ DebugFlags = 0;
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATE, 0L);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ return (FALSE); // Didn't process the message
+
+} // DlgTrace
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DlgTrace_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgTrace, hInst);
+ DialogBox(hInst, TEXT("LlsTrace"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // DlgTrace_Do
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgMain(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HWND hCtrl;
+ int wmId, wmEvent;
+ DWORD dwData, dwIndex;
+ PAINTSTRUCT ps;
+ HDC hDC;
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ case WM_PAINT:
+ hDC = BeginPaint(hDlg, &ps);
+ if (IsIconic(hDlg)) {
+ GetClientRect(hDlg, &rc);
+ DrawIcon(hDC, rc.left, rc.top, MyIcon);
+ }
+
+ EndPaint(hDlg, &ps);
+ break;
+
+ case WM_ERASEBKGND:
+
+ // Process so icon background isn't painted grey by CTL3D - main dlg
+ // can't be DS_MODALFRAME either, or else a frame is painted around
+ // the icon.
+ if (IsIconic(hDlg))
+ return TRUE;
+
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case ID_INIT:
+ LlsDebugInit( );
+
+ //
+ // Limit edit box to some reasonable value
+ //
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) ITEM_DATA_SIZE, 0);
+
+ //
+ // Fill up the combo box with our tables
+ //
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Service"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVICE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("User"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) USER_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("SID"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SID_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("License"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) LICENSE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Add Cache"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) ADD_CACHE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Master Service"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) MASTER_SERVICE_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Service Family"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVICE_FAMILY_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("License Group"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) MAPPING_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Server"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SERVER_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Secure Products"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) SECURE_PRODUCT_TABLE_NUM);
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TEXT("Certificate Database"));
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) CERTIFICATE_TABLE_NUM);
+
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) TEXT("Add Cache"));
+ break;
+
+ case IDC_DUMPALL:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ LlsDbgTableDump( dwData );
+ }
+
+ break;
+
+ case IDC_DUMPITEM:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ * (WORD *)ItemData = sizeof(ItemData);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) ItemData);
+
+ LlsDbgTableInfoDump( dwData, ItemData );
+ }
+
+ break;
+
+ case IDC_TRACE:
+ DlgTrace_Do(hDlg);
+ break;
+
+ case IDC_CONFIG:
+ LlsDbgConfigDump();
+ break;
+
+ case IDC_FORCEREPLICATE:
+ LlsDbgReplicationForce();
+ break;
+
+ case IDCANCEL:
+ PostMessage(hDlg, WM_DESTROY, 0, 0);
+ LlsClose();
+ break;
+
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+
+ return (FALSE); // Didn't process the message
+
+} // DlgMain
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico b/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico
new file mode 100644
index 000000000..162d88db8
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.ico
Binary files differ
diff --git a/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc b/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc
new file mode 100644
index 000000000..e78ee293b
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/llsdbg.rc
@@ -0,0 +1,89 @@
+
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Microsoft\256 LLS Debug Utility"
+
+#define VER_INTERNALNAME_STR "llsdbg.exe"
+#define VER_ORIGINALFILENAME_STR "llsdbg.exe"
+
+#include <common.ver>
+
+#include "resource.h"
+#include "..\..\inc\debug.h"
+
+LlsDbg ICON llsdbg.ico
+
+LlsDbg DIALOG DISCARDABLE 15, 40, 200, 150
+STYLE WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_POPUP
+CAPTION "LLS Debug Utility"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Table:",IDC_TABLE, 10,10,40,8, WS_VISIBLE
+ COMBOBOX IDC_COMBO1, 50,10,85,60,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP | WS_VISIBLE
+
+ LTEXT "Item:",IDC_ITEM, 10,30,40,8, WS_VISIBLE
+ EDITTEXT IDC_EDIT1, 50,30,85,12, ES_AUTOHSCROLL | WS_VISIBLE
+
+ PUSHBUTTON "Dump &All", IDC_DUMPALL, 140,10,40,14, WS_VISIBLE
+ PUSHBUTTON "Dump &Item", IDC_DUMPITEM, 140,30,40,14, WS_VISIBLE
+
+
+ PUSHBUTTON "Set &Trace", IDC_TRACE, 70,50,60,14
+ PUSHBUTTON "Dump &Config", IDC_CONFIG, 70,70,60,14
+ PUSHBUTTON "&Replicate", IDC_FORCEREPLICATE, 70,90,60,14
+ PUSHBUTTON "E&xit", IDCANCEL, 70,110,60,14
+
+
+ LTEXT "You must use a debugger to receive this output",IDC_STATIC
+ 10, 135, 170, 18
+
+}
+
+
+LlsTrace DIALOG DISCARDABLE 15, 40, 170, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "LLS Set Trace"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "&Set Trace", IDC_SET, 120,6,40,14
+ PUSHBUTTON "&Reset All", IDC_RESET, 120,26,40,14
+ PUSHBUTTON "&Cancel", IDCANCEL, 120,46,40,14
+
+ CONTROL "Warnings", IDC_WARNINGS,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 10, 100, 8
+ CONTROL "Function Trace", IDC_FUNCTION,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 20, 100, 8
+ CONTROL "Data Pack Functions", IDC_PACK,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 30, 100, 8
+ CONTROL "License Request", IDC_REQUEST,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 40, 100, 8
+ CONTROL "License Free", IDC_FREE,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 50, 100, 8
+ CONTROL "Registry Functions", IDC_REGISTRY,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 60, 100, 8
+ CONTROL "Replication", IDC_REPLICATION,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 70, 100, 8
+ CONTROL "RPC", IDC_RPC,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 80, 100, 8
+ CONTROL "Init", IDC_INIT,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 90, 100, 8
+ CONTROL "Database", IDC_DATABASE,
+ "Button", BS_AUTOCHECKBOX | WS_TABSTOP | WS_VISIBLE,
+ 10, 100, 100, 8
+
+ LTEXT "You must use a debugger to receive this output",IDC_STATIC
+ 10, 115, 130, 18
+}
diff --git a/private/net/svcdlls/lls/test/llsdbg/makefile b/private/net/svcdlls/lls/test/llsdbg/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/net/svcdlls/lls/test/llsdbg/resource.h b/private/net/svcdlls/lls/test/llsdbg/resource.h
new file mode 100644
index 000000000..a814f452c
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/resource.h
@@ -0,0 +1,55 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HRESOURCE_
+#define _HRESOURCE_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// +------------------------------------------------------------------------+
+// | Main MoveIt Dialog
+// +------------------------------------------------------------------------+
+
+#define IDC_STATIC -1
+#define IDR_MAINFRAME 1
+#define IDD_ABOUTBOX 999
+
+#define ID_INIT 1000
+
+#define IDC_SET 1001
+#define IDC_RESET 1002
+
+#define IDC_TABCTRL 1003
+#define IDC_COMBO1 1004
+#define IDC_EDIT1 1005
+
+#define IDC_WARNINGS 1010
+#define IDC_FUNCTION 1011
+#define IDC_PACK 1012
+#define IDC_REQUEST 1013
+#define IDC_FREE 1014
+#define IDC_REGISTRY 1015
+#define IDC_REPLICATION 1016
+#define IDC_RPC 1017
+#define IDC_INIT 1018
+#define IDC_DATABASE 1019
+
+#define IDC_TABLE 1050
+#define IDC_DUMPALL 1051
+#define IDC_DUMPITEM 1052
+#define IDC_ITEM 1053
+
+#define IDC_FORCEREPLICATE 1100
+#define IDC_TRACE 1101
+#define IDC_CONFIG 1102
+
+#define ID_UPDATE 1500
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/net/svcdlls/lls/test/llsdbg/sources b/private/net/svcdlls/lls/test/llsdbg/sources
new file mode 100644
index 000000000..5bf898aa5
--- /dev/null
+++ b/private/net/svcdlls/lls/test/llsdbg/sources
@@ -0,0 +1,23 @@
+TARGETNAME=llsdbg
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+USE_CRTDLL=1
+
+INCLUDES=$(_NTROOT)\private\inc;
+C_DEFINES= -DUNICODE -D_UNICODE
+
+SOURCES= \
+ llsdbg.c \
+ llsdbg.rc
+
+UMTYPE=windows
+UMENTRY=winmain
+UMLIBS= \
+ ..\common\obj\*\llsdbg.lib \
+ ..\..\common\obj\*\llscomm.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \
+ $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\mpr.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib