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

680 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. parseini.c
  5. Abstract:
  6. Process model-specific printer INI file, if any
  7. Environment:
  8. Windows NT printer driver
  9. Revision History:
  10. 01/22/97 -davidx-
  11. Allow ANSI-format INI file as well.
  12. 01/21/97 -davidx-
  13. Created it.
  14. --*/
  15. #include "lib.h"
  16. #define INI_FILENAME_EXT TEXT(".INI")
  17. #define INI_COMMENT_CHAR '#'
  18. #define OEMFILES_SECTION "[OEMFiles]"
  19. #define INI_COMMENT_CHAR_UNICODE L'#'
  20. #define OEMFILES_SECTION_UNICODE L"[OEMFiles]"
  21. DWORD
  22. DwCopyAnsiCharsToUnicode(
  23. PSTR pstr,
  24. DWORD dwLength,
  25. PWSTR pwstr)
  26. /*++
  27. Routine Description:
  28. Convert the specified ANSI source string into a Unicode string.
  29. It doesn't assume the ANSI source string is NULL terminated.
  30. Arguments:
  31. pstr - Points to the ANSI source string
  32. dwLength - Specifies number of bytes in the ANSI source string
  33. pwstr - Points to the buffer where the resulting Unicode string is returned
  34. Return Value:
  35. Number of wide characters written to the buffer pointed to by pwstr
  36. --*/
  37. {
  38. ULONG ulBytesWritten;
  39. #if !defined(KERNEL_MODE) || defined(USERMODE_DRIVER)
  40. return MultiByteToWideChar(CP_ACP, 0, pstr, dwLength, pwstr, dwLength);
  41. #else // NT4 kernel mode render module
  42. EngMultiByteToUnicodeN(pwstr, dwLength*sizeof(WCHAR), &ulBytesWritten, (PCHAR)pstr, dwLength);
  43. return (DWORD)(ulBytesWritten / sizeof(WCHAR));
  44. #endif
  45. }
  46. PWSTR
  47. PwstrParsePrinterIniFileW(
  48. PWSTR pwstrFileData,
  49. DWORD dwCharCount,
  50. PDWORD pdwReturnSize
  51. )
  52. /*++
  53. Routine Description:
  54. Parse a model-specific printer INI file (Unicode text) and
  55. assemble all key=value entries into MultiSZ string pairs
  56. Arguments:
  57. pwstrFileData - Points to printer INI file data (Unicode text file)
  58. dwCharCount - Size of printer INI file (in characters)
  59. pdwReturnSize - Return size of the parsed MultiSZ data (in bytes)
  60. Return Value:
  61. Pointer to parsed MultiSZ data, NULL if there is an error
  62. --*/
  63. {
  64. PWSTR pwstrCurLine, pwstrNextLine;
  65. PWSTR pwstrLineEnd, pwstrFileEnd;
  66. PWSTR pwstrEqual, pwstrResult, pwstr;
  67. DWORD dwLength;
  68. BOOL bOEMFilesSection = FALSE;
  69. //
  70. // Allocate a buffer to hold the parsed data.
  71. // We ask for a size equaling to that of the original file.
  72. // This may be a little redundant but it's better than
  73. // having to go through the data twice.
  74. //
  75. *pdwReturnSize = 0;
  76. if (! (pwstrResult = MemAlloc(sizeof(WCHAR) * (dwCharCount + 2))))
  77. {
  78. ERR(("Memory allocation failed\n"));
  79. return NULL;
  80. }
  81. pwstr = pwstrResult;
  82. pwstrFileEnd = pwstrFileData + dwCharCount;
  83. for (pwstrCurLine = pwstrFileData;
  84. pwstrCurLine < pwstrFileEnd;
  85. pwstrCurLine = pwstrNextLine)
  86. {
  87. //
  88. // Find the end of current line and
  89. // the beginning of next line
  90. //
  91. pwstrLineEnd = pwstrCurLine;
  92. while (pwstrLineEnd < pwstrFileEnd &&
  93. *pwstrLineEnd != L'\r' &&
  94. *pwstrLineEnd != L'\n')
  95. {
  96. pwstrLineEnd++;
  97. }
  98. pwstrNextLine = pwstrLineEnd;
  99. while ((pwstrNextLine < pwstrFileEnd) &&
  100. (*pwstrNextLine == L'\r' ||
  101. *pwstrNextLine == L'\n'))
  102. {
  103. pwstrNextLine++;
  104. }
  105. //
  106. // Throw away leading and trailing spaces
  107. // and ignore empty and comment lines
  108. //
  109. while (pwstrCurLine < pwstrLineEnd && iswspace(*pwstrCurLine))
  110. pwstrCurLine++;
  111. while (pwstrLineEnd > pwstrCurLine && iswspace(pwstrLineEnd[-1]))
  112. pwstrLineEnd--;
  113. if (pwstrCurLine >= pwstrLineEnd || *pwstrCurLine == INI_COMMENT_CHAR_UNICODE)
  114. continue;
  115. //
  116. // Handle [section] entries
  117. //
  118. if (*pwstrCurLine == L'[')
  119. {
  120. dwLength = (DWORD)(pwstrLineEnd - pwstrCurLine);
  121. bOEMFilesSection =
  122. dwLength == wcslen(OEMFILES_SECTION_UNICODE) &&
  123. _wcsnicmp(pwstrCurLine, OEMFILES_SECTION_UNICODE, dwLength) == EQUAL_STRING;
  124. if (! bOEMFilesSection)
  125. TERSE(("[Section] entry ignored\n"));
  126. continue;
  127. }
  128. //
  129. // Ignore all entries outside of [OEMFiles] section
  130. //
  131. if (! bOEMFilesSection)
  132. {
  133. TERSE(("Entries outside of [OEMFiles] section ignored\n"));
  134. continue;
  135. }
  136. //
  137. // Find the first occurrence of = character
  138. //
  139. pwstrEqual = pwstrCurLine;
  140. while (pwstrEqual < pwstrLineEnd && *pwstrEqual != L'=')
  141. pwstrEqual++;
  142. if (pwstrEqual >= pwstrLineEnd || pwstrEqual == pwstrCurLine)
  143. {
  144. WARNING(("Entry not in the form of key=value\n"));
  145. continue;
  146. }
  147. //
  148. // Add the key/value pair to the result buffer
  149. //
  150. if ((dwLength = (DWORD)(pwstrEqual - pwstrCurLine)) != 0)
  151. {
  152. CopyMemory(pwstr, pwstrCurLine, dwLength*sizeof(WCHAR));
  153. pwstr += dwLength;
  154. }
  155. *pwstr++ = NUL;
  156. pwstrEqual++;
  157. if ((dwLength = (DWORD)(pwstrLineEnd - pwstrEqual)) > 0)
  158. {
  159. CopyMemory(pwstr, pwstrEqual, dwLength*sizeof(WCHAR));
  160. pwstr += dwLength;
  161. }
  162. *pwstr++ = NUL;
  163. }
  164. *pwstr++ = NUL;
  165. *pdwReturnSize =(DWORD)((pwstr - pwstrResult) * sizeof(WCHAR));
  166. return pwstrResult;
  167. }
  168. PWSTR
  169. PwstrParsePrinterIniFileA(
  170. PSTR pstrFileData,
  171. DWORD dwCharCount,
  172. PDWORD pdwReturnSize
  173. )
  174. /*++
  175. Routine Description:
  176. Parse a model-specific printer INI file (ANSI text) and
  177. assemble all key=value entries into MultiSZ string pairs
  178. Arguments:
  179. pstrFileData - Points to printer INI file data (ANSI text file)
  180. dwCharCount - Size of printer INI file (in characters)
  181. pdwReturnSize - Return size of the parsed MultiSZ data (in bytes)
  182. Return Value:
  183. Pointer to parsed MultiSZ data, NULL if there is an error
  184. --*/
  185. {
  186. PSTR pstrCurLine, pstrNextLine;
  187. PSTR pstrLineEnd, pstrFileEnd, pstrEqual;
  188. PWSTR pwstrResult, pwstr;
  189. DWORD dwLength;
  190. BOOL bOEMFilesSection = FALSE;
  191. //
  192. // Allocate a buffer to hold the parsed data.
  193. // We ask for a size equaling to that of the original file.
  194. // This may be a little redundant but it's better than
  195. // having to go through the data twice.
  196. //
  197. *pdwReturnSize = 0;
  198. if (! (pwstrResult = MemAlloc(sizeof(WCHAR) * (dwCharCount + 2))))
  199. {
  200. ERR(("Memory allocation failed\n"));
  201. return NULL;
  202. }
  203. pwstr = pwstrResult;
  204. pstrFileEnd = pstrFileData + dwCharCount;
  205. for (pstrCurLine = pstrFileData;
  206. pstrCurLine < pstrFileEnd;
  207. pstrCurLine = pstrNextLine)
  208. {
  209. //
  210. // Find the end of current line and
  211. // the beginning of next line
  212. //
  213. pstrLineEnd = pstrCurLine;
  214. while (pstrLineEnd < pstrFileEnd &&
  215. *pstrLineEnd != '\r' &&
  216. *pstrLineEnd != '\n')
  217. {
  218. pstrLineEnd++;
  219. }
  220. pstrNextLine = pstrLineEnd;
  221. while ((pstrNextLine < pstrFileEnd) &&
  222. (*pstrNextLine == '\r' ||
  223. *pstrNextLine == '\n'))
  224. {
  225. pstrNextLine++;
  226. }
  227. //
  228. // Throw away leading and trailing spaces
  229. // and ignore empty and comment lines
  230. //
  231. while (pstrCurLine < pstrLineEnd && isspace(*pstrCurLine))
  232. pstrCurLine++;
  233. while (pstrLineEnd > pstrCurLine && isspace(pstrLineEnd[-1]))
  234. pstrLineEnd--;
  235. if (pstrCurLine >= pstrLineEnd || *pstrCurLine == INI_COMMENT_CHAR)
  236. continue;
  237. //
  238. // Handle [section] entries
  239. //
  240. if (*pstrCurLine == '[')
  241. {
  242. dwLength = (DWORD)(pstrLineEnd - pstrCurLine);
  243. bOEMFilesSection =
  244. dwLength == strlen(OEMFILES_SECTION) &&
  245. _strnicmp(pstrCurLine, OEMFILES_SECTION, dwLength) == EQUAL_STRING;
  246. if (! bOEMFilesSection)
  247. TERSE(("[Section] entry ignored\n"));
  248. continue;
  249. }
  250. //
  251. // Ignore all entries outside of [OEMFiles] section
  252. //
  253. if (! bOEMFilesSection)
  254. {
  255. TERSE(("Entries outside of [OEMFiles] section ignored\n"));
  256. continue;
  257. }
  258. //
  259. // Find the first occurrence of = character
  260. //
  261. pstrEqual = pstrCurLine;
  262. while (pstrEqual < pstrLineEnd && *pstrEqual != '=')
  263. pstrEqual++;
  264. if (pstrEqual >= pstrLineEnd || pstrEqual == pstrCurLine)
  265. {
  266. WARNING(("Entry not in the form of key=value\n"));
  267. continue;
  268. }
  269. //
  270. // Add the key/value pair to the result buffer
  271. // Convert ANSI chars to Unicode chars using system default code page
  272. //
  273. if ((dwLength = (DWORD)(pstrEqual - pstrCurLine)) != 0)
  274. pwstr += DwCopyAnsiCharsToUnicode(pstrCurLine, dwLength, pwstr);
  275. *pwstr++ = NUL;
  276. pstrEqual++;
  277. if ((dwLength = (DWORD)(pstrLineEnd - pstrEqual)) != 0)
  278. pwstr += DwCopyAnsiCharsToUnicode(pstrEqual, dwLength, pwstr);
  279. *pwstr++ = NUL;
  280. }
  281. *pwstr++ = NUL;
  282. *pdwReturnSize = (DWORD)((pwstr - pwstrResult) * sizeof(WCHAR));
  283. return pwstrResult;
  284. }
  285. BOOL
  286. BProcessPrinterIniFile(
  287. HANDLE hPrinter,
  288. PDRIVER_INFO_3 pDriverInfo3,
  289. PTSTR *ppParsedData,
  290. DWORD dwFlags
  291. )
  292. /*++
  293. Routine Description:
  294. Process model-specific printer INI file, if any
  295. Arguments:
  296. hPrinter - Handle to a local printer, with admin access
  297. pDriverInfo3 - Printer driver info level 3
  298. ppParsedData - output buffer to return ini file content
  299. dwFlags - FLAG_INIPROCESS_UPGRADE is set if the printer is being upgraded
  300. Return Value:
  301. TRUE if successful, FALSE if there is an error
  302. --*/
  303. {
  304. PCWSTR pwstrIniFilename; // the .INI file with latest LastWrite time
  305. PCWSTR pwstrCurFilename; // the current .INI file we see in the list
  306. PWSTR pwstrExtension;
  307. PWSTR pwstrParsedData;
  308. DWORD dwParsedDataSize;
  309. BOOL bResult = TRUE;
  310. //
  311. // Find INI filename associated with the printer driver
  312. //
  313. #if !defined(WINNT_40) || !defined(KERNEL_MODE)
  314. pwstrIniFilename = PtstrSearchDependentFileWithExtension(pDriverInfo3->pDependentFiles,
  315. INI_FILENAME_EXT);
  316. //
  317. // We only need to do .INI FileTime comparison if there are
  318. // more than one .INI file in the dependent file list.
  319. //
  320. if (pwstrIniFilename &&
  321. PtstrSearchDependentFileWithExtension(pwstrIniFilename + wcslen(pwstrIniFilename) + 1,
  322. INI_FILENAME_EXT))
  323. {
  324. FILETIME ftLatest = {0, 0};
  325. pwstrIniFilename = NULL;
  326. pwstrCurFilename = pDriverInfo3->pDependentFiles;
  327. while (pwstrCurFilename = PtstrSearchDependentFileWithExtension(pwstrCurFilename,
  328. INI_FILENAME_EXT))
  329. {
  330. HANDLE hFile;
  331. hFile = CreateFile(pwstrCurFilename,
  332. GENERIC_READ,
  333. FILE_SHARE_READ,
  334. NULL,
  335. OPEN_EXISTING,
  336. FILE_ATTRIBUTE_NORMAL | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
  337. NULL);
  338. if (hFile != INVALID_HANDLE_VALUE)
  339. {
  340. FILETIME ftCurrent = {0, 0};
  341. if (GetFileTime(hFile, NULL, NULL, &ftCurrent))
  342. {
  343. //
  344. // If it's the first .INI file we encountered, we just
  345. // neeed to remember its file name and time as the latest.
  346. //
  347. // Otherwise, we need to compare its file time with the
  348. // latest .INI file time we've seen before and choose the
  349. // newer file as the latest.
  350. //
  351. if ((pwstrIniFilename == NULL) ||
  352. (CompareFileTime(&ftCurrent, &ftLatest) == 1))
  353. {
  354. ftLatest = ftCurrent;
  355. pwstrIniFilename = pwstrCurFilename;
  356. }
  357. }
  358. else
  359. {
  360. ERR(("GetFileTime failed: %d\n", GetLastError()));
  361. }
  362. CloseHandle(hFile);
  363. }
  364. else
  365. {
  366. ERR(("CreateFile failed: %d\n", GetLastError()));
  367. }
  368. pwstrCurFilename += wcslen(pwstrCurFilename) + 1;
  369. }
  370. }
  371. #else // NT4 kernel mode only
  372. //
  373. // In point-and-print case, the client may not have admin
  374. // priviledge to write to server's registry, and in NT4
  375. // kernel mode we can't get the printer's DriverInfo3 for
  376. // the dependent file list, so in order to allow us get
  377. // the plugin info, we require following:
  378. //
  379. // If the printer's data file is named XYZ.PPD/XYZ.GPD, and
  380. // the printer has plugins, then it must use file named XYZ.INI
  381. // to specify its plugin info.
  382. //
  383. if ((pwstrIniFilename = DuplicateString(pDriverInfo3->pDataFile)) == NULL ||
  384. (pwstrExtension = wcsrchr(pwstrIniFilename, TEXT('.'))) == NULL ||
  385. wcslen(pwstrExtension) != _tcslen(INI_FILENAME_EXT))
  386. {
  387. ERR(("Can't compose the .ini file name from PPD/GPD name."));
  388. MemFree((PWSTR)pwstrIniFilename);
  389. return FALSE;
  390. }
  391. StringCchCopyW(pwstrExtension, wcslen(pwstrExtension) + 1, INI_FILENAME_EXT);
  392. #endif // !defined(WINNT_40) || !defined(KERNEL_MODE)
  393. //
  394. // We only have work to do if there is a model-specific printer INI file
  395. //
  396. if (pwstrIniFilename != NULL)
  397. {
  398. HFILEMAP hFileMap;
  399. PBYTE pubIniFileData;
  400. DWORD dwIniFileSize;
  401. hFileMap = MapFileIntoMemory(pwstrIniFilename,
  402. (PVOID *) &pubIniFileData,
  403. &dwIniFileSize);
  404. if (hFileMap != NULL)
  405. {
  406. //
  407. // If the first two bytes are FF FE, then we assume
  408. // the text file is in Unicode format. Otherwise,
  409. // assume the text is in ANSI format.
  410. //
  411. if (dwIniFileSize >= sizeof(WCHAR) &&
  412. pubIniFileData[0] == 0xFF &&
  413. pubIniFileData[1] == 0xFE)
  414. {
  415. ASSERT((dwIniFileSize % sizeof(WCHAR)) == 0);
  416. pwstrParsedData = PwstrParsePrinterIniFileW(
  417. (PWSTR) pubIniFileData + 1,
  418. dwIniFileSize / sizeof(WCHAR) - 1,
  419. &dwParsedDataSize);
  420. }
  421. else
  422. {
  423. pwstrParsedData = PwstrParsePrinterIniFileA(
  424. (PSTR) pubIniFileData,
  425. dwIniFileSize,
  426. &dwParsedDataSize);
  427. }
  428. bResult = (pwstrParsedData != NULL);
  429. #ifndef KERNEL_MODE
  430. //
  431. // If not in kernel mode (where we can't write to registry),
  432. // we will try to save the parsed data into registry.
  433. // This may not succeed if user doesn't have proper right.
  434. //
  435. //
  436. // Fixing RC1 bug #423567
  437. //
  438. #if 0
  439. if (bResult && hPrinter)
  440. {
  441. BSetPrinterDataMultiSZPair(
  442. hPrinter,
  443. REGVAL_INIDATA,
  444. pwstrParsedData,
  445. dwParsedDataSize);
  446. }
  447. #endif
  448. #endif
  449. //
  450. // If caller ask for the parsed data directly,
  451. // don't free it and save the pointer for caller.
  452. // Caller is responsible for freeing the memory
  453. //
  454. if (ppParsedData)
  455. {
  456. *ppParsedData = pwstrParsedData;
  457. }
  458. else
  459. {
  460. MemFree(pwstrParsedData);
  461. }
  462. UnmapFileFromMemory(hFileMap);
  463. }
  464. else
  465. bResult = FALSE;
  466. #if defined(WINNT_40) && defined(KERNEL_MODE)
  467. //
  468. // Need to free memory allocated by DuplicateString
  469. //
  470. MemFree((PWSTR)pwstrIniFilename);
  471. #endif
  472. }
  473. else
  474. {
  475. #ifndef KERNEL_MODE
  476. if (dwFlags & FLAG_INIPROCESS_UPGRADE)
  477. {
  478. DWORD dwType, dwSize, dwStatus;
  479. //
  480. // We know there is no .ini file in the dependent list. So
  481. // we will check if there is an old INI registry value there,
  482. // if so delete it.
  483. //
  484. ASSERT(hPrinter != NULL);
  485. dwStatus = GetPrinterData(hPrinter,
  486. REGVAL_INIDATA,
  487. &dwType,
  488. NULL,
  489. 0,
  490. &dwSize);
  491. if ((dwStatus == ERROR_MORE_DATA || dwStatus == ERROR_SUCCESS) &&
  492. (dwSize > 0) &&
  493. (dwType == REG_MULTI_SZ))
  494. {
  495. dwStatus = DeletePrinterData(hPrinter, REGVAL_INIDATA);
  496. if (dwStatus != ERROR_SUCCESS)
  497. {
  498. ERR(("Couldn't delete '%ws' during upgrade: %d\n", REGVAL_INIDATA, dwStatus));
  499. }
  500. }
  501. }
  502. #endif // !KERNEL_MODE
  503. bResult = FALSE;
  504. }
  505. return bResult;
  506. }