[LOCALSPL]

Implement all corner cases of handling the Name parameter in EnumPrinters. This can be done in a common way for all levels.
Fixes more tests.

svn path=/trunk/; revision=74393
This commit is contained in:
Colin Finck 2017-04-23 15:00:26 +00:00
parent 4414132a97
commit ceaed9f1ec

View file

@ -297,29 +297,61 @@ Cleanup:
} }
/** /**
* @name _IsLocalComputerName * @name _LocalEnumPrintersCheckName
* *
* Checks if the given Computer Name matches the local Computer Name. * Checks the Name parameter supplied to a call to EnumPrinters.
*
* @param Flags
* Flags parameter of EnumPrinters.
* *
* @param Name * @param Name
* Computer Name prepended with two backslashes to check. * Name parameter of EnumPrinters to check.
* *
* @param pwszComputerName * @param pwszComputerName
* Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters. * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
* Will contain a string "\\COMPUTERNAME\" on success that can be prepended in EnumPrinters. * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
* *
* @param pcchComputerName * @param pcchComputerName
* On success, this pointer receives the length in characters of pwszComputerName. * If a string to prepend is returned, this pointer receives its length in characters.
* *
* @return * @return
* ERROR_SUCCESS on success or an error code on failure. * ERROR_SUCCESS if processing in EnumPrinters can be continued.
* ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
* Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
*/ */
static DWORD static DWORD
_IsLocalComputerName(PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName) _LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
{ {
DWORD dwErrorCode; PCWSTR pName;
PCWSTR pComputerName;
// Prepend slashes to the computer name. // If there is no Name parameter to check, we can just continue in EnumPrinters.
if (!Name)
return ERROR_SUCCESS;
// Check if Name does not begin with two backslashes (required for specifying Computer Names).
if (Name[0] != L'\\' || Name[1] != L'\\')
{
if (Flags & PRINTER_ENUM_NAME)
{
// If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
// Print Provider Name or the local Computer Name.
// Compare with the Print Provider Name.
if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
return ERROR_SUCCESS;
// Dismiss anything else.
return ERROR_INVALID_NAME;
}
else
{
// If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
return ERROR_SUCCESS;
}
}
// Prepend the backslashes to the output computer name.
pwszComputerName[0] = L'\\'; pwszComputerName[0] = L'\\';
pwszComputerName[1] = L'\\'; pwszComputerName[1] = L'\\';
@ -327,29 +359,59 @@ _IsLocalComputerName(PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerNam
*pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1; *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName)) if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
{ {
dwErrorCode = GetLastError(); ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode); return GetLastError();
goto Cleanup;
} }
// Add the leading slashes to the total length. // Add the leading slashes to the total length.
*pcchComputerName += 2; *pcchComputerName += 2;
// Now compare this with the local computer name and reject it with ERROR_INVALID_NAME if it doesn't match. // Compare both names.
if (wcsicmp(&Name[2], &pwszComputerName[2]) != 0) pComputerName = &pwszComputerName[2];
pName = &Name[2];
for (;;)
{ {
dwErrorCode = ERROR_INVALID_NAME; // Are we at the end of the local Computer Name string?
goto Cleanup; if (!*pComputerName)
{
// Are we also at the end of the supplied Name parameter?
// A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
if (!*pName)
{
// If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
// Add a trailing backslash for that.
pwszComputerName[(*pcchComputerName)++] = L'\\';
pwszComputerName[*pcchComputerName] = 0;
return ERROR_SUCCESS;
}
else if (*pName == L'\\')
{
if (Flags & PRINTER_ENUM_NAME)
{
// If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
// Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
return ERROR_INVALID_NAME;
}
else
{
// If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
// Only the Computer Name between the backslashes is checked then.
// This is largely undocumented, but verified by tests (see winspool_apitest).
// In this case, no computer name is prepended in EnumPrinters though.
*pwszComputerName = 0;
*pcchComputerName = 0;
return ERROR_SUCCESS;
}
}
}
// Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
if (towlower(*pName) != towlower(*pComputerName))
return ERROR_INVALID_NAME;
pName++;
pComputerName++;
} }
// Add a trailing backslash to pwszComputerName, which will later be prepended in front of the printer names.
pwszComputerName[(*pcchComputerName)++] = L'\\';
pwszComputerName[*pcchComputerName] = 0;
dwErrorCode = ERROR_SUCCESS;
Cleanup:
return dwErrorCode;
} }
static DWORD static DWORD
@ -376,20 +438,19 @@ _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbN
} }
static DWORD static DWORD
_LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) _LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
{ {
return ERROR_INVALID_LEVEL; return ERROR_INVALID_LEVEL;
} }
static DWORD static DWORD
_LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) _LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
{ {
const WCHAR wszComma[] = L","; const WCHAR wszComma[] = L",";
size_t cbName; size_t cbName;
size_t cbComment; size_t cbComment;
size_t cbDescription; size_t cbDescription;
DWORD cchComputerName = 0;
DWORD dwErrorCode; DWORD dwErrorCode;
DWORD i; DWORD i;
PBYTE pPrinterInfo; PBYTE pPrinterInfo;
@ -398,34 +459,13 @@ _LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbB
PLOCAL_PRINTER pPrinter; PLOCAL_PRINTER pPrinter;
PWSTR p; PWSTR p;
PWSTR pwszStrings[3]; PWSTR pwszStrings[3];
WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
if (Flags & PRINTER_ENUM_NAME) if (Flags & PRINTER_ENUM_NAME && !Name)
{ {
if (Name) // The caller wants information about this Print Provider.
{ // spoolss packs this into an array of information about all Print Providers.
// The user supplied a Computer Name (with leading double backslashes) or Print Provider Name. dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
// Only process what's directed at us. goto Cleanup;
if (Name[0] == L'\\' && Name[1] == L'\\')
{
dwErrorCode = _IsLocalComputerName(Name, wszComputerName, &cchComputerName);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
}
else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
{
// The user supplied a name that cannot be processed by the Local Print Provider.
dwErrorCode = ERROR_INVALID_NAME;
goto Cleanup;
}
}
else
{
// The caller wants information about this Print Provider.
// spoolss packs this into an array of information about all Print Providers.
dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
goto Cleanup;
}
} }
// Count the required buffer size and the number of printers. // Count the required buffer size and the number of printers.
@ -513,19 +553,19 @@ Cleanup:
} }
static DWORD static DWORD
_LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) _LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
{ {
return ERROR_INVALID_LEVEL; return ERROR_INVALID_LEVEL;
} }
static DWORD static DWORD
_LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) _LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
{ {
return ERROR_INVALID_LEVEL; return ERROR_INVALID_LEVEL;
} }
static DWORD static DWORD
_LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) _LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned, DWORD cchComputerName, PWSTR wszComputerName)
{ {
return ERROR_INVALID_LEVEL; return ERROR_INVALID_LEVEL;
} }
@ -533,9 +573,12 @@ _LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbB
BOOL WINAPI BOOL WINAPI
LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{ {
DWORD cchComputerName = 0;
DWORD dwErrorCode; DWORD dwErrorCode;
WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
// Do no sanity checks here. This is verified by localspl_apitest! ASSERT(pcbNeeded);
ASSERT(pcReturned);
// Begin counting. // Begin counting.
*pcbNeeded = 0; *pcbNeeded = 0;
@ -558,25 +601,31 @@ LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DW
goto Cleanup; goto Cleanup;
} }
// Check the supplied Name parameter (if any).
// This may return a Computer Name string we later prepend to the output.
dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
if (dwErrorCode != ERROR_SUCCESS)
goto Cleanup;
if (Level == 0) if (Level == 0)
{ {
dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
} }
else if (Level == 1) else if (Level == 1)
{ {
dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
} }
else if (Level == 2) else if (Level == 2)
{ {
dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
} }
else if (Level == 4) else if (Level == 4)
{ {
dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
} }
else if (Level == 5) else if (Level == 5)
{ {
dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned, cchComputerName, wszComputerName);
} }
else else
{ {