452 lines
14 KiB
C++
452 lines
14 KiB
C++
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1999
|
|
//
|
|
// File: main.cpp
|
|
//
|
|
// Contents: Front end to license "needer"; lists TS licenses
|
|
//
|
|
// History: 06-05-99 t-BStern Created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
// The goal here is to export
|
|
// all [temporary] licenses [issued between <shortdate1> and <shortdate2>]
|
|
// to a file [named <outFile>] [only from server[s] <S>]
|
|
//
|
|
// So possible command lines are:
|
|
// lsls | lsls /T /D 1/1/99 2/2/00 /F outfile ls-server ls2 ls3
|
|
|
|
#include "lsreport.h"
|
|
#include "lsrepdef.h"
|
|
#include <oleauto.h>
|
|
|
|
|
|
// I found that ErrorPrintf didn't do %1-style formatting, so this does
|
|
// complete formatting and stderr output. It is referenced by code in
|
|
// lsreport.cpp, so if the program becomes GUI-based, ShowError needs to
|
|
// maintain a presence.
|
|
|
|
DWORD
|
|
ShowError(
|
|
IN DWORD dwStatus, // This is both the return value and the resource ID.
|
|
IN INT_PTR *args, // Casted to va_list* and passed to FormatMessage.
|
|
IN BOOL fSysError // Use the system message table (T) or the module (F)?
|
|
) {
|
|
LPTSTR lpSysError;
|
|
TCHAR szBuffer[TLS_ERROR_LENGTH];
|
|
DWORD dwFlag = FORMAT_MESSAGE_FROM_SYSTEM;
|
|
DWORD dwRet;
|
|
|
|
if ((dwStatus == ERROR_FILE_NOT_FOUND) || !fSysError)
|
|
{
|
|
// We need to special-case File-not-Found because the system error
|
|
// is insufficiently educational. FnF really means that the server
|
|
// is not running TS.
|
|
int retVal;
|
|
retVal = LoadString(NULL, dwStatus, szBuffer, TLS_ERROR_LENGTH);
|
|
if (!retVal)
|
|
{
|
|
// This is a more serious error.
|
|
|
|
dwStatus = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
dwFlag = FORMAT_MESSAGE_FROM_STRING;
|
|
}
|
|
}
|
|
dwRet = FormatMessage(dwFlag |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY, // Tell FM to use INT_PTRs internally.
|
|
szBuffer,
|
|
dwStatus,
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR)&lpSysError,
|
|
0,
|
|
(va_list *)args); // FormatMessage requires va_lists, even
|
|
// if the arguments are flagged as being INT_PTRs.
|
|
|
|
if ((dwRet != 0) && (lpSysError != NULL))
|
|
{
|
|
_fputts(lpSysError, stderr);
|
|
LocalFree(lpSysError);
|
|
}
|
|
|
|
return dwStatus;
|
|
}
|
|
|
|
|
|
// Used only to enumerate the License Servers around, and thereby build a list
|
|
// Used in EnumerateTlsServer()
|
|
|
|
BOOL
|
|
ServerEnumCallBack(
|
|
IN TLS_HANDLE hHandle,
|
|
IN LPCWSTR pszServerName,
|
|
IN HANDLE dwUserData // Really a PServerHolder
|
|
) {
|
|
LPWSTR *block;
|
|
PServerHolder pSrvHold; // The casts get so hairy that I'll do this.
|
|
pSrvHold = (PServerHolder)dwUserData;
|
|
if ((hHandle != NULL) && (pSrvHold != NULL))
|
|
{
|
|
if (pSrvHold->pszNames == NULL)
|
|
{
|
|
// We need to allocate the names list for the first time.
|
|
|
|
block = (LPWSTR *)LocalAlloc(0, sizeof (LPWSTR));
|
|
} else {
|
|
// The names list needs to get bigger.
|
|
|
|
block = (LPWSTR *)LocalReAlloc(pSrvHold->pszNames,
|
|
(1+pSrvHold->dwCount) * sizeof (LPWSTR),
|
|
0);
|
|
}
|
|
if (block != NULL)
|
|
{
|
|
// We can add a name to the list.
|
|
|
|
pSrvHold->pszNames = block;
|
|
pSrvHold->pszNames[pSrvHold->dwCount] = _tcsdup(pszServerName);
|
|
pSrvHold->dwCount++;
|
|
}
|
|
// It's okay if we have to stick with the names we already have.
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
SortDates(
|
|
PSYSTEMTIME pstStart,
|
|
PSYSTEMTIME pstEnd)
|
|
{
|
|
BOOL fSwapped = FALSE;
|
|
FILETIME ftStart, ftEnd;
|
|
SYSTEMTIME stHolder;
|
|
SystemTimeToFileTime(pstStart, &ftStart);
|
|
SystemTimeToFileTime(pstEnd, &ftEnd);
|
|
|
|
if ((ftStart.dwHighDateTime > ftEnd.dwHighDateTime) ||
|
|
((ftStart.dwHighDateTime == ftEnd.dwHighDateTime) &&
|
|
(ftStart.dwLowDateTime > ftEnd.dwLowDateTime)))
|
|
{
|
|
// We need to swap.
|
|
|
|
stHolder.wYear = pstStart->wYear;
|
|
stHolder.wMonth = pstStart->wMonth;
|
|
stHolder.wDay = pstStart->wDay;
|
|
|
|
pstStart->wYear = pstEnd->wYear;
|
|
pstStart->wMonth = pstEnd->wMonth;
|
|
pstStart->wDay = pstEnd->wDay;
|
|
|
|
pstEnd->wYear = stHolder.wYear;
|
|
pstEnd->wMonth = stHolder.wMonth;
|
|
pstEnd->wDay = stHolder.wDay;
|
|
|
|
fSwapped = TRUE;
|
|
}
|
|
return fSwapped;
|
|
}
|
|
|
|
|
|
// All that wmain needs to do is parse the command line and
|
|
// therefore collect a list of machines to connect to, along with
|
|
// any options passed, and open & close the (possibly command-line
|
|
// specified) output file.
|
|
|
|
extern "C" int _cdecl
|
|
wmain(
|
|
int argc,
|
|
WCHAR *argv[],
|
|
WCHAR // *envp[]
|
|
) {
|
|
// These represent which command-line options were chosen.
|
|
BOOL fTempOnly = FALSE;
|
|
BOOL fError = FALSE;
|
|
BOOL fDateSpec = FALSE;
|
|
|
|
DWORD dwStatus;
|
|
DWORD dwSrvLoc; // This is a bitfield.
|
|
ServerHolder srvHold; // This holds all the servers.
|
|
|
|
// These are for parsing the command-line-specified date(s).
|
|
DATE startDate;
|
|
DATE endDate;
|
|
UDATE usDate;
|
|
UDATE ueDate;
|
|
|
|
// Basic file I/O.
|
|
TCHAR ofName[MAX_PATH+1] = { 0 };
|
|
FILE *outFile;
|
|
|
|
int i;
|
|
INT_PTR arg; // All of my strings have at most 1 parm.
|
|
|
|
dwSrvLoc = 0;
|
|
srvHold.dwCount = 0;
|
|
srvHold.pszNames = NULL;
|
|
if (!(LoadString(NULL, IDS_DEFAULT_FILE, ofName, MAX_PATH) &&
|
|
InitLSReportStrings()))
|
|
{
|
|
return ShowError(GetLastError(), NULL, TRUE);
|
|
}
|
|
for (i = 1; (i < argc) && !fError; i++) {
|
|
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
|
|
{
|
|
switch (argv[i][1]) {
|
|
case 'F': case 'f': // format: /F [path\]filename
|
|
if (i+1 == argc)
|
|
{
|
|
// They didn't include enough parameters.
|
|
|
|
fError = TRUE;
|
|
} else {
|
|
i++;
|
|
_tcsncpy(ofName, argv[i], MAX_PATH);
|
|
}
|
|
break;
|
|
case 'D': case 'd': // format: /D startdate [enddate]
|
|
|
|
// Do a lot of date manipulation.
|
|
|
|
if (i+1 == argc)
|
|
{
|
|
fError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
dwStatus = VarDateFromStr(argv[i],
|
|
LOCALE_USER_DEFAULT,
|
|
VAR_DATEVALUEONLY,
|
|
&startDate);
|
|
if (dwStatus != S_OK)
|
|
{
|
|
// The date couldn't get converted.
|
|
|
|
ShowError(dwStatus, NULL, TRUE);
|
|
fError = TRUE;
|
|
break;
|
|
}
|
|
if (VarUdateFromDate(startDate, 0, &usDate) != S_OK)
|
|
{
|
|
// We don't want to set error, because the user can't
|
|
// fix this with command line syntax. We're out of
|
|
// memory or something. ABEND.
|
|
|
|
return ShowError(GetLastError(), NULL, TRUE);
|
|
}
|
|
i++;
|
|
if (i < argc)
|
|
{
|
|
dwStatus = VarDateFromStr(argv[i],
|
|
LOCALE_USER_DEFAULT,
|
|
VAR_DATEVALUEONLY,
|
|
&endDate);
|
|
if (dwStatus != S_OK)
|
|
{
|
|
ShowError(dwStatus, NULL, TRUE);
|
|
fError = TRUE;
|
|
break;
|
|
}
|
|
if (VarUdateFromDate(endDate, 0, &ueDate) != S_OK)
|
|
{
|
|
return ShowError(GetLastError(), NULL, TRUE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have to use today's date, because they didn't
|
|
// give us an EndDate.
|
|
|
|
GetSystemTime(&ueDate.st); // Fill in the SystemTime.
|
|
}
|
|
|
|
// Check if the dates are in the right order.
|
|
// If the user gives us only /D 1/1/2022 and it is 1999, I
|
|
// choose not to have a fit and die.
|
|
|
|
SortDates(&usDate.st, &ueDate.st);
|
|
fDateSpec = TRUE;
|
|
}
|
|
break;
|
|
case 'T': case 't': // Format: /T
|
|
fTempOnly = TRUE;
|
|
break;
|
|
// case '?': case 'H': case 'h': // Format: /?
|
|
default: // Let the default get this, since it'll work the same.
|
|
|
|
// This'll show syntax help.
|
|
|
|
fError = TRUE;
|
|
break;
|
|
} // switch
|
|
}
|
|
else
|
|
{
|
|
// It wasn't -T or /F or something.
|
|
// It must be a server name, since it's not anything else.
|
|
|
|
dwSrvLoc |= (1 << i); // Tag this is as a server name.
|
|
srvHold.dwCount++;
|
|
}
|
|
} // argc loop
|
|
|
|
if (fError)
|
|
{
|
|
ShowError(IDS_HELP_USAGE1, NULL, FALSE);
|
|
// Set the exe name:
|
|
arg = (INT_PTR)argv[0];
|
|
ShowError(IDS_HELP_USAGE2, &arg, FALSE);
|
|
ShowError(IDS_HELP_USAGE3, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE4, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE5, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE6, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE7, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE8, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE9, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE10, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE11, NULL, FALSE);
|
|
ShowError(IDS_HELP_USAGE12, &arg, FALSE);
|
|
ShowError(IDS_HELP_USAGE13, &arg, FALSE);
|
|
|
|
return ERROR_BAD_SYNTAX;
|
|
}
|
|
outFile = _tfopen(ofName, _T("w"));
|
|
if (outFile == NULL)
|
|
{
|
|
// This is an extra level of indirection for FormatMessage.
|
|
arg = (INT_PTR)&ofName;
|
|
ShowError(IDS_NO_FOPEN, &arg, FALSE);
|
|
return ShowError(GetLastError(), NULL, TRUE);
|
|
}
|
|
|
|
TLSInit();
|
|
|
|
if (dwSrvLoc)
|
|
{
|
|
int holder;
|
|
|
|
srvHold.pszNames = (LPWSTR *)LocalAlloc(0,
|
|
srvHold.dwCount * sizeof (LPWSTR *));
|
|
if (srvHold.pszNames == NULL)
|
|
{
|
|
dwStatus = ShowError(GetLastError(), NULL, TRUE);
|
|
goto done;
|
|
}
|
|
|
|
holder = 0;
|
|
for (i = 1; i < argc; i++) { // argc (less one) == max # of servers.
|
|
if (dwSrvLoc & (1 << i)) {
|
|
srvHold.pszNames[holder] = _tcsdup(argv[i]);
|
|
holder++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We need to collect a list of servers.
|
|
LPTSTR *pszEntSrvNames;
|
|
DWORD dwEntSrvNum;
|
|
HRESULT hrEntResult;
|
|
|
|
dwStatus = EnumerateTlsServer(
|
|
ServerEnumCallBack,
|
|
&srvHold,
|
|
3000, // seems a good timeout
|
|
FALSE);
|
|
|
|
hrEntResult = GetAllEnterpriseServers(&pszEntSrvNames, &dwEntSrvNum);
|
|
if (SUCCEEDED(hrEntResult))
|
|
{
|
|
DWORD j, k;
|
|
BOOL fFound;
|
|
|
|
for (k = 0; k < dwEntSrvNum; k++) {
|
|
fFound = FALSE;
|
|
for (j = 0; j < srvHold.dwCount; j++) {
|
|
if (!_tcscmp(srvHold.pszNames[j], pszEntSrvNames[k]))
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!fFound)
|
|
{
|
|
// This is a new name.
|
|
|
|
LPTSTR *block;
|
|
if (srvHold.pszNames == NULL)
|
|
{
|
|
// We have to allocate names for the first time.
|
|
|
|
block = (LPTSTR *)LocalAlloc(0, sizeof (LPTSTR));
|
|
}
|
|
else
|
|
{
|
|
// Try to increase the array.
|
|
|
|
block = (LPTSTR *)LocalReAlloc(srvHold.pszNames,
|
|
(1+srvHold.dwCount) * sizeof (LPTSTR),
|
|
0);
|
|
|
|
}
|
|
|
|
if (block != NULL)
|
|
{
|
|
// We can add a name to the list.
|
|
|
|
srvHold.pszNames = block;
|
|
srvHold.pszNames[srvHold.dwCount] = pszEntSrvNames[k];
|
|
srvHold.dwCount++;
|
|
}
|
|
else
|
|
{
|
|
// If we can't copy it into our array, we should drop it.
|
|
|
|
LocalFree(pszEntSrvNames[k]);
|
|
}
|
|
|
|
// End need to add name
|
|
}
|
|
|
|
// End loop through existing servers
|
|
}
|
|
|
|
// We've removed all of the names from this one way or the other.
|
|
LocalFree(pszEntSrvNames);
|
|
|
|
// End <GetEntSrv worked>
|
|
}
|
|
|
|
// Autodiscovery complete.
|
|
}
|
|
if (srvHold.dwCount)
|
|
{
|
|
dwStatus = ExportLicenses(
|
|
outFile,
|
|
&srvHold,
|
|
fTempOnly,
|
|
&usDate.st,
|
|
&ueDate.st,
|
|
fDateSpec);
|
|
do {
|
|
free(srvHold.pszNames[--srvHold.dwCount]);
|
|
} while (srvHold.dwCount);
|
|
LocalFree(srvHold.pszNames);
|
|
}
|
|
else
|
|
{
|
|
arg = (INT_PTR)argv[0];
|
|
ShowError(IDS_NO_SERVERS, &arg, FALSE);
|
|
dwStatus = ERROR_NO_SERVERS;
|
|
}
|
|
|
|
done:
|
|
fclose(outFile);
|
|
return dwStatus;
|
|
}
|