You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
680 lines
18 KiB
680 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
parseini.c
|
|
|
|
Abstract:
|
|
|
|
Process model-specific printer INI file, if any
|
|
|
|
Environment:
|
|
|
|
Windows NT printer driver
|
|
|
|
Revision History:
|
|
|
|
01/22/97 -davidx-
|
|
Allow ANSI-format INI file as well.
|
|
|
|
01/21/97 -davidx-
|
|
Created it.
|
|
|
|
--*/
|
|
|
|
#include "lib.h"
|
|
|
|
#define INI_FILENAME_EXT TEXT(".INI")
|
|
#define INI_COMMENT_CHAR '#'
|
|
#define OEMFILES_SECTION "[OEMFiles]"
|
|
#define INI_COMMENT_CHAR_UNICODE L'#'
|
|
#define OEMFILES_SECTION_UNICODE L"[OEMFiles]"
|
|
|
|
|
|
|
|
DWORD
|
|
DwCopyAnsiCharsToUnicode(
|
|
PSTR pstr,
|
|
DWORD dwLength,
|
|
PWSTR pwstr)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert the specified ANSI source string into a Unicode string.
|
|
It doesn't assume the ANSI source string is NULL terminated.
|
|
|
|
Arguments:
|
|
|
|
pstr - Points to the ANSI source string
|
|
dwLength - Specifies number of bytes in the ANSI source string
|
|
pwstr - Points to the buffer where the resulting Unicode string is returned
|
|
|
|
Return Value:
|
|
|
|
Number of wide characters written to the buffer pointed to by pwstr
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ulBytesWritten;
|
|
|
|
#if !defined(KERNEL_MODE) || defined(USERMODE_DRIVER)
|
|
|
|
return MultiByteToWideChar(CP_ACP, 0, pstr, dwLength, pwstr, dwLength);
|
|
|
|
#else // NT4 kernel mode render module
|
|
|
|
EngMultiByteToUnicodeN(pwstr, dwLength*sizeof(WCHAR), &ulBytesWritten, (PCHAR)pstr, dwLength);
|
|
|
|
return (DWORD)(ulBytesWritten / sizeof(WCHAR));
|
|
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
PWSTR
|
|
PwstrParsePrinterIniFileW(
|
|
PWSTR pwstrFileData,
|
|
DWORD dwCharCount,
|
|
PDWORD pdwReturnSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse a model-specific printer INI file (Unicode text) and
|
|
assemble all key=value entries into MultiSZ string pairs
|
|
|
|
Arguments:
|
|
|
|
pwstrFileData - Points to printer INI file data (Unicode text file)
|
|
dwCharCount - Size of printer INI file (in characters)
|
|
pdwReturnSize - Return size of the parsed MultiSZ data (in bytes)
|
|
|
|
Return Value:
|
|
|
|
Pointer to parsed MultiSZ data, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PWSTR pwstrCurLine, pwstrNextLine;
|
|
PWSTR pwstrLineEnd, pwstrFileEnd;
|
|
PWSTR pwstrEqual, pwstrResult, pwstr;
|
|
DWORD dwLength;
|
|
BOOL bOEMFilesSection = FALSE;
|
|
|
|
//
|
|
// Allocate a buffer to hold the parsed data.
|
|
// We ask for a size equaling to that of the original file.
|
|
// This may be a little redundant but it's better than
|
|
// having to go through the data twice.
|
|
//
|
|
|
|
*pdwReturnSize = 0;
|
|
|
|
if (! (pwstrResult = MemAlloc(sizeof(WCHAR) * (dwCharCount + 2))))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
pwstr = pwstrResult;
|
|
pwstrFileEnd = pwstrFileData + dwCharCount;
|
|
|
|
for (pwstrCurLine = pwstrFileData;
|
|
pwstrCurLine < pwstrFileEnd;
|
|
pwstrCurLine = pwstrNextLine)
|
|
{
|
|
//
|
|
// Find the end of current line and
|
|
// the beginning of next line
|
|
//
|
|
|
|
pwstrLineEnd = pwstrCurLine;
|
|
|
|
while (pwstrLineEnd < pwstrFileEnd &&
|
|
*pwstrLineEnd != L'\r' &&
|
|
*pwstrLineEnd != L'\n')
|
|
{
|
|
pwstrLineEnd++;
|
|
}
|
|
|
|
pwstrNextLine = pwstrLineEnd;
|
|
|
|
while ((pwstrNextLine < pwstrFileEnd) &&
|
|
(*pwstrNextLine == L'\r' ||
|
|
*pwstrNextLine == L'\n'))
|
|
{
|
|
pwstrNextLine++;
|
|
}
|
|
|
|
//
|
|
// Throw away leading and trailing spaces
|
|
// and ignore empty and comment lines
|
|
//
|
|
|
|
while (pwstrCurLine < pwstrLineEnd && iswspace(*pwstrCurLine))
|
|
pwstrCurLine++;
|
|
|
|
while (pwstrLineEnd > pwstrCurLine && iswspace(pwstrLineEnd[-1]))
|
|
pwstrLineEnd--;
|
|
|
|
if (pwstrCurLine >= pwstrLineEnd || *pwstrCurLine == INI_COMMENT_CHAR_UNICODE)
|
|
continue;
|
|
|
|
//
|
|
// Handle [section] entries
|
|
//
|
|
|
|
if (*pwstrCurLine == L'[')
|
|
{
|
|
dwLength = (DWORD)(pwstrLineEnd - pwstrCurLine);
|
|
|
|
bOEMFilesSection =
|
|
dwLength == wcslen(OEMFILES_SECTION_UNICODE) &&
|
|
_wcsnicmp(pwstrCurLine, OEMFILES_SECTION_UNICODE, dwLength) == EQUAL_STRING;
|
|
|
|
if (! bOEMFilesSection)
|
|
TERSE(("[Section] entry ignored\n"));
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Ignore all entries outside of [OEMFiles] section
|
|
//
|
|
|
|
if (! bOEMFilesSection)
|
|
{
|
|
TERSE(("Entries outside of [OEMFiles] section ignored\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Find the first occurrence of = character
|
|
//
|
|
|
|
pwstrEqual = pwstrCurLine;
|
|
|
|
while (pwstrEqual < pwstrLineEnd && *pwstrEqual != L'=')
|
|
pwstrEqual++;
|
|
|
|
if (pwstrEqual >= pwstrLineEnd || pwstrEqual == pwstrCurLine)
|
|
{
|
|
WARNING(("Entry not in the form of key=value\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add the key/value pair to the result buffer
|
|
//
|
|
|
|
if ((dwLength = (DWORD)(pwstrEqual - pwstrCurLine)) != 0)
|
|
{
|
|
CopyMemory(pwstr, pwstrCurLine, dwLength*sizeof(WCHAR));
|
|
pwstr += dwLength;
|
|
}
|
|
*pwstr++ = NUL;
|
|
|
|
pwstrEqual++;
|
|
|
|
if ((dwLength = (DWORD)(pwstrLineEnd - pwstrEqual)) > 0)
|
|
{
|
|
CopyMemory(pwstr, pwstrEqual, dwLength*sizeof(WCHAR));
|
|
pwstr += dwLength;
|
|
}
|
|
*pwstr++ = NUL;
|
|
}
|
|
|
|
*pwstr++ = NUL;
|
|
*pdwReturnSize =(DWORD)((pwstr - pwstrResult) * sizeof(WCHAR));
|
|
return pwstrResult;
|
|
}
|
|
|
|
|
|
|
|
PWSTR
|
|
PwstrParsePrinterIniFileA(
|
|
PSTR pstrFileData,
|
|
DWORD dwCharCount,
|
|
PDWORD pdwReturnSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parse a model-specific printer INI file (ANSI text) and
|
|
assemble all key=value entries into MultiSZ string pairs
|
|
|
|
Arguments:
|
|
|
|
pstrFileData - Points to printer INI file data (ANSI text file)
|
|
dwCharCount - Size of printer INI file (in characters)
|
|
pdwReturnSize - Return size of the parsed MultiSZ data (in bytes)
|
|
|
|
Return Value:
|
|
|
|
Pointer to parsed MultiSZ data, NULL if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PSTR pstrCurLine, pstrNextLine;
|
|
PSTR pstrLineEnd, pstrFileEnd, pstrEqual;
|
|
PWSTR pwstrResult, pwstr;
|
|
DWORD dwLength;
|
|
BOOL bOEMFilesSection = FALSE;
|
|
|
|
//
|
|
// Allocate a buffer to hold the parsed data.
|
|
// We ask for a size equaling to that of the original file.
|
|
// This may be a little redundant but it's better than
|
|
// having to go through the data twice.
|
|
//
|
|
|
|
*pdwReturnSize = 0;
|
|
|
|
if (! (pwstrResult = MemAlloc(sizeof(WCHAR) * (dwCharCount + 2))))
|
|
{
|
|
ERR(("Memory allocation failed\n"));
|
|
return NULL;
|
|
}
|
|
|
|
pwstr = pwstrResult;
|
|
pstrFileEnd = pstrFileData + dwCharCount;
|
|
|
|
for (pstrCurLine = pstrFileData;
|
|
pstrCurLine < pstrFileEnd;
|
|
pstrCurLine = pstrNextLine)
|
|
{
|
|
//
|
|
// Find the end of current line and
|
|
// the beginning of next line
|
|
//
|
|
|
|
pstrLineEnd = pstrCurLine;
|
|
|
|
while (pstrLineEnd < pstrFileEnd &&
|
|
*pstrLineEnd != '\r' &&
|
|
*pstrLineEnd != '\n')
|
|
{
|
|
pstrLineEnd++;
|
|
}
|
|
|
|
pstrNextLine = pstrLineEnd;
|
|
|
|
while ((pstrNextLine < pstrFileEnd) &&
|
|
(*pstrNextLine == '\r' ||
|
|
*pstrNextLine == '\n'))
|
|
{
|
|
pstrNextLine++;
|
|
}
|
|
|
|
//
|
|
// Throw away leading and trailing spaces
|
|
// and ignore empty and comment lines
|
|
//
|
|
|
|
while (pstrCurLine < pstrLineEnd && isspace(*pstrCurLine))
|
|
pstrCurLine++;
|
|
|
|
while (pstrLineEnd > pstrCurLine && isspace(pstrLineEnd[-1]))
|
|
pstrLineEnd--;
|
|
|
|
if (pstrCurLine >= pstrLineEnd || *pstrCurLine == INI_COMMENT_CHAR)
|
|
continue;
|
|
|
|
//
|
|
// Handle [section] entries
|
|
//
|
|
|
|
if (*pstrCurLine == '[')
|
|
{
|
|
dwLength = (DWORD)(pstrLineEnd - pstrCurLine);
|
|
|
|
bOEMFilesSection =
|
|
dwLength == strlen(OEMFILES_SECTION) &&
|
|
_strnicmp(pstrCurLine, OEMFILES_SECTION, dwLength) == EQUAL_STRING;
|
|
|
|
if (! bOEMFilesSection)
|
|
TERSE(("[Section] entry ignored\n"));
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Ignore all entries outside of [OEMFiles] section
|
|
//
|
|
|
|
if (! bOEMFilesSection)
|
|
{
|
|
TERSE(("Entries outside of [OEMFiles] section ignored\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Find the first occurrence of = character
|
|
//
|
|
|
|
pstrEqual = pstrCurLine;
|
|
|
|
while (pstrEqual < pstrLineEnd && *pstrEqual != '=')
|
|
pstrEqual++;
|
|
|
|
if (pstrEqual >= pstrLineEnd || pstrEqual == pstrCurLine)
|
|
{
|
|
WARNING(("Entry not in the form of key=value\n"));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add the key/value pair to the result buffer
|
|
// Convert ANSI chars to Unicode chars using system default code page
|
|
//
|
|
|
|
if ((dwLength = (DWORD)(pstrEqual - pstrCurLine)) != 0)
|
|
pwstr += DwCopyAnsiCharsToUnicode(pstrCurLine, dwLength, pwstr);
|
|
|
|
*pwstr++ = NUL;
|
|
|
|
pstrEqual++;
|
|
|
|
if ((dwLength = (DWORD)(pstrLineEnd - pstrEqual)) != 0)
|
|
pwstr += DwCopyAnsiCharsToUnicode(pstrEqual, dwLength, pwstr);
|
|
|
|
*pwstr++ = NUL;
|
|
}
|
|
|
|
*pwstr++ = NUL;
|
|
*pdwReturnSize = (DWORD)((pwstr - pwstrResult) * sizeof(WCHAR));
|
|
return pwstrResult;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BProcessPrinterIniFile(
|
|
HANDLE hPrinter,
|
|
PDRIVER_INFO_3 pDriverInfo3,
|
|
PTSTR *ppParsedData,
|
|
DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process model-specific printer INI file, if any
|
|
|
|
Arguments:
|
|
|
|
hPrinter - Handle to a local printer, with admin access
|
|
pDriverInfo3 - Printer driver info level 3
|
|
ppParsedData - output buffer to return ini file content
|
|
dwFlags - FLAG_INIPROCESS_UPGRADE is set if the printer is being upgraded
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
--*/
|
|
|
|
{
|
|
PCWSTR pwstrIniFilename; // the .INI file with latest LastWrite time
|
|
PCWSTR pwstrCurFilename; // the current .INI file we see in the list
|
|
PWSTR pwstrExtension;
|
|
PWSTR pwstrParsedData;
|
|
DWORD dwParsedDataSize;
|
|
BOOL bResult = TRUE;
|
|
|
|
//
|
|
// Find INI filename associated with the printer driver
|
|
//
|
|
|
|
#if !defined(WINNT_40) || !defined(KERNEL_MODE)
|
|
|
|
pwstrIniFilename = PtstrSearchDependentFileWithExtension(pDriverInfo3->pDependentFiles,
|
|
INI_FILENAME_EXT);
|
|
|
|
//
|
|
// We only need to do .INI FileTime comparison if there are
|
|
// more than one .INI file in the dependent file list.
|
|
//
|
|
if (pwstrIniFilename &&
|
|
PtstrSearchDependentFileWithExtension(pwstrIniFilename + wcslen(pwstrIniFilename) + 1,
|
|
INI_FILENAME_EXT))
|
|
{
|
|
FILETIME ftLatest = {0, 0};
|
|
|
|
pwstrIniFilename = NULL;
|
|
pwstrCurFilename = pDriverInfo3->pDependentFiles;
|
|
|
|
while (pwstrCurFilename = PtstrSearchDependentFileWithExtension(pwstrCurFilename,
|
|
INI_FILENAME_EXT))
|
|
{
|
|
HANDLE hFile;
|
|
|
|
hFile = CreateFile(pwstrCurFilename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
|
|
NULL);
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
FILETIME ftCurrent = {0, 0};
|
|
|
|
if (GetFileTime(hFile, NULL, NULL, &ftCurrent))
|
|
{
|
|
//
|
|
// If it's the first .INI file we encountered, we just
|
|
// neeed to remember its file name and time as the latest.
|
|
//
|
|
// Otherwise, we need to compare its file time with the
|
|
// latest .INI file time we've seen before and choose the
|
|
// newer file as the latest.
|
|
//
|
|
if ((pwstrIniFilename == NULL) ||
|
|
(CompareFileTime(&ftCurrent, &ftLatest) == 1))
|
|
{
|
|
ftLatest = ftCurrent;
|
|
pwstrIniFilename = pwstrCurFilename;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR(("GetFileTime failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
}
|
|
else
|
|
{
|
|
ERR(("CreateFile failed: %d\n", GetLastError()));
|
|
}
|
|
|
|
pwstrCurFilename += wcslen(pwstrCurFilename) + 1;
|
|
}
|
|
}
|
|
|
|
#else // NT4 kernel mode only
|
|
|
|
//
|
|
// In point-and-print case, the client may not have admin
|
|
// priviledge to write to server's registry, and in NT4
|
|
// kernel mode we can't get the printer's DriverInfo3 for
|
|
// the dependent file list, so in order to allow us get
|
|
// the plugin info, we require following:
|
|
//
|
|
// If the printer's data file is named XYZ.PPD/XYZ.GPD, and
|
|
// the printer has plugins, then it must use file named XYZ.INI
|
|
// to specify its plugin info.
|
|
//
|
|
|
|
if ((pwstrIniFilename = DuplicateString(pDriverInfo3->pDataFile)) == NULL ||
|
|
(pwstrExtension = wcsrchr(pwstrIniFilename, TEXT('.'))) == NULL ||
|
|
wcslen(pwstrExtension) != _tcslen(INI_FILENAME_EXT))
|
|
{
|
|
ERR(("Can't compose the .ini file name from PPD/GPD name."));
|
|
|
|
MemFree((PWSTR)pwstrIniFilename);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
StringCchCopyW(pwstrExtension, wcslen(pwstrExtension) + 1, INI_FILENAME_EXT);
|
|
|
|
#endif // !defined(WINNT_40) || !defined(KERNEL_MODE)
|
|
|
|
//
|
|
// We only have work to do if there is a model-specific printer INI file
|
|
//
|
|
|
|
if (pwstrIniFilename != NULL)
|
|
{
|
|
HFILEMAP hFileMap;
|
|
PBYTE pubIniFileData;
|
|
DWORD dwIniFileSize;
|
|
|
|
hFileMap = MapFileIntoMemory(pwstrIniFilename,
|
|
(PVOID *) &pubIniFileData,
|
|
&dwIniFileSize);
|
|
|
|
if (hFileMap != NULL)
|
|
{
|
|
//
|
|
// If the first two bytes are FF FE, then we assume
|
|
// the text file is in Unicode format. Otherwise,
|
|
// assume the text is in ANSI format.
|
|
//
|
|
|
|
if (dwIniFileSize >= sizeof(WCHAR) &&
|
|
pubIniFileData[0] == 0xFF &&
|
|
pubIniFileData[1] == 0xFE)
|
|
{
|
|
ASSERT((dwIniFileSize % sizeof(WCHAR)) == 0);
|
|
|
|
pwstrParsedData = PwstrParsePrinterIniFileW(
|
|
(PWSTR) pubIniFileData + 1,
|
|
dwIniFileSize / sizeof(WCHAR) - 1,
|
|
&dwParsedDataSize);
|
|
}
|
|
else
|
|
{
|
|
pwstrParsedData = PwstrParsePrinterIniFileA(
|
|
(PSTR) pubIniFileData,
|
|
dwIniFileSize,
|
|
&dwParsedDataSize);
|
|
}
|
|
|
|
bResult = (pwstrParsedData != NULL);
|
|
|
|
#ifndef KERNEL_MODE
|
|
|
|
//
|
|
// If not in kernel mode (where we can't write to registry),
|
|
// we will try to save the parsed data into registry.
|
|
// This may not succeed if user doesn't have proper right.
|
|
//
|
|
|
|
//
|
|
// Fixing RC1 bug #423567
|
|
//
|
|
|
|
#if 0
|
|
if (bResult && hPrinter)
|
|
{
|
|
BSetPrinterDataMultiSZPair(
|
|
hPrinter,
|
|
REGVAL_INIDATA,
|
|
pwstrParsedData,
|
|
dwParsedDataSize);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//
|
|
// If caller ask for the parsed data directly,
|
|
// don't free it and save the pointer for caller.
|
|
// Caller is responsible for freeing the memory
|
|
//
|
|
|
|
if (ppParsedData)
|
|
{
|
|
*ppParsedData = pwstrParsedData;
|
|
}
|
|
else
|
|
{
|
|
MemFree(pwstrParsedData);
|
|
}
|
|
|
|
UnmapFileFromMemory(hFileMap);
|
|
}
|
|
else
|
|
bResult = FALSE;
|
|
|
|
#if defined(WINNT_40) && defined(KERNEL_MODE)
|
|
|
|
//
|
|
// Need to free memory allocated by DuplicateString
|
|
//
|
|
|
|
MemFree((PWSTR)pwstrIniFilename);
|
|
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef KERNEL_MODE
|
|
|
|
if (dwFlags & FLAG_INIPROCESS_UPGRADE)
|
|
{
|
|
DWORD dwType, dwSize, dwStatus;
|
|
|
|
//
|
|
// We know there is no .ini file in the dependent list. So
|
|
// we will check if there is an old INI registry value there,
|
|
// if so delete it.
|
|
//
|
|
|
|
ASSERT(hPrinter != NULL);
|
|
|
|
dwStatus = GetPrinterData(hPrinter,
|
|
REGVAL_INIDATA,
|
|
&dwType,
|
|
NULL,
|
|
0,
|
|
&dwSize);
|
|
|
|
if ((dwStatus == ERROR_MORE_DATA || dwStatus == ERROR_SUCCESS) &&
|
|
(dwSize > 0) &&
|
|
(dwType == REG_MULTI_SZ))
|
|
{
|
|
dwStatus = DeletePrinterData(hPrinter, REGVAL_INIDATA);
|
|
|
|
if (dwStatus != ERROR_SUCCESS)
|
|
{
|
|
ERR(("Couldn't delete '%ws' during upgrade: %d\n", REGVAL_INIDATA, dwStatus));
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // !KERNEL_MODE
|
|
|
|
bResult = FALSE;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|