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.

1388 lines
48 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. Mooly Beery (moolyb) 2000-12-20
  8. --*/
  9. #include <windows.h>
  10. #include <setupapi.h>
  11. #include <shellapi.h>
  12. #include <mapidefs.h>
  13. #include <mapitags.h> // To get the property definitions.
  14. #include <stdio.h>
  15. #include <tchar.h>
  16. #include "migrate.h" // Contains prototypes & version information.
  17. #include "property.h" // Stolen from Elliott -- contains their fax properties
  18. #include "resource.h" // Migration resources.
  19. #include "faxutil.h"
  20. #include "FaxSetup.h"
  21. #include "FaxReg.h"
  22. // ------------------------------------------------------------
  23. // Defines & macros
  24. #define SWAPWORD(x) (((x) << 16) | ((x) >> 16))
  25. //
  26. // Fax Applications will be blocked by the Upgrade and required to be removed.
  27. // Save them in the Registry before that.
  28. //
  29. #define REGKEYUPG_INSTALLEDFAX _T("Software\\Microsoft\\FaxUpgrade")
  30. // ------------------------------------------------------------
  31. // Internal data
  32. // First, this is the name of the INF file that we generate.
  33. static TCHAR szInfFileBase[] = TEXT("migrate.inf");
  34. TCHAR szInfFileName[MAX_PATH]; // This will be the fully qualified path of the above.
  35. static char lpWorkingDir[MAX_PATH]; // This is our working directory.
  36. static TCHAR szDoInstall[4]; // Will be either "No" or "Yes".
  37. static TCHAR szFaxAreaCode[16]; // Contains the fax modem area code.
  38. static TCHAR szFaxNumber[9]; // Fax # w/o area or country code.
  39. static TCHAR szNTProfileName[MAX_PATH]; // Profile to use for routing.
  40. static TCHAR szFaxStoreDir[MAX_PATH]; // Folder to use for routing.
  41. static TCHAR szUserName[MAX_PATH]; // This will be the user's name who owns the fax service.
  42. static TCHAR szUserID[MAX_PATH]; // This is the login name of the user who owns the fax.
  43. static LPCTSTR REG_KEY_AWF_LOCAL_MODEMS = TEXT("SOFTWARE\\Microsoft\\At Work Fax\\Local Modems");
  44. static LPCTSTR REG_KEY_AWF_INSTALLED = TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MSAWFax");
  45. // The following are section names from the Microsoft registry.
  46. // They're used to find the fax profile for a user.
  47. static const LPTSTR LPUSERPROF = TEXT("Software\\Microsoft\\Windows Messaging Subsystem\\Profiles");
  48. static const LPTSTR LPPROFNAME = TEXT("DefaultProfile");
  49. // The following's part of the path to the Exchange profile in question.
  50. static const LPTSTR LPPROFILES = TEXT("Software\\Microsoft\\Windows Messaging Subsystem\\Profiles");
  51. // This is how we get the root UID.
  52. static const LPTSTR LPPROFUID = TEXT("Profile UID");
  53. // This is the name we use for the logon user section of 'faxuser.ini'
  54. LPCTSTR lpLogonUser = TEXT("Logon User");
  55. // This keeps track of the number of users migrated. Used to make annotations
  56. // in the INF file.
  57. static DWORD dwUserCount = 0;
  58. // ------------------------------------------------------------
  59. // Internal function prototypes
  60. static BOOL GetUserProfileName(HKEY hUser, LPTSTR lpProfName, DWORD cbSize);
  61. static BOOL GetRegProfileKey(HKEY hUser, LPTSTR lpProfName, PHKEY phRegProfileKey);
  62. static void DumpUserInfo(HKEY hUserInfo, LPCSTR UserName, LPTSTR szProfileName,IN LPCSTR UnattendFile);
  63. static void SetGlobalFaxNumberInfo(LPCTSTR szPhone);
  64. static BOOL InitializeInfFile(LPCTSTR WorkingDirectory);
  65. static BOOL IsAWFInstalled();
  66. static DWORD MigrateDevices9X(IN LPCSTR UnattendFile);
  67. static DWORD CopyCoverPageFiles9X();
  68. static DWORD RememberInstalledFax(IN DWORD dwFaxInstalled);
  69. static DWORD MigrateUninstalledFax(IN LPCTSTR lpctstrUnattendFile, OUT bool *pbFaxWasInstalled);
  70. VENDORINFO VendorInfo;
  71. // QueryVersion
  72. //
  73. // This routine returns version information about the migration DLL.
  74. //
  75. // Parameters:
  76. // Commented below.
  77. //
  78. // Returns:
  79. // ERROR_SUCCESS.
  80. //
  81. // Author:
  82. // Brian Dewey (t-briand) 1997-7-23
  83. LONG
  84. CALLBACK
  85. QueryVersion
  86. (
  87. OUT LPCSTR *ProductID, // Unique identifier string.
  88. OUT LPUINT DllVersion, // Version number. Cannot be zero.
  89. OUT LPINT *CodePageArray, // OPTIONAL. Language dependencies.
  90. OUT LPCSTR *ExeNamesBuf, // OPTIONAL. Executables to look for.
  91. OUT PVENDORINFO *ppVendorInfo
  92. )
  93. {
  94. int iRes = 0;
  95. DWORD dwErr = ERROR_SUCCESS;
  96. DEBUG_FUNCTION_NAME(_T("QueryVersion"));
  97. if (ProductID)
  98. {
  99. *ProductID = "Microsoft Fax";
  100. }
  101. if (DllVersion)
  102. {
  103. *DllVersion = FAX_MIGRATION_VERSION;
  104. }
  105. if (CodePageArray)
  106. {
  107. *CodePageArray = NULL; // No language dependencies
  108. }
  109. if (ExeNamesBuf)
  110. {
  111. *ExeNamesBuf = NULL;
  112. }
  113. if (ppVendorInfo)
  114. {
  115. *ppVendorInfo = &VendorInfo;
  116. }
  117. iRes = LoadString( hinstMigDll,
  118. MSG_VI_COMPANY_NAME,
  119. &VendorInfo.CompanyName[0],
  120. sizeof(VendorInfo.CompanyName));
  121. if ((iRes==0) && (dwErr=GetLastError()))
  122. {
  123. DebugPrintEx(DEBUG_ERR,"LoadString MSG_VI_COMPANY_NAME failed (ec=%d)",dwErr);
  124. }
  125. iRes = LoadString( hinstMigDll,
  126. MSG_VI_SUPPORT_NUMBER,
  127. &VendorInfo.SupportNumber[0],
  128. sizeof(VendorInfo.SupportNumber));
  129. if ((iRes==0) && (dwErr=GetLastError()))
  130. {
  131. DebugPrintEx(DEBUG_ERR,"LoadString MSG_VI_SUPPORT_NUMBER failed (ec=%d)",dwErr);
  132. }
  133. iRes = LoadString( hinstMigDll,
  134. MSG_VI_SUPPORT_URL,
  135. &VendorInfo.SupportUrl[0],
  136. sizeof(VendorInfo.SupportUrl));
  137. if ((iRes==0) && (dwErr=GetLastError()))
  138. {
  139. DebugPrintEx(DEBUG_ERR,"LoadString MSG_VI_SUPPORT_URL failed (ec=%d)",dwErr);
  140. }
  141. iRes = LoadString( hinstMigDll,
  142. MSG_VI_INSTRUCTIONS,
  143. &VendorInfo.InstructionsToUser[0],
  144. sizeof(VendorInfo.InstructionsToUser));
  145. if ((iRes==0) && (dwErr=GetLastError()))
  146. {
  147. DebugPrintEx(DEBUG_ERR,"LoadString MSG_VI_INSTRUCTIONS failed (ec=%d)",dwErr);
  148. }
  149. return ERROR_SUCCESS;
  150. }
  151. // Initialize9x
  152. //
  153. // This is called to initialize the migration process. See the migration dll
  154. // spec for more details.
  155. //
  156. // Parameters:
  157. // Commented below.
  158. //
  159. // Author:
  160. // Brian Dewey (t-briand) 1997-7-14
  161. LONG
  162. CALLBACK
  163. Initialize9x
  164. (
  165. IN LPCSTR WorkingDirectory, // Place to store files.
  166. IN LPCSTR SourceDirectories, // Location of the Windows NT source.
  167. IN LPCSTR MediaDirectory // Path to the original media directory
  168. )
  169. {
  170. DEBUG_FUNCTION_NAME(_T("Initialize9x"));
  171. DebugPrintEx(DEBUG_MSG, "Working directory is %s", WorkingDirectory);
  172. DebugPrintEx(DEBUG_MSG, "Source directories is %s", SourceDirectories); // will show only first ?
  173. DebugPrintEx(DEBUG_MSG, "Media directory is %s", MediaDirectory);
  174. InitializeInfFile(WorkingDirectory);
  175. strncpy(lpWorkingDir, WorkingDirectory, MAX_PATH);
  176. return ERROR_SUCCESS; // A very confused return value.
  177. }
  178. // MigrateUser9x
  179. //
  180. // This routine records the fax information specific to a user.
  181. //
  182. // Parameters:
  183. // Documented below.
  184. //
  185. // Returns:
  186. // ERROR_SUCCESS.
  187. //
  188. // Author:
  189. // Brian Dewey (t-briand) 1997-7-14
  190. LONG
  191. CALLBACK
  192. MigrateUser9x
  193. (
  194. IN HWND ParentWnd, // Parent (if need a UI)
  195. IN LPCSTR UnattendFile, // Name of unattend file
  196. IN HKEY UserRegKey, // Key to this user's registry settings.
  197. IN LPCSTR UserName, // Account name of user.
  198. LPVOID Reserved
  199. )
  200. {
  201. TCHAR szProfileName[MAX_PATH]; // Holds the name of this user's profile.
  202. HKEY hRegProfileKey; // The fax profile key in the registry.
  203. DWORD dwExceptCode; // Exception error code
  204. DEBUG_FUNCTION_NAME(_T("MigrateUser9x"));
  205. DebugPrintEx(DEBUG_MSG,"Unattend File is %s",UnattendFile);
  206. DebugPrintEx(DEBUG_MSG,"User Name is %s",UserName);
  207. __try
  208. {
  209. // @@@ This function gets the name of the default MAPI profile for a user.
  210. if (GetUserProfileName(UserRegKey, szProfileName, sizeof(szProfileName)))
  211. {
  212. DebugPrintEx(DEBUG_MSG,"Profile name = %s",szProfileName);
  213. // @@@ Given a key to a user, and the name of that user's MAPI profile
  214. // @@@ it will get a key to FAX service section of the MAPI profile in the registry
  215. if (GetRegProfileKey(UserRegKey, szProfileName, &hRegProfileKey))
  216. {
  217. // We now know we want to do an installation.
  218. DebugPrintEx(DEBUG_MSG,"Successfully got profile information.");
  219. _tcscpy(szNTProfileName, szProfileName); // Remember this name for NT.
  220. // NULL means the logon user...
  221. if (UserName != NULL)
  222. {
  223. _tcscpy(szUserID, UserName); // Remember the ID for the unattend.txt file.
  224. }
  225. else
  226. {
  227. _tcscpy(szUserID, lpLogonUser); // Use the logon user name.
  228. }
  229. // @@@ Writes user information out to the INF
  230. DumpUserInfo(hRegProfileKey, szUserID, szProfileName,UnattendFile);
  231. RegCloseKey(hRegProfileKey);
  232. }
  233. else
  234. {
  235. DebugPrintEx(DEBUG_WRN,"Could not get profile information.");
  236. return ERROR_NOT_INSTALLED;
  237. }
  238. }
  239. else
  240. {
  241. DebugPrintEx(DEBUG_WRN,"Could not find profile name.");
  242. return ERROR_NOT_INSTALLED;
  243. }
  244. return ERROR_SUCCESS; // A very confused return value.
  245. }
  246. __except(EXCEPTION_EXECUTE_HANDLER)
  247. {
  248. dwExceptCode = GetExceptionCode();
  249. switch(dwExceptCode)
  250. {
  251. case EXCEPTION_ACCESS_VIOLATION: DebugPrintEx(DEBUG_ERR,"Access violation.");
  252. break;
  253. case EXCEPTION_INT_DIVIDE_BY_ZERO:
  254. case EXCEPTION_FLT_DIVIDE_BY_ZERO: DebugPrintEx(DEBUG_ERR,"Divide by zero.");
  255. break;
  256. default: DebugPrintEx(DEBUG_ERR,"Unhandled exception.");
  257. break;
  258. }
  259. return ERROR_SUCCESS;
  260. }
  261. }
  262. // MigrateSystem9x
  263. //
  264. // This routine copies system-wide settings.
  265. // It also takes care of writing the [Fax] section of the unattend file.
  266. //
  267. // Parameters:
  268. // Documented below.
  269. //
  270. // Returns:
  271. // ERROR_SUCCESS.
  272. //
  273. // Author:
  274. // Brian Dewey (t-briand) 1997-7-14
  275. LONG
  276. CALLBACK
  277. MigrateSystem9x
  278. (
  279. IN HWND ParentWnd, // Parent for UI.
  280. IN LPCSTR UnattendFile, // Name of unattend file
  281. LPVOID Reserved
  282. )
  283. {
  284. DWORD dwReturn = NO_ERROR;
  285. DWORD dwFaxInstalled = 0;
  286. DEBUG_FUNCTION_NAME(_T("MigrateSystem9x"));
  287. //
  288. // Check if SBS 5.0 / .NET SB3 / .NET RC1 Fax Client are present
  289. //
  290. dwReturn = CheckInstalledFax((FXSTATE_SBS5_CLIENT | FXSTATE_BETA3_CLIENT | FXSTATE_DOTNET_CLIENT), &dwFaxInstalled);
  291. if (dwReturn != NO_ERROR)
  292. {
  293. DebugPrintEx(DEBUG_WRN, _T("CheckInstalledFaxClient() failed, ec=%ld. Suppose that nothing is installed."), dwReturn);
  294. }
  295. //
  296. // if any of these applications are found on the machine,
  297. // the upgrade will be blocked through MigDB.inf
  298. // and the user will be required to uninstall them.
  299. //
  300. // but we want to remember the fact that they were present on the machine.
  301. // we do it by writting to the registry.
  302. // after the upgrade will be restarted, we put this data into the unattended file,
  303. // and by this FaxOcm gets this data.
  304. //
  305. if (dwFaxInstalled != FXSTATE_NONE)
  306. {
  307. dwReturn = RememberInstalledFax(dwFaxInstalled);
  308. if (dwReturn != NO_ERROR)
  309. {
  310. DebugPrintEx(DEBUG_WRN, _T("RememberInstalledFax() failed, ec=%ld."), dwReturn);
  311. }
  312. else
  313. {
  314. DebugPrintEx(DEBUG_MSG, _T("RememberInstalledFax() succeded."));
  315. }
  316. //
  317. // we can go out ==> Upgrade will be blocked anyway.
  318. //
  319. return ERROR_SUCCESS;
  320. }
  321. //
  322. // Any of the applications is not installed.
  323. // Check if they were here before. If yes, then write this fact to the unattended file.
  324. //
  325. bool bFaxWasInstalled = false;
  326. dwReturn = MigrateUninstalledFax(UnattendFile, &bFaxWasInstalled);
  327. if (dwReturn != NO_ERROR)
  328. {
  329. DebugPrintEx(DEBUG_WRN, _T("MigrateUninstalledFax() failed, ec=%ld."), dwReturn);
  330. }
  331. //
  332. // If SBS 5.0 Client or AWF is installed, we need to set FAX=ON in Unattended.txt
  333. //
  334. BOOL bAWFInstalled = IsAWFInstalled();
  335. if (bFaxWasInstalled || bAWFInstalled)
  336. {
  337. //
  338. // force installation of the Fax component in Whistler.
  339. //
  340. if (!WritePrivateProfileString("Components", UNATTEND_FAX_SECTION, "ON", UnattendFile))
  341. {
  342. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString Components failed (ec=%d)",GetLastError());
  343. }
  344. else
  345. {
  346. DebugPrintEx(DEBUG_MSG, _T("Set FAX=ON in UnattendFile."));
  347. }
  348. }
  349. else
  350. {
  351. DebugPrintEx(DEBUG_WRN, _T("Neither AWF not SBS 50 or XP DL Client is installed."));
  352. return ERROR_NOT_INSTALLED;
  353. }
  354. if (bAWFInstalled)
  355. {
  356. //
  357. // Continue Migration of AWF
  358. //
  359. if (MigrateDevices9X(UnattendFile)!=ERROR_SUCCESS)
  360. {
  361. DebugPrintEx(DEBUG_ERR,"MigrateDevices9X failed (ec=%d)",GetLastError());
  362. }
  363. if (CopyCoverPageFiles9X()!=ERROR_SUCCESS)
  364. {
  365. DebugPrintEx(DEBUG_ERR,"CopyCoverPageFiles9X failed (ec=%d)",GetLastError());
  366. }
  367. }
  368. return ERROR_SUCCESS; // A very confused return value.
  369. }
  370. // ------------------------------------------------------------
  371. // Auxiliary functions
  372. // GetUserProfileName
  373. //
  374. // This function gets the name of the default MAPI profile for a user.
  375. //
  376. // Parameters:
  377. // hUser Pointer to the HKCU equivalent in setup.
  378. // lpProfName Pointer to buffer that will hold the profile name.
  379. // cbSize Size of said buffer.
  380. //
  381. // Returns:
  382. // TRUE on success, FALSE on failure.
  383. //
  384. // Author:
  385. // Brian Dewey (t-briand) 1997-8-6
  386. static
  387. BOOL
  388. GetUserProfileName(HKEY hUser, LPTSTR lpProfName, DWORD cbSize)
  389. {
  390. LONG lResult; // Result of API calls.
  391. HKEY hUserProf; // Key to the user profile section.
  392. DWORD dwType; // Holds the type of the data.
  393. DEBUG_FUNCTION_NAME(_T("GetUserProfileName"));
  394. lResult = RegOpenKeyEx( hUser, // Opening a user key...
  395. LPUSERPROF, // This section of the registry...
  396. 0, // Reserved; must be 0.
  397. KEY_READ, // Read permission,
  398. &hUserProf); // Store the key here.
  399. if (lResult!=ERROR_SUCCESS)
  400. {
  401. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",LPUSERPROF,GetLastError());
  402. return FALSE; // We failed.
  403. }
  404. lResult = RegQueryValueEx( hUserProf, // The key to the registry.
  405. LPPROFNAME, // Name of the value I want.
  406. NULL, // Reserved.
  407. &dwType, // Holds the type.
  408. LPBYTE(lpProfName), // Holds the profile name.
  409. &cbSize); // Size of the buffer.
  410. if (lResult!=ERROR_SUCCESS)
  411. {
  412. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx %s failed (ec=%d)",LPPROFNAME,GetLastError());
  413. }
  414. RegCloseKey(hUserProf); // Remember to close the key!!
  415. return (lResult==ERROR_SUCCESS);
  416. }
  417. // GetRegProfileKey
  418. //
  419. // OK, this is a horrible routine. Given a key to a user, and the name of
  420. // that user's MAPI profile, it will get a key to FAX service section of the
  421. // MAPI profile in the registry. The advantage of this is I can get MAPI properties
  422. // (such as user name, fax number, etc.) without using MAPI routines --
  423. // they come straight from the registry. But still, it seems like an awful
  424. // hack. I cringe. You can see me cringe in the comments below.
  425. //
  426. // Parameters:
  427. // hUser The HKCU equivalent for setup.
  428. // lpProfName Name of the user's default profile.
  429. // phRegProfileKey (OUT) Pointer to the FAX section of the MAPI profile.
  430. //
  431. // Returns:
  432. // TRUE on success, FALSE on failure.
  433. //
  434. // Author:
  435. // Brian Dewey (t-briand) 1997-8-6
  436. static BOOL
  437. GetRegProfileKey(HKEY hUser, LPTSTR lpProfName, PHKEY phRegProfileKey)
  438. {
  439. HKEY hProfiles = NULL;
  440. HKEY hUserProf = NULL;
  441. UINT iIndex = 0;
  442. DWORD dwErr = ERROR_SUCCESS;
  443. TCHAR szProfileName[MAX_PATH+1] = {0};
  444. DWORD dwType = 0;
  445. BYTE abData[MAX_PATH] = {0};
  446. DWORD cbData = 0;
  447. DEBUG_FUNCTION_NAME(_T("GetRegProfileKey"));
  448. dwErr = RegOpenKeyEx( hUser, // Opening a user key...
  449. LPPROFILES, // This section of the registry...
  450. 0, // Reserved; must be 0.
  451. KEY_READ, // Read permission,
  452. &hProfiles);
  453. if (dwErr!=ERROR_SUCCESS)
  454. {
  455. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",LPPROFILES,dwErr);
  456. goto exit;
  457. }
  458. dwErr = RegOpenKeyEx( hProfiles, // Opening a user key...
  459. lpProfName, // This section of the registry...
  460. 0, // Reserved; must be 0.
  461. KEY_READ, // Read permission,
  462. &hUserProf);
  463. if (dwErr!=ERROR_SUCCESS)
  464. {
  465. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",lpProfName,dwErr);
  466. goto exit;
  467. }
  468. // enumerate all subkeys and find the one that belongs to our transport provider
  469. while (dwErr!=ERROR_NO_MORE_ITEMS)
  470. {
  471. // get one subkey
  472. dwErr = RegEnumKey(hUserProf,iIndex++,szProfileName,MAX_PATH+1);
  473. if (dwErr!=ERROR_SUCCESS)
  474. {
  475. DebugPrintEx(DEBUG_ERR,"RegEnumKey failed (ec=%d)",dwErr);
  476. goto exit;
  477. }
  478. // open it
  479. dwErr = RegOpenKeyEx(hUserProf,szProfileName,0,KEY_READ,phRegProfileKey);
  480. if (dwErr!=ERROR_SUCCESS)
  481. {
  482. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",szProfileName,dwErr);
  483. goto exit;
  484. }
  485. cbData = sizeof(abData); // Reset the size.
  486. dwErr = RegQueryValueEx((*phRegProfileKey),
  487. "001E300A",
  488. NULL,
  489. &dwType,
  490. abData,
  491. &cbData);
  492. if (dwErr==ERROR_SUCCESS)
  493. {
  494. if (strcmp((char*)abData,"awfaxp.dll")==0)
  495. {
  496. // found it
  497. DebugPrintEx(DEBUG_MSG,"Found our Transport provider");
  498. goto exit;
  499. }
  500. }
  501. else if (dwErr!=ERROR_FILE_NOT_FOUND)
  502. {
  503. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx failed (ec=%d)",dwErr);
  504. RegCloseKey((*phRegProfileKey));
  505. goto exit;
  506. }
  507. dwErr = ERROR_SUCCESS;
  508. RegCloseKey((*phRegProfileKey));
  509. }
  510. exit:
  511. if (hUserProf)
  512. {
  513. RegCloseKey(hUserProf);
  514. }
  515. if (hProfiles)
  516. {
  517. RegCloseKey(hProfiles);
  518. }
  519. return (dwErr==ERROR_SUCCESS);
  520. }
  521. #define PR_NUMBER_OF_RETRIES 0x45080002
  522. #define PR_TIME_BETWEEN_RETRIES 0x45090002
  523. // DumpUserInfo
  524. //
  525. // Writes user information out to 'faxuser.ini'.
  526. //
  527. // Parameters:
  528. // hUserInfo Pointer to the fax section of the user's profile.
  529. // UserName the user ID of this user.
  530. // szProfileName The MAPI profile name the user uses.
  531. //
  532. // Returns:
  533. // Nothing.
  534. //
  535. // Author:
  536. // Brian Dewey (t-briand) 1997-8-6
  537. static void
  538. DumpUserInfo(HKEY hUserInfo, LPCSTR UserName, LPTSTR szProfileName,IN LPCSTR UnattendFile)
  539. {
  540. // Types
  541. typedef struct tagUSERINFO {
  542. DWORD dwPropID; // Property ID
  543. LPTSTR szDescription;
  544. } USERINFO;
  545. // Data
  546. USERINFO auiProperties[] =
  547. {
  548. { PR_POSTAL_ADDRESS, TEXT("Address") },
  549. { PR_COMPANY_NAME, TEXT("Company") },
  550. { PR_DEPARTMENT_NAME, TEXT("Department") },
  551. { PR_SENDER_EMAIL_ADDRESS, TEXT("FaxNumber") },
  552. { PR_SENDER_NAME, TEXT("FullName") },
  553. { PR_HOME_TELEPHONE_NUMBER, TEXT("HomePhone") },
  554. { PR_OFFICE_LOCATION, TEXT("Office") },
  555. { PR_OFFICE_TELEPHONE_NUMBER, TEXT("OfficePhone") },
  556. { PR_TITLE, TEXT("Title") },
  557. { PR_NUMBER_OF_RETRIES, TEXT("NumberOfRetries") },
  558. { PR_TIME_BETWEEN_RETRIES, TEXT("TimeBetweenRetries") },
  559. };
  560. TCHAR szPropStr[9]; // DWORD == 32 bits == 4 bytes == 8 hex digits + 1 null
  561. UINT iCount; // Loop counter.
  562. UINT iMax; // Largest property number.
  563. DWORD dwType; // Type of registry data
  564. DWORD dwCount;
  565. BYTE abData[256]; // Data buffer.
  566. DWORD cbData; // Size of the data buffer.
  567. LONG lResult; // Result of API call.
  568. INT i; // Loop counter.
  569. TCHAR szUserBuf[9]; // used for annotating INF file.
  570. TCHAR szBinaryBuf[MAX_PATH];
  571. TCHAR* pszSeperator = NULL;
  572. DEBUG_FUNCTION_NAME(_T("DumpUserInfo"));
  573. // Note that we're dumping this user's information.
  574. _stprintf(szUserBuf, "USER%04d", dwUserCount++);
  575. if (!WritePrivateProfileString( TEXT("Users"),
  576. szUserBuf,
  577. UserName,
  578. szInfFileName))
  579. {
  580. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  581. }
  582. // Write the MAPI profile name.
  583. if (!WritePrivateProfileString( TEXT(UserName), // this works???
  584. TEXT("MAPI"),
  585. szProfileName,
  586. szInfFileName))
  587. {
  588. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  589. }
  590. iMax = sizeof(auiProperties) / sizeof(USERINFO);
  591. DebugPrintEx(DEBUG_MSG,"There are %d properties.",iMax);
  592. for (iCount = 0; iCount < iMax; iCount++)
  593. {
  594. _stprintf(szPropStr, TEXT("%0*x"), 8, SWAPWORD(auiProperties[iCount].dwPropID));
  595. cbData = sizeof(abData); // Reset the size.
  596. lResult = RegQueryValueEx( hUserInfo, // Get info from this key...
  597. szPropStr, // using this name.
  598. NULL, // reserved.
  599. &dwType, // Will store the data type.
  600. abData, // Data buffer.
  601. &cbData); // Size of data buffer.
  602. if (lResult==ERROR_SUCCESS)
  603. {
  604. // TODO: handle more data types!
  605. if (_tcscmp(auiProperties[iCount].szDescription, TEXT("FullName")) == 0)
  606. {
  607. // We've got the full name. Remember this for the unattend.txt
  608. // file.
  609. _tcscpy(szUserName, LPTSTR(abData));
  610. }
  611. switch(dwType)
  612. {
  613. case REG_SZ:
  614. if (_tcscmp(auiProperties[iCount].szDescription, TEXT("FaxNumber")) == 0)
  615. {
  616. if (pszSeperator = _tcsrchr(LPTSTR(abData),_T('@')))
  617. {
  618. // found a '@', treat everything after it as the phone number
  619. // everything before it is the mailbox.
  620. *pszSeperator = _T('\0');
  621. if (!WritePrivateProfileString( TEXT(UserName),
  622. TEXT("Mailbox"),
  623. LPCSTR(abData),
  624. szInfFileName))
  625. {
  626. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  627. }
  628. if (!WritePrivateProfileString( TEXT(UserName),
  629. TEXT("FaxNumber"),
  630. _tcsinc(pszSeperator), // Print what was after the '@'.
  631. szInfFileName
  632. ))
  633. {
  634. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  635. }
  636. break;
  637. }
  638. else
  639. {
  640. // no '@' found, which means everything is the phone number.
  641. DebugPrintEx(DEBUG_MSG,"No mailbox was found in this profile");
  642. // fallthrough will write the fax number to the INF...
  643. }
  644. }// if
  645. // Replace '\n' characters in the string with semicolons.
  646. i = 0;
  647. while(abData[i] != _T('\0'))
  648. {
  649. if((abData[i] == _T('\n')) || (abData[i] == _T('\r')))
  650. {
  651. abData[i] = _T(';');
  652. }
  653. i++;
  654. }
  655. if (!WritePrivateProfileString( TEXT(UserName),
  656. auiProperties[iCount].szDescription,
  657. LPCSTR(abData),
  658. szInfFileName
  659. ))
  660. {
  661. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  662. }
  663. DebugPrintEx(DEBUG_MSG,"%s = %s",auiProperties[iCount].szDescription,abData);
  664. break;
  665. case REG_BINARY:
  666. // The data is just free-form binary. Print it one byte at a time.
  667. DebugPrintEx(DEBUG_MSG,"%s = ",auiProperties[iCount].szDescription);
  668. memset(szBinaryBuf,0,sizeof(szBinaryBuf));
  669. dwCount = 0;
  670. for (i=cbData-1;i>=0;i--)
  671. {
  672. DebugPrintEx(DEBUG_MSG,"%0*d",2,abData[i]);
  673. dwCount += sprintf(szBinaryBuf+dwCount,"%0*d",2,abData[i]);
  674. }
  675. // write to INF
  676. if (!WritePrivateProfileString( UNATTEND_FAX_SECTION,
  677. auiProperties[iCount].szDescription,
  678. szBinaryBuf,
  679. UnattendFile
  680. ))
  681. {
  682. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  683. }
  684. break;
  685. default:
  686. DebugPrintEx( DEBUG_WRN,
  687. "Unknown data type (%d) for property '%s'.",
  688. dwType,
  689. auiProperties[iCount].szDescription);
  690. }
  691. }
  692. else
  693. {
  694. DebugPrintEx(DEBUG_ERR,"Could not get property '%s'.",auiProperties[iCount].szDescription);
  695. }
  696. }
  697. }
  698. // SetGlobalFaxNumberInfo
  699. //
  700. // This routine sets the global variables 'szFaxAreaCode' and 'szFaxNumber' based on
  701. // the value in szPhone. It expects szPhone to be in the following format:
  702. //
  703. // [[<country code>] '(' <area code> ')'] <phone number>
  704. //
  705. // (Brackets denote something optional. Literals are in single quotes, non-terminals are
  706. // in angle brackets. Note that if there's a country code, there must be an area code.)
  707. //
  708. // Parameters:
  709. // szPhone Described above.
  710. //
  711. // Returns:
  712. // Nothing.
  713. //
  714. // Side effects:
  715. // Sets the values of szFaxAreaCode and szFaxNumber.
  716. //
  717. // Author:
  718. // Brian Dewey (t-briand) 1997-7-24
  719. static void
  720. SetGlobalFaxNumberInfo(LPCTSTR szPhone)
  721. {
  722. UINT i; // Loop index.
  723. UINT j; // Loop index.
  724. // First, look through the string for an area code.
  725. i = 0;
  726. while ((szPhone[i] != _T('\0')) && (szPhone[i] != _T('(')))
  727. {
  728. i++;
  729. }
  730. if(szPhone[i] == _T('('))
  731. {
  732. // We've found an area code!
  733. // are all area codes at most 3 digits?? I sized the buffer to 16, but this will
  734. // still AV on a badly-formed #.
  735. i++;
  736. j=0;
  737. while(szPhone[i] != _T(')'))
  738. {
  739. szFaxAreaCode[j] = szPhone[i];
  740. i++;
  741. j++;
  742. }
  743. i++;
  744. // szPhone[i] should now immediately after the ')' at the end
  745. // of the area code. Everything from here on out is a phone number.
  746. while(_istspace(szPhone[i]))
  747. {
  748. i++;
  749. }
  750. }
  751. else
  752. {
  753. // If we're here, there was no area code. We need to rewind either to
  754. // the beginning of the string or to the first whitespace.
  755. while(!_istspace(szPhone[i]))
  756. {
  757. i--;
  758. }
  759. i++; // The loop always rewinds one too far.
  760. }
  761. // ASSERT: We're now ready to begin copying from szPhone to
  762. // szFaxNumber.
  763. j = 0;
  764. while(szPhone[i] != '\0')
  765. {
  766. szFaxNumber[j] = szPhone[i];
  767. i++;
  768. j++;
  769. }
  770. }
  771. // InitializeInfFile
  772. //
  773. // This routine writes out the [Version] section of the inf file.
  774. //
  775. // Parameters:
  776. // None.
  777. //
  778. // Returns:
  779. // TRUE on success, FALSE on failure.
  780. //
  781. // Side effects:
  782. // Generates a fully-qualified file name in szInfFileName. Currently, that's
  783. // given by <windows dir>\<base file name>.
  784. //
  785. // Author:
  786. // Brian Dewey (t-briand) 1997-8-5
  787. static BOOL
  788. InitializeInfFile(LPCTSTR WorkingDirectory)
  789. {
  790. TCHAR szWindowsPath[MAX_PATH]; // This will hold the path to the windows directory.
  791. DWORD cbPathSize = sizeof(szWindowsPath);
  792. TCHAR szDriveLetter[2]; // Will hold the drive letter.
  793. DEBUG_FUNCTION_NAME(_T("InitializeInfFile"));
  794. // First, fully qualify the file name.
  795. if (!GetWindowsDirectory(szWindowsPath, cbPathSize))
  796. {
  797. DebugPrintEx(DEBUG_ERR,"GetWindowsDirectory failed (ec=%d)",GetLastError());
  798. return FALSE; // It must be serious if that system call failed.
  799. }
  800. szDriveLetter[0] = szWindowsPath[0];
  801. szDriveLetter[1] = 0;
  802. _stprintf(szInfFileName, TEXT("%s\\%s"), WorkingDirectory, szInfFileBase);
  803. DebugPrintEx(DEBUG_MSG,"Will store all information in INF file = '%s'",szInfFileName);
  804. // Now, put the version header on the inf file.
  805. if (!WritePrivateProfileString( TEXT("Version"),
  806. TEXT("Signature"),
  807. TEXT("\"$WINDOWS NT$\""),
  808. szInfFileName))
  809. {
  810. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  811. }
  812. // now, write out the amount of space we'll need. Currently, we
  813. // just put the awdvstub.exe program in the SystemRoot directory.
  814. // Even w/ symbols, that's under 500K. Report that.
  815. if (!WritePrivateProfileString( TEXT("NT Disk Space Requirements"),
  816. szDriveLetter,
  817. TEXT("500000"),
  818. szInfFileName))
  819. {
  820. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString failed (ec=%d)",GetLastError());
  821. }
  822. return TRUE;
  823. }
  824. ///////////////////////////////////////////////////////////////////////////////////////
  825. // Function:
  826. // MigrateDevices9X
  827. //
  828. // Purpose: Save the active device's settings in the INF
  829. // Get the device info from the AWF key under HKLM
  830. //
  831. // Params:
  832. // IN LPCSTR UnattendFile - name of the answer file
  833. //
  834. // Return Value:
  835. // Win32 Error code
  836. //
  837. // Author:
  838. // Mooly Beery (MoolyB) 13-dec-2000
  839. ///////////////////////////////////////////////////////////////////////////////////////
  840. static DWORD MigrateDevices9X(IN LPCSTR UnattendFile)
  841. {
  842. DWORD dwErr = ERROR_SUCCESS;
  843. HKEY hKeyLocalModems = NULL;
  844. HKEY hKeyGeneral = NULL;
  845. HKEY hKeyActiveDevice = NULL;
  846. CHAR szActiveDeviceSection[MAX_PATH] = {0};
  847. CHAR szLocalID[MAX_PATH] = {0};
  848. CHAR szAnswerMode[32] = {0};
  849. CHAR szNumRings[32] = {0};
  850. DWORD cbSize = 0;
  851. DWORD dwType = 0;
  852. DEBUG_FUNCTION_NAME(_T("MigrateDevices9X"));
  853. // get the active device's settings
  854. // open HLKM\Software\Microsoft\At Work Fax\Local Modems
  855. dwErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  856. REG_KEY_AWF_LOCAL_MODEMS,
  857. 0,
  858. KEY_READ,
  859. &hKeyLocalModems);
  860. if (dwErr!=ERROR_SUCCESS)
  861. {
  862. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",REG_KEY_AWF_LOCAL_MODEMS,dwErr);
  863. goto exit;
  864. }
  865. // open the 'general' sub key
  866. dwErr = RegOpenKeyEx( hKeyLocalModems,
  867. "General",
  868. 0,
  869. KEY_READ,
  870. &hKeyGeneral);
  871. if (dwErr!=ERROR_SUCCESS)
  872. {
  873. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx General failed (ec=%d)",dwErr);
  874. goto exit;
  875. }
  876. // get the LocalID REG_SZ, this will be used as the TSID and CSID
  877. cbSize = sizeof(szLocalID);
  878. dwErr = RegQueryValueEx( hKeyGeneral,
  879. INF_RULE_LOCAL_ID,
  880. NULL,
  881. &dwType,
  882. LPBYTE(szLocalID),
  883. &cbSize);
  884. if (dwErr==ERROR_SUCCESS)
  885. {
  886. DebugPrintEx(DEBUG_MSG,"RegQueryValueEx LocalID returned %s",szLocalID);
  887. }
  888. else
  889. {
  890. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx LocalID failed (ec=%d)",dwErr);
  891. goto exit;
  892. }
  893. // write the TSID & CSID entry in the Fax section of unattended.txt
  894. if (!WritePrivateProfileString( UNATTEND_FAX_SECTION,
  895. INF_RULE_LOCAL_ID,
  896. szLocalID,
  897. UnattendFile))
  898. {
  899. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString TSID failed (ec=%d)",GetLastError());
  900. }
  901. // get the ActiveDeviceSection REG_SZ
  902. cbSize = sizeof(szActiveDeviceSection);
  903. dwErr = RegQueryValueEx( hKeyGeneral,
  904. "ActiveDeviceSection",
  905. NULL,
  906. &dwType,
  907. LPBYTE(szActiveDeviceSection),
  908. &cbSize);
  909. if (dwErr==ERROR_SUCCESS)
  910. {
  911. DebugPrintEx(DEBUG_MSG,"RegQueryValueEx ActiveDeviceSection returned %s",szActiveDeviceSection);
  912. }
  913. else
  914. {
  915. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx ActiveDeviceSection failed (ec=%d)",dwErr);
  916. goto exit;
  917. }
  918. // open HLKM\Software\Microsoft\At Work Fax\Local Modems\ "ActiveDeviceSection"
  919. dwErr = RegOpenKeyEx( hKeyLocalModems,
  920. szActiveDeviceSection,
  921. 0,
  922. KEY_READ,
  923. &hKeyActiveDevice);
  924. if (dwErr!=ERROR_SUCCESS)
  925. {
  926. DebugPrintEx(DEBUG_ERR,"RegOpenKeyEx %s failed (ec=%d)",szActiveDeviceSection,dwErr);
  927. goto exit;
  928. }
  929. // get the AnswerMode REG_SZ value,
  930. // 0 - Don't answer
  931. // 1 - Manual
  932. // 2 - Answer after x rings.
  933. cbSize = sizeof(szAnswerMode);
  934. dwErr = RegQueryValueEx( hKeyActiveDevice,
  935. INF_RULE_ANSWER_MODE,
  936. NULL,
  937. &dwType,
  938. LPBYTE(szAnswerMode),
  939. &cbSize);
  940. if (dwErr==ERROR_SUCCESS)
  941. {
  942. DebugPrintEx(DEBUG_MSG,"RegQueryValueEx AnswerMode returned %s",szAnswerMode);
  943. }
  944. else
  945. {
  946. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx AnswerMode failed (ec=%d)",dwErr);
  947. goto exit;
  948. }
  949. if (!WritePrivateProfileString( UNATTEND_FAX_SECTION,
  950. INF_RULE_ANSWER_MODE,
  951. szAnswerMode,
  952. UnattendFile))
  953. {
  954. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString Receive failed (ec=%d)",GetLastError());
  955. }
  956. // get the NumRings REG_SZ value,
  957. cbSize = sizeof(szNumRings);
  958. dwErr = RegQueryValueEx( hKeyActiveDevice,
  959. INF_RULE_NUM_RINGS,
  960. NULL,
  961. &dwType,
  962. LPBYTE(szNumRings),
  963. &cbSize);
  964. if (dwErr==ERROR_SUCCESS)
  965. {
  966. DebugPrintEx(DEBUG_MSG,"RegQueryValueEx NumRings returned %s",szNumRings);
  967. }
  968. else
  969. {
  970. DebugPrintEx(DEBUG_ERR,"RegQueryValueEx NumRings failed (ec=%d)",dwErr);
  971. goto exit;
  972. }
  973. if (!WritePrivateProfileString( UNATTEND_FAX_SECTION,
  974. INF_RULE_NUM_RINGS,
  975. szNumRings,
  976. UnattendFile))
  977. {
  978. DebugPrintEx(DEBUG_ERR,"WritePrivateProfileString NumRings failed (ec=%d)",GetLastError());
  979. }
  980. exit:
  981. if (hKeyLocalModems)
  982. {
  983. RegCloseKey(hKeyLocalModems);
  984. }
  985. if (hKeyActiveDevice)
  986. {
  987. RegCloseKey(hKeyActiveDevice);
  988. }
  989. if (hKeyGeneral)
  990. {
  991. RegCloseKey(hKeyGeneral);
  992. }
  993. return dwErr;
  994. }
  995. ///////////////////////////////////////////////////////////////////////////////////////
  996. // Function:
  997. // CopyCoverPageFiles9X
  998. //
  999. // Purpose: Copy all of the *.CPE files from %windir% to the temporary
  1000. // directory for our migration
  1001. //
  1002. // Params:
  1003. // None
  1004. //
  1005. // Return Value:
  1006. // Win32 Error code
  1007. //
  1008. // Author:
  1009. // Mooly Beery (MoolyB) 13-dec-2000
  1010. ///////////////////////////////////////////////////////////////////////////////////////
  1011. DWORD CopyCoverPageFiles9X()
  1012. {
  1013. DWORD dwErr = ERROR_SUCCESS;
  1014. CHAR szWindowsDir[MAX_PATH] = {0};
  1015. SHFILEOPSTRUCT fileOpStruct;
  1016. DEBUG_FUNCTION_NAME(_T("CopyCoverPageFiles9X"));
  1017. ZeroMemory(&fileOpStruct, sizeof(SHFILEOPSTRUCT));
  1018. // Get the windows directory
  1019. if (!GetWindowsDirectory(szWindowsDir, MAX_PATH))
  1020. {
  1021. dwErr = GetLastError();
  1022. DebugPrintEx(DEBUG_ERR,"GetWindowsDirectory failed (ec=%d)",dwErr);
  1023. goto exit;
  1024. }
  1025. //
  1026. // Copy *.cpe from windows-dir to temp-dir
  1027. //
  1028. strcat(szWindowsDir,"\\*.cpe");
  1029. fileOpStruct.hwnd = NULL;
  1030. fileOpStruct.wFunc = FO_COPY;
  1031. fileOpStruct.pFrom = szWindowsDir;
  1032. fileOpStruct.pTo = lpWorkingDir;
  1033. fileOpStruct.fFlags =
  1034. FOF_FILESONLY | // Perform the operation on files only if a wildcard file name (*.*) is specified.
  1035. FOF_NOCONFIRMMKDIR | // Do not confirm the creation of a new directory if the operation requires one to be created.
  1036. FOF_NOCONFIRMATION | // Respond with "Yes to All" for any dialog box that is displayed.
  1037. FOF_NORECURSION | // Only operate in the local directory. Don't operate recursively into subdirectories.
  1038. FOF_SILENT | // Do not display a progress dialog box.
  1039. FOF_NOERRORUI; // Do not display a user interface if an error occurs.
  1040. fileOpStruct.fAnyOperationsAborted = FALSE;
  1041. fileOpStruct.hNameMappings = NULL;
  1042. fileOpStruct.lpszProgressTitle = NULL;
  1043. DebugPrintEx(DEBUG_MSG,
  1044. TEXT("Calling to SHFileOperation from %s to %s."),
  1045. fileOpStruct.pFrom,
  1046. fileOpStruct.pTo);
  1047. if (SHFileOperation(&fileOpStruct))
  1048. {
  1049. dwErr = GetLastError();
  1050. DebugPrintEx(DEBUG_ERR,"SHFileOperation failed (ec: %ld)",dwErr);
  1051. goto exit;
  1052. }
  1053. exit:
  1054. return dwErr;
  1055. }
  1056. ///////////////////////////////////////////////////////////////////////////////////////
  1057. // Function:
  1058. // IsAWFInstalled
  1059. //
  1060. // Purpose:
  1061. // Check if AWF is installed
  1062. //
  1063. // Params:
  1064. // None
  1065. //
  1066. // Return Value:
  1067. // TRUE - AWF is installed
  1068. // FALSE - AWF not installed or an error occured error
  1069. //
  1070. // Author:
  1071. // Mooly Beery (MoolyB) 13-dec-2000
  1072. ///////////////////////////////////////////////////////////////////////////////////////
  1073. static BOOL IsAWFInstalled()
  1074. {
  1075. HKEY hKey = NULL;
  1076. DWORD dwErr = ERROR_SUCCESS;
  1077. DEBUG_FUNCTION_NAME(_T("IsAWFInstalled"));
  1078. dwErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  1079. REG_KEY_AWF_INSTALLED,
  1080. 0,
  1081. KEY_READ,
  1082. &hKey);
  1083. if (dwErr!=ERROR_SUCCESS)
  1084. {
  1085. DebugPrintEx(DEBUG_MSG,"RegOpenKeyEx %s failed (ec=%d), assume AWF is not installed",REG_KEY_AWF_LOCAL_MODEMS,dwErr);
  1086. return FALSE;
  1087. }
  1088. RegCloseKey(hKey);
  1089. DebugPrintEx(DEBUG_MSG,"AWF is installed");
  1090. return TRUE;
  1091. }
  1092. static DWORD RememberInstalledFax(
  1093. IN DWORD dwFaxInstalled
  1094. )
  1095. /*++
  1096. Routine name : RememberInstalledFax
  1097. Routine description:
  1098. for each installed Fax Client application, write to the registry that this app is installed.
  1099. Arguments :
  1100. IN DWORD dwFaxInstalled - input bit-wise combination of fxState_UpgradeApp_e values that defines which
  1101. fax client appications are present of the machine.
  1102. Author:
  1103. Iv Garber (IvG), May, 2002
  1104. Return Value:
  1105. Success or Failure code.
  1106. --*/
  1107. {
  1108. DWORD dwReturn = NO_ERROR;
  1109. HKEY hKey = NULL;
  1110. DEBUG_FUNCTION_NAME(_T("RememberInstalledFax"));
  1111. //
  1112. // check parameters
  1113. //
  1114. if (dwFaxInstalled == FXSTATE_NONE)
  1115. {
  1116. DebugPrintEx(DEBUG_MSG, _T("No Fax application is installed -> Upgrade will not be blocked."));
  1117. return dwReturn;
  1118. }
  1119. //
  1120. // Create Registry Key
  1121. //
  1122. hKey = OpenRegistryKey(HKEY_LOCAL_MACHINE, REGKEYUPG_INSTALLEDFAX, TRUE, KEY_SET_VALUE);
  1123. if (!hKey)
  1124. {
  1125. dwReturn = GetLastError();
  1126. DebugPrintEx(
  1127. DEBUG_WRN,
  1128. _T("OpenRegistryKey( ' %s ' ) failed, ec = %ld. Cannot remember installed fax apps."),
  1129. REGKEYUPG_INSTALLEDFAX,
  1130. dwReturn);
  1131. return dwReturn;
  1132. }
  1133. //
  1134. // store the value in the Registry
  1135. //
  1136. if (!SetRegistryDword(hKey, NULL, dwFaxInstalled))
  1137. {
  1138. dwReturn = GetLastError();
  1139. DebugPrintEx(DEBUG_WRN, _T("SetRegistryDword( ' %ld ' ) failed, ec = %ld."), dwFaxInstalled, dwReturn);
  1140. }
  1141. RegCloseKey(hKey);
  1142. return dwReturn;
  1143. }
  1144. static DWORD MigrateUninstalledFax(
  1145. IN LPCTSTR lpctstrUnattendFile,
  1146. OUT bool *pbFaxWasInstalled
  1147. )
  1148. /*++
  1149. Routine name : MigrateUninstalledFax
  1150. Routine description:
  1151. Put the data about Fax Applications that were installed on the machine before upgrade,
  1152. from the Registry to the Unattended file, to be used by FaxOCM.
  1153. Author:
  1154. Iv Garber (IvG), May, 2001
  1155. Arguments:
  1156. lpctstrUnattendFile [in] - name of the answer file to write the data to
  1157. pbFaxWasInstalled [out] - address of a bool variable to receive True if SBS 5.0 /XPDL Client was installed
  1158. on the machine before the upgrade, otherwise False.
  1159. Return Value:
  1160. Success or Failure code.
  1161. --*/
  1162. {
  1163. DWORD dwReturn = NO_ERROR;
  1164. HKEY hKey = NULL;
  1165. DWORD dwValue = FXSTATE_NONE;
  1166. TCHAR szValue[10] = {0};
  1167. DEBUG_FUNCTION_NAME(_T("MigrateUninstalledFax"));
  1168. if (pbFaxWasInstalled)
  1169. {
  1170. *pbFaxWasInstalled = false;
  1171. }
  1172. //
  1173. // Open a key
  1174. //
  1175. hKey = OpenRegistryKey(HKEY_LOCAL_MACHINE, REGKEYUPG_INSTALLEDFAX, FALSE, KEY_QUERY_VALUE);
  1176. if (!hKey)
  1177. {
  1178. dwReturn = GetLastError();
  1179. DebugPrintEx(
  1180. DEBUG_MSG,
  1181. _T("OpenRegistryKey( ' %s ' ) failed, ec = %ld. No Fax was installed before the upgrade."),
  1182. REGKEYUPG_INSTALLEDFAX,
  1183. dwReturn);
  1184. if (dwReturn == ERROR_FILE_NOT_FOUND)
  1185. {
  1186. //
  1187. // This is not real error
  1188. //
  1189. dwReturn = NO_ERROR;
  1190. }
  1191. return dwReturn;
  1192. }
  1193. //
  1194. // Read the data
  1195. //
  1196. dwReturn = GetRegistryDwordEx(hKey, NULL, &dwValue);
  1197. if (dwReturn != ERROR_SUCCESS)
  1198. {
  1199. DebugPrintEx(DEBUG_WRN, _T("GetRegistryDwordEx() failed, ec = %ld."), dwReturn);
  1200. goto CloseRegistry;
  1201. }
  1202. if (pbFaxWasInstalled)
  1203. {
  1204. *pbFaxWasInstalled = true;
  1205. }
  1206. DebugPrintEx(DEBUG_MSG, _T("Found uninstalled fax apps : %ld"), dwValue);
  1207. //
  1208. // Convert dwValue to String
  1209. //
  1210. _itot(dwValue, szValue, 10);
  1211. //
  1212. // write szValue to the unattended file
  1213. //
  1214. if (!WritePrivateProfileString(
  1215. UNATTEND_FAX_SECTION,
  1216. UNINSTALLEDFAX_INFKEY,
  1217. szValue,
  1218. lpctstrUnattendFile))
  1219. {
  1220. dwReturn = GetLastError();
  1221. DebugPrintEx(
  1222. DEBUG_ERR,
  1223. _T("WritePrivateProfileString(FaxApps = ' %s ') failed (ec=%d)"),
  1224. szValue,
  1225. dwReturn);
  1226. }
  1227. else
  1228. {
  1229. DebugPrintEx(
  1230. DEBUG_ERR,
  1231. _T("WritePrivateProfileString(FaxApps = ' %s ') OK."),
  1232. szValue);
  1233. }
  1234. CloseRegistry:
  1235. RegCloseKey(hKey);
  1236. return dwReturn;
  1237. }