Source code of Windows XP (NT5)
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.

890 lines
33 KiB

  1. /*++
  2. w95mig.c
  3. Copyright (c) 1997 Microsoft Corporation
  4. This module contains the win95 side of the migration code.
  5. Author:
  6. Brian Dewey (t-briand) 1997-7-18
  7. --*/
  8. #include <windows.h>
  9. #include <setupapi.h>
  10. #include <mapidefs.h>
  11. #include <mapitags.h> // To get the property definitions.
  12. #include <stdio.h>
  13. #include <tchar.h>
  14. #include "migrate.h" // Contains prototypes & version information.
  15. #include "property.h" // Stolen from Elliott -- contains their fax properties
  16. #include "debug.h" // for tracing.
  17. #include "resource.h" // Migration resources.
  18. #include "msg.h"
  19. // ------------------------------------------------------------
  20. // Defines & macros
  21. #define SWAPWORD(x) (((x) << 16) | ((x) >> 16))
  22. // ------------------------------------------------------------
  23. // Internal data
  24. // First, this is the name of the INF file that we generate.
  25. static TCHAR szInfFileBase[] = TEXT("migrate.inf");
  26. TCHAR szInfFileName[MAX_PATH]; // This will be the fully qualified path of the above.
  27. static char lpWorkingDir[MAX_PATH]; // This is our working directory.
  28. static TCHAR szDoInstall[4]; // Will be either "No" or "Yes".
  29. static TCHAR szFaxPrinterName[MAX_PATH]; // Name of the fax printer.
  30. static TCHAR szFaxAreaCode[16]; // Contains the fax modem area code.
  31. static TCHAR szFaxNumber[9]; // Fax # w/o area or country code.
  32. static TCHAR szNTProfileName[MAX_PATH]; // Profile to use for routing.
  33. static TCHAR szFaxStoreDir[MAX_PATH]; // Folder to use for routing.
  34. static TCHAR szUserName[MAX_PATH]; // This will be the user's name who owns the fax service.
  35. static TCHAR szUserID[MAX_PATH]; // This is the login name of the user who owns the fax.
  36. static TCHAR szPerformInstall[16]; // Will be szPerformYes or szPerformNo.
  37. static TCHAR szPerformYes[] = TEXT("New");
  38. static TCHAR szPerformNo[] = TEXT("Skip");
  39. // The following are section names from the Microsoft registry.
  40. // They're used to find the fax profile for a user.
  41. static const LPTSTR LPUSERPROF = TEXT("Software\\Microsoft\\Windows Messaging Subsystem\\Profiles");
  42. static const LPTSTR LPPROFNAME = TEXT("DefaultProfile");
  43. // The following's part of the path to the Exchange profile in question.
  44. static const LPTSTR LPPROFILES = TEXT("Software\\Microsoft\\Windows Messaging Subsystem\\Profiles");
  45. // This is how we find the fax printer.
  46. static const LPTSTR LPPRINTER = TEXT("System\\CurrentControlSet\\control\\Print\\Printers");
  47. // This is how we get the root UID.
  48. static const LPTSTR LPPROFUID = TEXT("Profile UID");
  49. // This is the name we use for the logon user section of 'faxuser.ini'
  50. LPCTSTR lpLogonUser = TEXT("Logon User");
  51. // This keeps track of the number of users migrated. Used to make annotations
  52. // in the INF file.
  53. static DWORD dwUserCount = 0;
  54. // ------------------------------------------------------------
  55. // Internal function prototypes
  56. static BOOL GetUserProfileName(HKEY hUser, LPTSTR lpProfName, DWORD cbSize);
  57. static BOOL GetFaxPrinterName(LPTSTR lpPrinterName, DWORD cbSize);
  58. static BOOL GetRegProfileKey(HKEY hUser, LPTSTR lpProfName, PHKEY phRegProfileKey);
  59. static void DumpUserInfo(HKEY hUserInfo, LPCSTR UserName, LPTSTR szProfileName);
  60. static void SetGlobalFaxNumberInfo(LPCTSTR szPhone);
  61. static void GenerateIncompatableMessages();
  62. static BOOL InitializeInfFile(LPCTSTR WorkingDirectory);
  63. typedef struct {
  64. CHAR CompanyName[256];
  65. CHAR SupportNumber[256];
  66. CHAR SupportUrl[256];
  67. CHAR InstructionsToUser[1024];
  68. } VENDORINFO, *PVENDORINFO;
  69. VENDORINFO VendorInfo;
  70. // QueryVersion
  71. //
  72. // This routine returns version information about the migration DLL.
  73. //
  74. // Parameters:
  75. // Commented below.
  76. //
  77. // Returns:
  78. // ERROR_SUCCESS.
  79. //
  80. // Author:
  81. // Brian Dewey (t-briand) 1997-7-23
  82. LONG
  83. CALLBACK
  84. QueryVersion (
  85. OUT LPCSTR *ProductID, // Unique identifier string.
  86. OUT LPUINT DllVersion, // Version number. Cannot be zero.
  87. OUT LPINT *CodePageArray, // OPTIONAL. Language dependencies.
  88. OUT LPCSTR *ExeNamesBuf, // OPTIONAL. Executables to look for.
  89. OUT PVENDORINFO *ppVendorInfo
  90. )
  91. {
  92. *ProductID = "Microsoft Fax";
  93. *DllVersion = FAX_MIGRATION_VERSION;
  94. *CodePageArray = NULL; // No language dependencies
  95. *ExeNamesBuf = NULL;
  96. *ppVendorInfo = &VendorInfo;
  97. FormatMessage(
  98. FORMAT_MESSAGE_FROM_HMODULE,
  99. hinstMigDll,
  100. MSG_VI_COMPANY_NAME,
  101. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  102. &VendorInfo.CompanyName[0],
  103. sizeof(VendorInfo.CompanyName),
  104. NULL
  105. );
  106. FormatMessage(
  107. FORMAT_MESSAGE_FROM_HMODULE,
  108. hinstMigDll,
  109. MSG_VI_SUPPORT_NUMBER,
  110. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  111. &VendorInfo.SupportNumber[0],
  112. sizeof(VendorInfo.SupportNumber),
  113. NULL
  114. );
  115. FormatMessage(
  116. FORMAT_MESSAGE_FROM_HMODULE,
  117. hinstMigDll,
  118. MSG_VI_SUPPORT_URL,
  119. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  120. &VendorInfo.SupportUrl[0],
  121. sizeof(VendorInfo.SupportUrl),
  122. NULL
  123. );
  124. FormatMessage(
  125. FORMAT_MESSAGE_FROM_HMODULE,
  126. hinstMigDll,
  127. MSG_VI_INSTRUCTIONS,
  128. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  129. &VendorInfo.InstructionsToUser[0],
  130. sizeof(VendorInfo.InstructionsToUser),
  131. NULL
  132. );
  133. return ERROR_SUCCESS;
  134. }
  135. // Initialize9x
  136. //
  137. // This is called to initialize the migration process. See the migration dll
  138. // spec for more details.
  139. //
  140. // Parameters:
  141. // Commented below.
  142. //
  143. // Author:
  144. // Brian Dewey (t-briand) 1997-7-14
  145. LONG
  146. CALLBACK
  147. Initialize9x(
  148. IN LPCSTR WorkingDirectory, // Place to store files.
  149. IN LPCSTR SourceDirectory, // Location of the Windows NT source.
  150. LPVOID Reserved // Exactly what it says.
  151. )
  152. {
  153. TRACE((TEXT("Fax migration DLL initialized.\r\n")));
  154. InitializeInfFile(WorkingDirectory);
  155. strncpy(lpWorkingDir, WorkingDirectory, MAX_PATH);
  156. _tcscpy(szPerformInstall, szPerformNo); // First assume we don't need to install.
  157. return ERROR_SUCCESS; // A very confused return value.
  158. }
  159. // MigrateUser9x
  160. //
  161. // This routine records the fax information specific to a user.
  162. //
  163. // Parameters:
  164. // Documented below.
  165. //
  166. // Returns:
  167. // ERROR_SUCCESS.
  168. //
  169. // Author:
  170. // Brian Dewey (t-briand) 1997-7-14
  171. LONG
  172. CALLBACK
  173. MigrateUser9x(
  174. IN HWND ParentWnd, // Parent (if need a UI)
  175. IN LPCSTR UnattendFile, // Name of unattend file
  176. IN HKEY UserRegKey, // Key to this user's registry settings.
  177. IN LPCSTR UserName, // Account name of user.
  178. LPVOID Reserved
  179. )
  180. {
  181. TCHAR szProfileName[MAX_PATH]; // Holds the name of this user's profile.
  182. HKEY hRegProfileKey; // The fax profile key in the registry.
  183. DWORD dwExceptCode; // Exception error code
  184. __try {
  185. TRACE((TEXT("Fax migration dll migrating user.\r\n")));
  186. if(GetUserProfileName(UserRegKey, szProfileName, sizeof(szProfileName))) {
  187. TRACE((TEXT("\tProfile name = %s.\r\n"), szProfileName));
  188. if(GetRegProfileKey(UserRegKey, szProfileName, &hRegProfileKey)) {
  189. // We now know we want to do an installation.
  190. _tcscpy(szPerformInstall, szPerformYes);
  191. TRACE((TEXT("Successfully got profile information.\r\n")));
  192. _tcscpy(szNTProfileName, szProfileName); // Remember this name for NT.
  193. // NULL means the logon user...
  194. if(UserName != NULL)
  195. _tcscpy(szUserID, UserName); // Remember the ID for the unattend.txt file.
  196. else
  197. _tcscpy(szUserID, lpLogonUser); // Use the logon user name.
  198. DumpUserInfo(hRegProfileKey, szUserID, szProfileName);
  199. RegCloseKey(hRegProfileKey);
  200. } else {
  201. TRACE(TEXT(("Could not get profile information.\r\n")));
  202. return ERROR_NOT_INSTALLED;
  203. }
  204. } else {
  205. TRACE((TEXT("\tCould not find profile name.\r\n")));
  206. return ERROR_NOT_INSTALLED;
  207. }
  208. return ERROR_SUCCESS; // A very confused return value.
  209. }
  210. __except(EXCEPTION_EXECUTE_HANDLER) {
  211. dwExceptCode = GetExceptionCode();
  212. switch(dwExceptCode) {
  213. case EXCEPTION_ACCESS_VIOLATION:
  214. TRACE((TEXT("Fax:MigrateUser9x:Access violation.\r\n")));
  215. break;
  216. case EXCEPTION_INT_DIVIDE_BY_ZERO:
  217. case EXCEPTION_FLT_DIVIDE_BY_ZERO:
  218. TRACE((TEXT("Fax:MigrateUser9x:Divide by zero.\r\n")));
  219. break;
  220. default:
  221. TRACE((TEXT("Fax:MigrateUser9x:Unhandled exception.\r\n")));
  222. break;
  223. }
  224. return ERROR_SUCCESS;
  225. }
  226. }
  227. // MigrateSystem9x
  228. //
  229. // This routine copies system-wide settings.
  230. // It also takes care of writing the [Fax] section of the unattend file.
  231. //
  232. // Parameters:
  233. // Documented below.
  234. //
  235. // Returns:
  236. // ERROR_SUCCESS.
  237. //
  238. // Author:
  239. // Brian Dewey (t-briand) 1997-7-14
  240. LONG
  241. CALLBACK
  242. MigrateSystem9x(
  243. IN HWND ParentWnd, // Parent for UI.
  244. IN LPCSTR UnattendFile, // Name of unattend file
  245. LPVOID Reserved
  246. )
  247. {
  248. typedef struct tagANSWER {
  249. LPTSTR szKey;
  250. LPTSTR szValue;
  251. } ANSWER;
  252. // This array defines the information we need to add to the unattend.txt
  253. // file.
  254. ANSWER aAnswers[] = {
  255. { TEXT("Mode"), szPerformInstall }, // Must be "New" or "Skip".
  256. { TEXT("SetupType"), TEXT("Workstation") }, // Must be "Workstation".
  257. { TEXT("FaxPrinterName"), szFaxPrinterName }, // I'm guessing here.
  258. { TEXT("FaxNumber"), szFaxNumber }, // Must get this from a profile...
  259. // { TEXT("UseExchange"), TEXT("No") }, // Assume "No" Exchange Server.
  260. // { TEXT("ProfileName"), TEXT("") }, // And therefore no exchange profile.
  261. // { TEXT("RouteMail"), TEXT("No") }, // Don't deliver to an inbox.
  262. // { TEXT("RouteProfileName"), TEXT("") }, // Therefore, profile not needed.
  263. // { TEXT("Platforms"), TEXT("i386") }, // win95 => i386 (only platform!)
  264. // { TEXT("RoutePrint"), TEXT("No") }, // Default to "No" printing
  265. // { TEXT("RoutePrintername"), TEXT("") }, // And therefore no printer name.
  266. // { TEXT("AccountName"), TEXT("Administrator") }, // Run as local administrator.
  267. // { TEXT("Password"), TEXT("") }, // Password for that darn account.
  268. { TEXT("FaxPhone"), szFaxNumber }, // Again, pull this from a user.
  269. // { TEXT("RouteFolder"), TEXT("Yes") }, // Default "Yes".
  270. // { TEXT("FolderName"), szFaxStoreDir }, // Where we stick the stuff.
  271. // { TEXT("ServerName"), TEXT("") }, // Server irrelivant in NTW stand-alone.
  272. // { TEXT("SenderName"), szUserName }, // Pull from a user
  273. // { TEXT("SenderFaxAreaCode"), szFaxAreaCode }, // Pull from a user
  274. // { TEXT("SenderFaxNumber"), szFaxNumber } // Pull from a user
  275. };
  276. UINT i, // Loop index
  277. iMax; // Largest index.
  278. LPCTSTR szFaxSection = TEXT("Fax");
  279. TCHAR szFaxStoreDirRes[MAX_PATH];
  280. // Get the fax printer.
  281. GetFaxPrinterName(szFaxPrinterName, sizeof(szFaxPrinterName));
  282. // Figure out where to put the fax.
  283. // FIXBKD. This will need an incompatability message.
  284. _tcscpy(szFaxStoreDirRes, TEXT("uninitialized"));
  285. if(!LoadString(hinstMigDll,
  286. IDS_FAXSTORE,
  287. szFaxStoreDirRes,
  288. sizeof(szFaxStoreDirRes)
  289. )) {
  290. TRACE((TEXT("Unable to get fax storage directory; using default.\r\n")));
  291. DebugSystemError(GetLastError());
  292. TRACE((TEXT("I currently think the store dir is '%s'.\r\n"),
  293. szFaxStoreDirRes));
  294. _tcscpy(szFaxStoreDirRes, TEXT("%windir%\\FaxStore"));
  295. }
  296. ExpandEnvironmentStrings(szFaxStoreDirRes, szFaxStoreDir, sizeof(szFaxStoreDir));
  297. iMax = sizeof(aAnswers)/sizeof(ANSWER); // Compute size of array.
  298. for(i = 0; i < iMax; i++) {
  299. WritePrivateProfileString(
  300. szFaxSection,
  301. aAnswers[i].szKey,
  302. aAnswers[i].szValue,
  303. UnattendFile
  304. );
  305. }
  306. return ERROR_SUCCESS; // A very confused return value.
  307. }
  308. // ------------------------------------------------------------
  309. // Auxiliary functions
  310. // GetUserProfileName
  311. //
  312. // This function gets the name of the default MAPI profile for a user.
  313. //
  314. // Parameters:
  315. // hUser Pointer to the HKCU equivalent in setup.
  316. // lpProfName Pointer to buffer that will hold the profile name.
  317. // cbSize Size of said buffer.
  318. //
  319. // Returns:
  320. // TRUE on success, FALSE on failure.
  321. //
  322. // Author:
  323. // Brian Dewey (t-briand) 1997-8-6
  324. static
  325. BOOL
  326. GetUserProfileName(HKEY hUser, LPTSTR lpProfName, DWORD cbSize)
  327. {
  328. LONG lResult; // Result of API calls.
  329. HKEY hUserProf; // Key to the user profile section.
  330. DWORD dwType; // Holds the type of the data.
  331. TRACE((TEXT("In GetUserProfileName.\r\n")));
  332. lResult = RegOpenKeyEx(
  333. hUser, // Opening a user key...
  334. LPUSERPROF, // This section of the registry...
  335. 0, // Reserved; must be 0.
  336. KEY_READ, // Read permission,
  337. &hUserProf // Store the key here.
  338. );
  339. if(lResult != ERROR_SUCCESS) return FALSE; // We failed.
  340. lResult = RegQueryValueEx(
  341. hUserProf, // The key to the registry.
  342. LPPROFNAME, // Name of the value I want.
  343. NULL, // Reserved.
  344. &dwType, // Holds the type.
  345. lpProfName, // Holds the profile name.
  346. &cbSize // Size of the buffer.
  347. );
  348. RegCloseKey(hUserProf); // Remember to close the key!!
  349. TRACE((TEXT("Leaving GetUserProfileName.\r\n")));
  350. return lResult == ERROR_SUCCESS;
  351. }
  352. // GetFaxPrinterName
  353. //
  354. // This routine searches the win9x registry for a printer on port "FAX:". When it finds it,
  355. // it returns the name in the provided buffer.
  356. //
  357. // Parameters:
  358. // lpPrinterName Pointer to buffer that will hold the printer name.
  359. // cbSize Size of the buffer.
  360. //
  361. // Returns:
  362. // TRUE if a fax printer was found. FALSE otherwise. The data stored in
  363. // lpPrinterName is only valid on a return of TRUE.
  364. //
  365. // Author:
  366. // Brian Dewey (t-briand) 1997-8-6
  367. static BOOL
  368. GetFaxPrinterName(LPTSTR lpPrinterName, DWORD cbSize)
  369. {
  370. HKEY hPrinterSection; // Printer information in the registry.
  371. HKEY hPrinterKey; // Key to a specific printer.
  372. LONG lResult; // Result of api calls.
  373. DWORD dwIndex; // Index for enumeration.
  374. TCHAR szTempBuf[MAX_PATH]; // Temporary buffer for printer names.
  375. DWORD cbBufSize; // Size of said buffer.
  376. FILETIME ftTime; // What time was the key last modified?
  377. static const TCHAR LPPORT[] = TEXT("Port"); // The value we want to query.
  378. static const TCHAR LPFAX[] = TEXT("FAX:"); // What we're looking for.
  379. DWORD dwType; // Value type.
  380. TCHAR lpPortName[16]; // Should be amply big.
  381. DWORD cbPortSize; // Holds the size of the port name buffer.
  382. TRACE((TEXT("Attempting to get fax printer name.\r\n")));
  383. lResult = RegOpenKeyEx(
  384. HKEY_LOCAL_MACHINE, // Start here.
  385. LPPRINTER, // We're going for the printer section...
  386. 0, // Reserved.
  387. KEY_READ, // Get read permission
  388. &hPrinterSection // Put the key here.
  389. );
  390. if(lResult != ERROR_SUCCESS) {
  391. TRACE((TEXT("GetFaxPrinterName:Unable to open HKEY_LOCAL_MACHINE\\%s.\r\n"),
  392. LPPRINTER));
  393. return FALSE;
  394. }
  395. dwIndex = 0; // Start getting subkeys with the first.
  396. cbBufSize = sizeof(szTempBuf); // Initialize the size variable.
  397. while(RegEnumKeyEx(hPrinterSection, // Enumerate the printers.
  398. dwIndex++, // Keep incrementing the index.
  399. szTempBuf, // Store the name here.
  400. &cbBufSize, // This will hold the length of the name.
  401. NULL, // RESERVED.
  402. NULL, // Class info -- I don't need.
  403. NULL, // Size of class info -- also not needed.
  404. &ftTime) == ERROR_SUCCESS) {
  405. TRACE((TEXT("GetFaxPrinterName:Examining printer %s.\r\n"),
  406. szTempBuf));
  407. lResult = RegOpenKeyEx(
  408. hPrinterSection,
  409. szTempBuf, // Try to open this key.
  410. 0, // Reserved.
  411. KEY_READ, // Get read-only permission
  412. &hPrinterKey // Save the key here.
  413. );
  414. if(lResult != ERROR_SUCCESS) {
  415. RegCloseKey(hPrinterSection);
  416. return FALSE;
  417. }
  418. cbPortSize = sizeof(lpPortName);
  419. lResult = RegQueryValueEx(
  420. hPrinterKey, // Look in here...
  421. LPPORT, // The name of the desired value.
  422. NULL, // Reserved.
  423. &dwType, // Holds the type.
  424. lpPortName, // Store the value here.
  425. &cbPortSize // Get the size.
  426. );
  427. RegCloseKey(hPrinterKey);
  428. if(lResult != ERROR_SUCCESS) {
  429. TRACE((TEXT("GetFaxPrinterName:Unable to get the port information.\r\n")));
  430. RegCloseKey(hPrinterSection);
  431. return FALSE;
  432. }
  433. TRACE((TEXT("GetFaxPrinterName:Port = %s (looking for %s).\r\n"),
  434. lpPortName, LPFAX));
  435. if(!_tcsnicmp(LPFAX, lpPortName, cbPortSize)) {
  436. // Found it!
  437. TRACE((TEXT("GetFaxPrinterName:Success! Printer = %s.\r\n"),
  438. szTempBuf));
  439. RegCloseKey(hPrinterSection);
  440. _tcsncpy(lpPrinterName, szTempBuf, cbSize);
  441. return TRUE;
  442. }
  443. // If we get this far, then just try again.
  444. }
  445. // couldn't find a fax printer...
  446. TRACE((TEXT("GetFaxPrinterName:Unable to get fax printer.\r\n")));
  447. return FALSE;
  448. }
  449. // GetRegProfileKey
  450. //
  451. // OK, this is a horrible routine. Given a key to a user, and the name of
  452. // that user's MAPI profile, it will get a key to FAX service section of the
  453. // MAPI profile in the registry. The advantage of this is I can get MAPI properties
  454. // (such as user name, fax number, etc.) without using MAPI routines --
  455. // they come straight from the registry. But still, it seems like an awful
  456. // hack. I cringe. You can see me cringe in the comments below.
  457. //
  458. // Parameters:
  459. // hUser The HKCU equivalent for setup.
  460. // lpProfName Name of the user's default profile.
  461. // phRegProfileKey (OUT) Pointer to the FAX section of the MAPI profile.
  462. //
  463. // Returns:
  464. // TRUE on success, FALSE on failure.
  465. //
  466. // Author:
  467. // Brian Dewey (t-briand) 1997-8-6
  468. static BOOL
  469. GetRegProfileKey(HKEY hUser, LPTSTR lpProfName, PHKEY phRegProfileKey)
  470. {
  471. LONG lResult;
  472. HKEY hProfiles; // Key to all the profiles.
  473. HKEY hUserProf; // Key to the user's profile section
  474. BYTE abProfileUID[32]; // Holds the profile UID.
  475. DWORD cbProfileUID = sizeof(abProfileUID);
  476. TCHAR szProfileName[65]; // This will hold the encoded profile name.
  477. DWORD dwType; // Data type for registry query
  478. UINT i; // Loop index.
  479. TRACE((TEXT("In GetRegProfileKey.\r\n")));
  480. lResult = RegOpenKeyEx(
  481. hUser, // Opening a user key...
  482. LPPROFILES, // This section of the registry...
  483. 0, // Reserved; must be 0.
  484. KEY_READ, // Read permission,
  485. &hProfiles
  486. );
  487. if(lResult != ERROR_SUCCESS) return FALSE;
  488. lResult = RegOpenKeyEx(
  489. hProfiles, // Opening a user key...
  490. lpProfName, // This section of the registry...
  491. 0, // Reserved; must be 0.
  492. KEY_READ, // Read permission,
  493. &hUserProf
  494. );
  495. if(lResult != ERROR_SUCCESS) return FALSE;
  496. lResult = RegQueryValueEx(
  497. hUserProf, // From the current profile section,
  498. LPPROFUID, // Get the profile root uid.
  499. NULL,
  500. &dwType, // Holds the type of data
  501. abProfileUID, // Store the value here.
  502. &cbProfileUID // Holds the size.
  503. );
  504. if(lResult != ERROR_SUCCESS) {
  505. RegCloseKey(hUserProf);
  506. RegCloseKey(hProfiles);
  507. return FALSE;
  508. }
  509. // WARNING: The following is an incredibly horrid hack. I'm ashamed to write
  510. // code like this. But anyway, I think the way to get to the registry entries
  511. // associated with a mapi profile is to get the root UID of the profile, add 5 to
  512. // the first byte, ignore the last byte, and use that as a registry key. So that's
  513. // what I do. It works on the win95 box I've got in my office... probably the
  514. // best thing to do is to connect to MAPI, but I don't want to think of that
  515. // possibility yet...
  516. abProfileUID[0] += 4; // 5 is the magic offset???
  517. cbProfileUID--; // Chop off the extraneous byte at the end.
  518. for(i=0; i < cbProfileUID; i++)
  519. _stprintf(szProfileName + (i*2), TEXT("%0*x"), 2, abProfileUID[i]);
  520. lResult = RegOpenKeyEx(
  521. hUserProf, // This section...
  522. szProfileName, // The long MAPIUID...
  523. 0, // Reserved.
  524. KEY_READ, // Read permission.
  525. phRegProfileKey // Store the key here.
  526. );
  527. RegCloseKey(hUserProf); // Don't need this key any more!
  528. RegCloseKey(hProfiles); // Nor this one.
  529. TRACE((TEXT("Leaving GetRegProfileKey.\r\n")));
  530. return lResult == ERROR_SUCCESS;
  531. }
  532. // DumpUserInfo
  533. //
  534. // Writes user information out to 'faxuser.ini'.
  535. //
  536. // Parameters:
  537. // hUserInfo Pointer to the fax section of the user's profile.
  538. // UserName the user ID of this user.
  539. // szProfileName The MAPI profile name the user uses.
  540. //
  541. // Returns:
  542. // Nothing.
  543. //
  544. // Author:
  545. // Brian Dewey (t-briand) 1997-8-6
  546. static void
  547. DumpUserInfo(HKEY hUserInfo, LPCSTR UserName, LPTSTR szProfileName)
  548. {
  549. // Types
  550. typedef struct tagUSERINFO {
  551. DWORD dwPropID; // Property ID
  552. LPTSTR szDescription;
  553. } USERINFO;
  554. // Data
  555. USERINFO auiProperties[] = {
  556. { PR_POSTAL_ADDRESS, TEXT("Address") },
  557. { PR_COMPANY_NAME, TEXT("Company") },
  558. { PR_DEPARTMENT_NAME, TEXT("Department") },
  559. { PR_SENDER_EMAIL_ADDRESS, TEXT("FaxNumber") },
  560. { PR_SENDER_NAME, TEXT("FullName") },
  561. { PR_HOME_TELEPHONE_NUMBER, TEXT("HomePhone") },
  562. { PR_OFFICE_LOCATION, TEXT("Office") },
  563. { PR_OFFICE_TELEPHONE_NUMBER, TEXT("OfficePhone") },
  564. { PR_TITLE, TEXT("Title") }
  565. };
  566. TCHAR szPropStr[9]; // DWORD == 32 bits == 4 bytes == 8 hex digits + 1 null
  567. UINT iCount; // Loop counter.
  568. UINT iMax; // Largest property number.
  569. DWORD dwType; // Type of registry data
  570. BYTE abData[256]; // Data buffer.
  571. DWORD cbData; // Size of the data buffer.
  572. LONG lResult; // Result of API call.
  573. UINT i; // Loop counter.
  574. BOOL bMailboxFound; // Flag used when searching for a mailbox.
  575. TCHAR szUserBuf[9]; // used for annotating INF file.
  576. TRACE((TEXT("Entering DumpUserInfo.\r\n")));
  577. // Note that we're dumping this user's information.
  578. _stprintf(szUserBuf, "USER%04d", dwUserCount++);
  579. WritePrivateProfileString(
  580. TEXT("Users"),
  581. szUserBuf,
  582. UserName,
  583. szInfFileName
  584. );
  585. // Write the MAPI profile name.
  586. WritePrivateProfileString(
  587. TEXT(UserName), // this works???
  588. TEXT("MAPI"),
  589. szProfileName,
  590. szInfFileName
  591. );
  592. iMax = sizeof(auiProperties) / sizeof(USERINFO);
  593. TRACE((TEXT("There are %d properties.\r\n"), iMax));
  594. for(iCount = 0; iCount < iMax; iCount++) {
  595. _stprintf(szPropStr, TEXT("%0*x"), 8, SWAPWORD(auiProperties[iCount].dwPropID));
  596. cbData = sizeof(abData); // Reset the size.
  597. lResult = RegQueryValueEx(
  598. hUserInfo, // Get info from this key...
  599. szPropStr, // using this name.
  600. NULL, // reserved.
  601. &dwType, // Will store the data type.
  602. abData, // Data buffer.
  603. &cbData // Size of data buffer.
  604. );
  605. if(lResult == ERROR_SUCCESS) {
  606. // TODO: handle more data types!
  607. if(_tcscmp(auiProperties[iCount].szDescription, TEXT("FullName")) == 0) {
  608. // We've got the full name. Remember this for the unattend.txt
  609. // file.
  610. _tcscpy(szUserName, abData);
  611. }
  612. switch(dwType) {
  613. case REG_SZ:
  614. if(_tcscmp(auiProperties[iCount].szDescription, TEXT("FaxNumber")) == 0) {
  615. // We special case the fax number.
  616. // Why? Because it's either a fax number -OR-
  617. // it's "mailbox@faxnumber". I search for the "@" in
  618. // the string. If it's there, everything before it goes
  619. // into the mailbox property, everything after goes into
  620. // the fax number. If it's not there, I assume the entire
  621. // string is a fax number.
  622. i = 0;
  623. bMailboxFound = FALSE;
  624. while(abData[i] != _T('\0')) {
  625. if(abData[i] == _T('@')) {
  626. // Found it!
  627. abData[i] = _T('\0');// Null terminate the mailbox name.
  628. if(!WritePrivateProfileString(
  629. TEXT(UserName),
  630. TEXT("Mailbox"),
  631. abData,
  632. szInfFileName
  633. )) {
  634. DebugSystemError(GetLastError());
  635. }
  636. if(!WritePrivateProfileString(
  637. TEXT(UserName),
  638. TEXT("FaxNumber"),
  639. abData + i + 1, // Print what was after the '@'.
  640. szInfFileName
  641. )) {
  642. DebugSystemError(GetLastError());
  643. }
  644. SetGlobalFaxNumberInfo(abData + i + 1);
  645. bMailboxFound = TRUE;
  646. break; // while
  647. }
  648. i++;
  649. }
  650. if(bMailboxFound)
  651. break; // case -- if we found the mailbox, we're done.
  652. // else we fall through to default processing.
  653. else
  654. SetGlobalFaxNumberInfo(abData);
  655. }// if
  656. // Replace '\n' characters in the string with semicolons.
  657. i = 0;
  658. while(abData[i] != _T('\0')) {
  659. if((abData[i] == _T('\n')) || (abData[i] == _T('\r')))
  660. abData[i] = _T(';');
  661. i++;
  662. }
  663. if(!WritePrivateProfileString(
  664. TEXT(UserName),
  665. auiProperties[iCount].szDescription,
  666. abData,
  667. szInfFileName
  668. )) {
  669. DebugSystemError(GetLastError());
  670. }
  671. TRACE((TEXT("%s = %s\r\n"), auiProperties[iCount].szDescription,
  672. abData));
  673. break;
  674. case REG_BINARY:
  675. // The data is just free-form binary. Print it one byte at a time.
  676. TRACE((TEXT("%s = "), auiProperties[iCount].szDescription));
  677. for(i = 0; i < cbData; i++)
  678. TRACE((TEXT("%0*x "), 2, abData[i]));
  679. TRACE((TEXT("\r\n")));
  680. break;
  681. default:
  682. TRACE((TEXT("Unknown data type (%d) for property '%s'.\r\n"),
  683. dwType,
  684. auiProperties[iCount].szDescription));
  685. }
  686. } else {
  687. TRACE((TEXT("Could not get property '%s'.\r\n"),
  688. auiProperties[iCount].szDescription));
  689. }
  690. }
  691. TRACE((TEXT("Leaving DumpUserInfo.\r\n")));
  692. }
  693. // SetGlobalFaxNumberInfo
  694. //
  695. // This routine sets the global variables 'szFaxAreaCode' and 'szFaxNumber' based on
  696. // the value in szPhone. It expects szPhone to be in the following format:
  697. //
  698. // [[<country code>] '(' <area code> ')'] <phone number>
  699. //
  700. // (Brackets denote something optional. Literals are in single quotes, non-terminals are
  701. // in angle brackets. Note that if there's a country code, there must be an area code.)
  702. //
  703. // Parameters:
  704. // szPhone Described above.
  705. //
  706. // Returns:
  707. // Nothing.
  708. //
  709. // Side effects:
  710. // Sets the values of szFaxAreaCode and szFaxNumber.
  711. //
  712. // Author:
  713. // Brian Dewey (t-briand) 1997-7-24
  714. static void
  715. SetGlobalFaxNumberInfo(LPCTSTR szPhone)
  716. {
  717. UINT i; // Loop index.
  718. UINT j; // Loop index.
  719. // First, look through the string for an area code.
  720. i = 0;
  721. while((szPhone[i] != _T('\0')) && (szPhone[i] != _T('(')))
  722. i++;
  723. if(szPhone[i] == _T('(')) {
  724. // We've found an area code!
  725. // are all area codes at most 3 digits?? I sized the buffer to 16, but this will
  726. // still AV on a badly-formed #.
  727. i++;
  728. j=0;
  729. while(szPhone[i] != _T(')')) {
  730. szFaxAreaCode[j] = szPhone[i];
  731. i++;
  732. j++;
  733. }
  734. i++;
  735. // szPhone[i] should now immediately after the ')' at the end
  736. // of the area code. Everything from here on out is a phone number.
  737. while(_istspace(szPhone[i])) i++;
  738. } else {
  739. // If we're here, there was no area code. We need to rewind either to
  740. // the beginning of the string or to the first whitespace.
  741. while((i >= 0) && (!_istspace(szPhone[i]))) {
  742. i--;
  743. }
  744. i++; // The loop always rewinds one too far.
  745. }
  746. // ASSERT: We're now ready to begin copying from szPhone to
  747. // szFaxNumber.
  748. j = 0;
  749. while(szPhone[i] != '\0') {
  750. szFaxNumber[j] = szPhone[i];
  751. i++;
  752. j++;
  753. }
  754. }
  755. // GenerateIncompatableMessages
  756. //
  757. // This function writes incompatability messages to the MIGRATE.INF file.
  758. //
  759. // Parameters:
  760. // None.
  761. //
  762. // Returns:
  763. // Nothing.
  764. //
  765. // Author:
  766. // Brian Dewey (t-briand) 1997-8-1
  767. static void
  768. GenerateIncompatableMessages()
  769. {
  770. return;
  771. }
  772. // InitializeInfFile
  773. //
  774. // This routine writes out the [Version] section of the inf file.
  775. //
  776. // Parameters:
  777. // None.
  778. //
  779. // Returns:
  780. // TRUE on success, FALSE on failure.
  781. //
  782. // Side effects:
  783. // Generates a fully-qualified file name in szInfFileName. Currently, that's
  784. // given by <windows dir>\<base file name>.
  785. //
  786. // Author:
  787. // Brian Dewey (t-briand) 1997-8-5
  788. static BOOL
  789. InitializeInfFile(LPCTSTR WorkingDirectory)
  790. {
  791. TCHAR szWindowsPath[MAX_PATH]; // This will hold the path to the windows directory.
  792. DWORD cbPathSize = sizeof(szWindowsPath);
  793. TCHAR szDriveLetter[2]; // Will hold the drive letter.
  794. // First, fully qualify the file name.
  795. if(!GetWindowsDirectory(szWindowsPath, cbPathSize)) {
  796. TRACE((TEXT("InitializeInfFile:Unable to get the windows directory!\r\n")));
  797. return FALSE; // It must be serious if that system call failed.
  798. }
  799. szDriveLetter[0] = szWindowsPath[0];
  800. szDriveLetter[1] = 0;
  801. _stprintf(szInfFileName, TEXT("%s\\%s"), WorkingDirectory, szInfFileBase);
  802. TRACE((TEXT("InitializeInfFile:Will store all information in INF file = '%s'\r\n"),
  803. szInfFileName));
  804. // Now, put the version header on the inf file.
  805. WritePrivateProfileString(
  806. TEXT("Version"),
  807. TEXT("Signature"),
  808. TEXT("\"$WINDOWS NT$\""),
  809. szInfFileName
  810. );
  811. // now, write out the amount of space we'll need. Currently, we
  812. // just put the awdvstub.exe program in the SystemRoot directory.
  813. // Even w/ symbols, that's under 500K. Report that.
  814. WritePrivateProfileString(
  815. TEXT("NT Disk Space Requirements"),
  816. szDriveLetter,
  817. TEXT("500000"),
  818. szInfFileName
  819. );
  820. return TRUE;
  821. }