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.

1332 lines
41 KiB

  1. /*
  2. * CSV.C
  3. *
  4. * Migrate CSV <-> WAB
  5. *
  6. * Copyright 1997 Microsoft Corporation. All Rights Reserved.
  7. */
  8. #include "_comctl.h"
  9. #include <windows.h>
  10. #include <commctrl.h>
  11. #include <mapix.h>
  12. #include <wab.h>
  13. #include <wabguid.h>
  14. #include <wabdbg.h>
  15. #include <wabmig.h>
  16. #include <emsabtag.h>
  17. #include <shlwapi.h>
  18. #include "wabimp.h"
  19. #include "..\..\wab32res\resrc2.h"
  20. #include "dbgutil.h"
  21. BOOL HandleImportError(HWND hwnd, ULONG ids, HRESULT hResult, LPTSTR lpDisplayName,
  22. LPTSTR lpEmailAddress, LPWAB_IMPORT_OPTIONS lpImportOptions);
  23. BOOL HandleExportError(HWND hwnd, ULONG ids, HRESULT hResult, LPTSTR lpDisplayName,
  24. LPTSTR lpEmailAddress, LPWAB_EXPORT_OPTIONS lpExportOptions);
  25. /***************************************************************************
  26. Name : IsDomainName
  27. Purpose : Is this domain correctly formatted for an Internet address?
  28. Parameters: lpDomain -> Domain name to check
  29. Returns : TRUE if the domain is a correct format for an Internet
  30. address.
  31. Comment : Valid domain names have this form:
  32. bar[.bar]*
  33. where bar must have non-empty contents
  34. no high bits are allowed on any characters
  35. no '@' allowed
  36. ***************************************************************************/
  37. BOOL IsDomainName(LPTSTR lpDomain) {
  38. BOOL fBar = FALSE;
  39. if (lpDomain) {
  40. if (*lpDomain == '\0' || *lpDomain == '.') {
  41. // domain name must have contents and can't start with '.'
  42. return(FALSE);
  43. }
  44. while (*lpDomain) {
  45. // Internet addresses only allow pure ASCII. No high bits!
  46. if (*lpDomain & 0x80 || *lpDomain == '@') {
  47. return(FALSE);
  48. }
  49. if (*lpDomain == '.') {
  50. // Recursively check this part of the domain name
  51. return(IsDomainName(CharNext(lpDomain)));
  52. }
  53. lpDomain = CharNext(lpDomain);
  54. }
  55. return(TRUE);
  56. }
  57. return(FALSE);
  58. }
  59. /***************************************************************************
  60. Name : IsInternetAddress
  61. Purpose : Is this address correctly formatted for an Internet address
  62. Parameters: lpAddress -> Address to check
  63. Returns : TRUE if the address is a correct format for an Internet
  64. address.
  65. Comment : Valid addresses have this form:
  66. foo@bar[.bar]*
  67. where foo and bar must have non-empty contents
  68. ***************************************************************************/
  69. BOOL IsInternetAddress(LPTSTR lpAddress) {
  70. BOOL fDomain = FALSE;
  71. // Step through the address looking for '@'. If there's an at sign in the middle
  72. // of a string, this is close enough to being an internet address for me.
  73. if (lpAddress) {
  74. // Can't start with '@'
  75. if (*lpAddress == '@') {
  76. return(FALSE);
  77. }
  78. while (*lpAddress) {
  79. // Internet addresses only allow pure ASCII. No high bits!
  80. if (*lpAddress & 0x80) {
  81. return(FALSE);
  82. }
  83. if (*lpAddress == '@') {
  84. // Found the at sign. Is there anything following?
  85. // (Must NOT be another '@')
  86. return(IsDomainName(CharNext(lpAddress)));
  87. }
  88. lpAddress = CharNext(lpAddress);
  89. }
  90. }
  91. return(FALSE);
  92. }
  93. /***************************************************************************
  94. Name : OpenCSVFile
  95. Purpose : Opens a CSV file for import
  96. Parameters: hwnd = main dialog window
  97. lpFileName = filename to create
  98. lphFile -> returned file handle
  99. Returns : HRESULT
  100. Comment :
  101. ***************************************************************************/
  102. HRESULT OpenCSVFile(HWND hwnd, LPTSTR lpFileName, LPHANDLE lphFile) {
  103. LPTSTR lpFilter;
  104. TCHAR szFileName[MAX_PATH + 1] = "";
  105. OPENFILENAME ofn;
  106. HANDLE hFile = INVALID_HANDLE_VALUE;
  107. HRESULT hResult = hrSuccess;
  108. DWORD ec;
  109. if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
  110. GENERIC_READ,
  111. 0, // sharing
  112. NULL,
  113. CREATE_NEW,
  114. FILE_FLAG_SEQUENTIAL_SCAN,
  115. NULL))) {
  116. ec = GetLastError();
  117. DebugTrace("CreateFile(%s) -> %u\n", lpFileName, ec);
  118. switch (ec) {
  119. case ERROR_FILE_NOT_FOUND:
  120. case ERROR_PATH_NOT_FOUND:
  121. default:
  122. ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
  123. hResult = ResultFromScode(MAPI_E_NOT_FOUND);
  124. break;
  125. }
  126. }
  127. if (! hResult) {
  128. *lphFile = hFile;
  129. }
  130. return(hResult);
  131. }
  132. /***************************************************************************
  133. Name : CountCSVRows
  134. Purpose : Counts the rows in the CSV file
  135. Parameters: hFile = open CSV file
  136. szSep = list separator
  137. lpulcEntries -> returned count of rows
  138. Returns : HRESULT
  139. Comment : File pointer should be positioned past the header row prior
  140. to calling this function. This function leaves the file
  141. pointer where it found it.
  142. ***************************************************************************/
  143. HRESULT CountCSVRows(HANDLE hFile, LPTSTR szSep, LPULONG lpulcEntries) {
  144. HRESULT hResult = hrSuccess;
  145. PUCHAR * rgItems = NULL;
  146. ULONG ulStart;
  147. ULONG cProps, i;
  148. *lpulcEntries = 0;
  149. Assert(hFile != INVALID_HANDLE_VALUE);
  150. if (0xFFFFFFFF == (ulStart = SetFilePointer(hFile, 0, NULL, FILE_CURRENT))) {
  151. DebugTrace("CountCSVRows SetFilePointer -> %u\n", GetLastError());
  152. return(ResultFromScode(MAPI_E_CALL_FAILED));
  153. }
  154. while (hResult == hrSuccess) {
  155. // Read the line
  156. if (ReadCSVLine(hFile, szSep, &cProps, &rgItems)) {
  157. // End of file
  158. break;
  159. }
  160. (*lpulcEntries)++;
  161. if (rgItems) {
  162. for (i = 0; i < cProps; i++) {
  163. if (rgItems[i]) {
  164. LocalFree(rgItems[i]);
  165. }
  166. }
  167. LocalFree(rgItems);
  168. rgItems = NULL;
  169. }
  170. }
  171. if (0xFFFFFFFF == SetFilePointer(hFile, ulStart, NULL, FILE_BEGIN)) {
  172. DebugTrace("CountCSVRows SetFilePointer -> %u\n", GetLastError());
  173. }
  174. return(hResult);
  175. }
  176. BOOL TestCSVName(ULONG index,
  177. LPPROP_NAME lpImportMapping,
  178. ULONG ulcFields,
  179. PUCHAR * rgItems,
  180. ULONG cProps,
  181. BOOL fTryUnchosen) {
  182. return((index != NOT_FOUND) &&
  183. index < ulcFields &&
  184. index < cProps &&
  185. (fTryUnchosen || lpImportMapping[index].fChosen) &&
  186. rgItems[index] &&
  187. rgItems[index][0]);
  188. }
  189. /***************************************************************************
  190. Name : MakeDisplayName
  191. Purpose : Forms a display name based on the values of various props.
  192. Parameters: lppDisplayName -> Returned display name. This should only
  193. be used for certain purposes. It can be used for error
  194. dialogs, but if it was generated from first/middle/last,
  195. it should not be used for PR_DISPLAY_NAME!
  196. lpImportMapping = import mapping table
  197. ulcFields = size of import mapping table
  198. rgItems = fields for this CSV item
  199. cProps = number of fields in rgItems
  200. iDisplayName = indicies of name related props
  201. iNickname
  202. iSurname
  203. iGivenName
  204. iMiddleName
  205. iEmailAddress
  206. iCompanyName
  207. Returns : index of attribute forming the display name, or
  208. if FML, return INDEX_FIRST_MIDDLE_LAST.
  209. Comment : Form the display name based on these rules:
  210. 1. If there's already a display name and it's chosen,
  211. use it.
  212. 2. if there's a chosen first, middle or last name, add them
  213. together and use them.
  214. 3. if there's a chosen nickname, use it
  215. 4. if there's a chosen email-address, use it.
  216. 5. if there's a chosen company name, use it.
  217. 6. look again without regard to whether it was chosen or not.
  218. ***************************************************************************/
  219. ULONG MakeDisplayName(LPTSTR * lppDisplayName,
  220. LPPROP_NAME lpImportMapping,
  221. ULONG ulcFields,
  222. PUCHAR * rgItems,
  223. ULONG cProps,
  224. ULONG iDisplayName,
  225. ULONG iNickname,
  226. ULONG iSurname,
  227. ULONG iGivenName,
  228. ULONG iMiddleName,
  229. ULONG iEmailAddress,
  230. ULONG iCompanyName) {
  231. BOOL fTryUnchosen = FALSE;
  232. BOOL fSurname = FALSE;
  233. BOOL fGivenName = FALSE;
  234. BOOL fMiddleName = FALSE;
  235. ULONG index = NOT_FOUND;
  236. ULONG ulSize = 0;
  237. LPTSTR lpDisplayName = NULL;
  238. try_again:
  239. if (TestCSVName(iDisplayName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  240. index = iDisplayName;
  241. goto found;
  242. }
  243. if (TestCSVName(iSurname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen) ||
  244. TestCSVName(iGivenName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen) ||
  245. TestCSVName(iMiddleName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  246. index = INDEX_FIRST_MIDDLE_LAST;
  247. goto found;
  248. }
  249. if (TestCSVName(iNickname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  250. index = iNickname;
  251. goto found;
  252. }
  253. if (TestCSVName(iEmailAddress, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  254. index = iEmailAddress;
  255. goto found;
  256. }
  257. if (TestCSVName(iCompanyName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  258. index = iCompanyName;
  259. goto found;
  260. }
  261. if (! fTryUnchosen) {
  262. fTryUnchosen = TRUE;
  263. goto try_again;
  264. }
  265. found:
  266. *lppDisplayName = NULL;
  267. switch (index) {
  268. case NOT_FOUND:
  269. break;
  270. case INDEX_FIRST_MIDDLE_LAST:
  271. if (fSurname = TestCSVName(iSurname, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  272. ulSize += (lstrlen(rgItems[iSurname]) + 1);
  273. }
  274. if (fGivenName = TestCSVName(iGivenName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  275. ulSize += (lstrlen(rgItems[iGivenName]) + 1);
  276. }
  277. if (fMiddleName = TestCSVName(iMiddleName, lpImportMapping, ulcFields, rgItems, cProps, fTryUnchosen)) {
  278. ulSize += (lstrlen(rgItems[iMiddleName]) + 1);
  279. }
  280. Assert(ulSize);
  281. if (lpDisplayName = *lppDisplayName = LocalAlloc(LPTR, ulSize)) {
  282. // BUGBUG: This does not localize. The effect is that in the collision/error
  283. // dialogs, we will get the order of names wrong. It should not effect the properties
  284. // actually stored on the object since we won't set PR_DISPLAY_NAME if it was
  285. // generated by First/Middle/Last. I can live with this, but we'll see if the
  286. // testers find it. BruceK
  287. if (fGivenName) {
  288. StrCatBuff(lpDisplayName, rgItems[iGivenName], ulSize);
  289. }
  290. if (fMiddleName) {
  291. if (*lpDisplayName) {
  292. StrCatBuff(lpDisplayName, " ", ulSize);
  293. }
  294. StrCatBuff(lpDisplayName, rgItems[iMiddleName], ulSize);
  295. }
  296. if (fSurname) {
  297. if (*lpDisplayName) {
  298. StrCatBuff(lpDisplayName, " ", ulSize);
  299. }
  300. StrCatBuff(lpDisplayName, rgItems[iSurname], ulSize);
  301. }
  302. }
  303. break;
  304. default:
  305. ulSize = lstrlen(rgItems[index]) + 1;
  306. if (*lppDisplayName = LocalAlloc(LPTR, ulSize)) {
  307. StrCpyN(*lppDisplayName, rgItems[index], ulSize);
  308. }
  309. break;
  310. }
  311. return(index);
  312. }
  313. #define MAX_SEP 20
  314. void GetListSeparator(LPTSTR szBuf)
  315. {
  316. // Buffer is assumed to be MAX_SEP chars long
  317. if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, szBuf, MAX_SEP))
  318. {
  319. szBuf[0] = TEXT(',');
  320. szBuf[1] = 0;
  321. }
  322. }
  323. HRESULT CSVImport(HWND hWnd,
  324. LPADRBOOK lpAdrBook,
  325. LPWABOBJECT lpWABObject,
  326. LPWAB_PROGRESS_CALLBACK lpProgressCB,
  327. LPWAB_EXPORT_OPTIONS lpOptions) {
  328. HRESULT hResult = hrSuccess;
  329. register ULONG i;
  330. ULONG cbWABEID, ulObjType;
  331. ULONG index;
  332. ULONG ulLastChosenProp = 0;
  333. ULONG ulcFields = 0;
  334. ULONG cProps;
  335. ULONG ulCreateFlags = CREATE_CHECK_DUP_STRICT;
  336. TCHAR szBuffer[MAX_RESOURCE_STRING + 1];
  337. WAB_PROGRESS Progress;
  338. LPABCONT lpContainer = NULL;
  339. HANDLE hFile = INVALID_HANDLE_VALUE;
  340. TCHAR rgFileName[MAX_PATH + 1] = "";
  341. PUCHAR * rgItems = NULL;
  342. REPLACE_INFO RI;
  343. LPMAPIPROP lpMailUserWAB = NULL;
  344. SPropValue sPropVal;
  345. BOOL fSkipSetProps;
  346. LPTSTR lpDisplayName = NULL, lpEmailAddress = NULL;
  347. ULONG iEmailAddress = NOT_FOUND, iDisplayName = NOT_FOUND, iSurname = NOT_FOUND,
  348. iGivenName = NOT_FOUND, iCompanyName = NOT_FOUND, iMiddleName = NOT_FOUND,
  349. iNickname = NOT_FOUND, iDisplay = NOT_FOUND;
  350. TCHAR szSep[MAX_SEP];
  351. SetGlobalBufferFunctions(lpWABObject);
  352. // Read in the Property Name strings to the PropNames array
  353. for (i = 0; i < NUM_EXPORT_PROPS; i++) {
  354. rgPropNames[i].lpszName = LoadAllocString(rgPropNames[i].ids);
  355. Assert(rgPropNames[i].lpszName);
  356. DebugTrace("Property 0x%08x name: %s\n", rgPropNames[i].ulPropTag, rgPropNames[i].lpszName);
  357. }
  358. GetListSeparator(szSep);
  359. // Present UI Wizard
  360. if (hResult = ImportWizard(hWnd, rgFileName, ARRAYSIZE(rgFileName), rgPropNames, szSep, &lpImportMapping, &ulcFields, &hFile)) {
  361. goto exit;
  362. }
  363. Assert(hFile != INVALID_HANDLE_VALUE);
  364. // Find name props and last chosen property
  365. for (i = 0; i < ulcFields; i++) {
  366. if (lpImportMapping[i].fChosen) {
  367. ulLastChosenProp = i;
  368. }
  369. switch (lpImportMapping[i].ulPropTag) {
  370. case PR_EMAIL_ADDRESS:
  371. iEmailAddress = i;
  372. break;
  373. case PR_DISPLAY_NAME:
  374. iDisplayName = i;
  375. break;
  376. case PR_SURNAME:
  377. iSurname = i;
  378. break;
  379. case PR_GIVEN_NAME:
  380. iGivenName = i;
  381. break;
  382. case PR_COMPANY_NAME:
  383. iCompanyName = i;
  384. break;
  385. case PR_MIDDLE_NAME:
  386. iMiddleName = i;
  387. break;
  388. case PR_NICKNAME:
  389. iNickname = i;
  390. break;
  391. }
  392. }
  393. //
  394. // Open the WAB's PAB container: fills global lpCreateEIDsWAB
  395. //
  396. if (hResult = LoadWABEIDs(lpAdrBook, &lpContainer)) {
  397. goto exit;
  398. }
  399. //
  400. // All set... now loop through the file lines, adding each to the WAB
  401. //
  402. // How many lines are there?
  403. if (hResult = CountCSVRows(hFile, szSep, &ulcEntries)) {
  404. goto exit;
  405. }
  406. DebugTrace("CSV file contains %u entries\n", ulcEntries);
  407. // Initialize the Progress Bar
  408. Progress.denominator = max(ulcEntries, 1);
  409. Progress.numerator = 0;
  410. if (LoadString(hInst, IDS_STATE_IMPORT_MU, szBuffer, sizeof(szBuffer))) {
  411. DebugTrace("Status Message: %s\n", szBuffer);
  412. Progress.lpText = szBuffer;
  413. } else {
  414. DebugTrace("Cannot load resource string %u\n", IDS_STATE_IMPORT_MU);
  415. Progress.lpText = NULL;
  416. }
  417. lpProgressCB(hWnd, &Progress);
  418. while (hResult == hrSuccess) {
  419. // Read the CSV attributes
  420. if (hResult = ReadCSVLine(hFile, szSep, &cProps, &rgItems)) {
  421. DebugTrace("ReadCSVLine -> %x\n", GetScode(hResult));
  422. if (GetScode(hResult) == MAPI_E_NOT_FOUND) {
  423. // EOF
  424. hResult = hrSuccess;
  425. }
  426. break; // nothing more to read
  427. }
  428. iDisplay = iDisplayName;
  429. if (TestCSVName(iEmailAddress,
  430. lpImportMapping,
  431. ulcFields,
  432. rgItems,
  433. cProps,
  434. TRUE)) {
  435. lpEmailAddress = rgItems[iEmailAddress];
  436. }
  437. switch (index = MakeDisplayName(&lpDisplayName,
  438. lpImportMapping,
  439. ulcFields,
  440. rgItems,
  441. cProps,
  442. iDisplayName,
  443. iNickname,
  444. iSurname,
  445. iGivenName,
  446. iMiddleName,
  447. iEmailAddress,
  448. iCompanyName)) {
  449. case NOT_FOUND:
  450. // No name props
  451. // BUGBUG: Should give special error?
  452. break;
  453. case INDEX_FIRST_MIDDLE_LAST:
  454. break;
  455. default:
  456. iDisplay = index;
  457. break;
  458. }
  459. // Should be the same number of fields in every entry, but if not,
  460. // we'll handle it below.
  461. // Assert(cProps == ulcFields); // Outlook does this!
  462. ulCreateFlags = CREATE_CHECK_DUP_STRICT;
  463. if (lpOptions->ReplaceOption == WAB_REPLACE_ALWAYS) {
  464. ulCreateFlags |= CREATE_REPLACE;
  465. }
  466. retry:
  467. // Create a new wab mailuser
  468. if (HR_FAILED(hResult = lpContainer->lpVtbl->CreateEntry(lpContainer,
  469. lpCreateEIDsWAB[iconPR_DEF_CREATE_MAILUSER].Value.bin.cb,
  470. (LPENTRYID)lpCreateEIDsWAB[iconPR_DEF_CREATE_MAILUSER].Value.bin.lpb,
  471. ulCreateFlags,
  472. &lpMailUserWAB))) {
  473. DebugTrace("CreateEntry(WAB MailUser) -> %x\n", GetScode(hResult));
  474. goto exit;
  475. }
  476. for (i = 0; i <= min(ulLastChosenProp, cProps); i++)
  477. {
  478. if (lpImportMapping[i].fChosen && lpImportMapping[i].lpszName)
  479. {
  480. if (rgItems[i] && *rgItems[i]) {
  481. // Look it up in the WAB property names table
  482. DebugTrace("Prop %u: <%s> %s\n", i, lpImportMapping[i].lpszName, rgItems[i]);
  483. sPropVal.ulPropTag = lpImportMapping[i].ulPropTag;
  484. Assert(PROP_TYPE(lpImportMapping[i].ulPropTag) == PT_TSTRING);
  485. sPropVal.Value.LPSZ = rgItems[i];
  486. fSkipSetProps = FALSE;
  487. if (sPropVal.ulPropTag == PR_EMAIL_ADDRESS)
  488. {
  489. if (! IsInternetAddress(sPropVal.Value.LPSZ))
  490. {
  491. DebugTrace("Found non-SMTP address %s\n", sPropVal.Value.LPSZ);
  492. if (HandleImportError(hWnd,
  493. 0,
  494. WAB_W_BAD_EMAIL,
  495. lpDisplayName,
  496. lpEmailAddress,
  497. lpOptions))
  498. {
  499. hResult = ResultFromScode(MAPI_E_USER_CANCEL);
  500. goto exit;
  501. }
  502. lpEmailAddress = NULL;
  503. fSkipSetProps = TRUE;
  504. }
  505. }
  506. if (! fSkipSetProps)
  507. {
  508. // Set the property on the WAB entry
  509. if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SetProps(lpMailUserWAB,
  510. 1, // cValues
  511. &sPropVal, // property array
  512. NULL)))
  513. { // problems array
  514. DebugTrace("ImportEntry:SetProps(WAB) -> %x\n", GetScode(hResult));
  515. goto exit;
  516. }
  517. // [PaulHi] 3/4/99 Raid 73637
  518. // If we have a valid email address then we need to also add the
  519. // PR_ADDRTYPE property set to "SMTP".
  520. if (sPropVal.ulPropTag == PR_EMAIL_ADDRESS)
  521. {
  522. sPropVal.ulPropTag = PR_ADDRTYPE;
  523. sPropVal.Value.LPSZ = (LPTSTR)szSMTP;
  524. hResult = lpMailUserWAB->lpVtbl->SetProps(
  525. lpMailUserWAB,
  526. 1,
  527. &sPropVal,
  528. NULL);
  529. if (HR_FAILED(hResult))
  530. {
  531. DebugTrace("CSV ImportEntry:SetProps(WAB) for PR_ADDRTYPE -> %x\n", GetScode(hResult));
  532. goto exit;
  533. }
  534. }
  535. }
  536. }
  537. }
  538. }
  539. if (index != iDisplayName && index != NOT_FOUND && index != INDEX_FIRST_MIDDLE_LAST) {
  540. // Set the PR_DISPLAY_NAME
  541. sPropVal.ulPropTag = PR_DISPLAY_NAME;
  542. sPropVal.Value.LPSZ = rgItems[index];
  543. // Set the property on the WAB entry
  544. if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SetProps(lpMailUserWAB,
  545. 1, // cValues
  546. &sPropVal, // property array
  547. NULL))) { // problems array
  548. DebugTrace("ImportEntry:SetProps(WAB) -> %x\n", GetScode(hResult));
  549. goto exit;
  550. }
  551. }
  552. // Save the new wab mailuser or distlist
  553. if (HR_FAILED(hResult = lpMailUserWAB->lpVtbl->SaveChanges(lpMailUserWAB,
  554. KEEP_OPEN_READONLY | FORCE_SAVE))) {
  555. if (GetScode(hResult) == MAPI_E_COLLISION) {
  556. /*
  557. // Find the display name
  558. Assert(lpDisplayName);
  559. if (! lpDisplayName) {
  560. DebugTrace("Collision, but can't find PR_DISPLAY_NAME in entry\n");
  561. goto exit;
  562. }
  563. */ // WAB replaces no Display Names with Unknown
  564. // Do we need to prompt?
  565. if (lpOptions->ReplaceOption == WAB_REPLACE_PROMPT) {
  566. // Prompt user with dialog. If they say YES, we should try again
  567. RI.lpszDisplayName = lpDisplayName ? lpDisplayName : "";
  568. RI.lpszEmailAddress = lpEmailAddress;
  569. RI.ConfirmResult = CONFIRM_ERROR;
  570. RI.lpImportOptions = lpOptions;
  571. DialogBoxParam(hInst,
  572. MAKEINTRESOURCE(IDD_ImportReplace),
  573. hWnd,
  574. ReplaceDialogProc,
  575. (LPARAM)&RI);
  576. switch (RI.ConfirmResult) {
  577. case CONFIRM_YES:
  578. case CONFIRM_YES_TO_ALL:
  579. // YES
  580. // NOTE: recursive Migrate will fill in the SeenList entry
  581. // go try again!
  582. lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
  583. lpMailUserWAB = NULL;
  584. ulCreateFlags |= CREATE_REPLACE;
  585. goto retry;
  586. break;
  587. case CONFIRM_ABORT:
  588. hResult = ResultFromScode(MAPI_E_USER_CANCEL);
  589. goto exit;
  590. default:
  591. // NO
  592. break;
  593. }
  594. }
  595. hResult = hrSuccess;
  596. } else {
  597. DebugTrace("SaveChanges(WAB MailUser) -> %x\n", GetScode(hResult));
  598. }
  599. }
  600. // Clean up
  601. if (rgItems) {
  602. for (i = 0; i < cProps; i++) {
  603. if (rgItems[i]) {
  604. LocalFree(rgItems[i]);
  605. }
  606. }
  607. LocalFree(rgItems);
  608. rgItems = NULL;
  609. }
  610. // Update progress bar
  611. Progress.numerator++;
  612. // TEST CODE!
  613. if (Progress.numerator == Progress.denominator) {
  614. // Done? Do I need to do anything?
  615. }
  616. lpProgressCB(hWnd, &Progress);
  617. if (lpMailUserWAB) {
  618. lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
  619. lpMailUserWAB = NULL;
  620. }
  621. if (lpDisplayName) {
  622. LocalFree(lpDisplayName);
  623. lpDisplayName = NULL;
  624. }
  625. // if (hResult) {
  626. // if (HandleExportError(hWnd, 0, hResult, lpRow->aRow[0].lpProps[iptaColumnsPR_DISPLAY_NAME].Value.LPSZ)) {
  627. // hResult = ResultFromScode(MAPI_E_USER_CANCEL);
  628. // } else {
  629. // hResult = hrSuccess;
  630. // }
  631. // }
  632. }
  633. exit:
  634. if (hFile) {
  635. CloseHandle(hFile);
  636. }
  637. if (lpDisplayName) {
  638. LocalFree(lpDisplayName);
  639. }
  640. // Don't free lpEmailAddress! It's part of the rgItems below.
  641. // Free the WAB objects
  642. if (lpMailUserWAB) {
  643. lpMailUserWAB->lpVtbl->Release(lpMailUserWAB);
  644. }
  645. if (lpContainer) {
  646. lpContainer->lpVtbl->Release(lpContainer);
  647. }
  648. // Free the prop name strings
  649. for (i = 0; i < NUM_EXPORT_PROPS; i++) {
  650. if (rgPropNames[i].lpszName) {
  651. LocalFree(rgPropNames[i].lpszName);
  652. }
  653. }
  654. // Free any CSV attributes left
  655. if (rgItems) {
  656. for (i = 0; i < cProps; i++) {
  657. if (rgItems[i]) {
  658. LocalFree(rgItems[i]);
  659. }
  660. }
  661. LocalFree(rgItems);
  662. }
  663. if (lpCreateEIDsWAB) {
  664. WABFreeBuffer(lpCreateEIDsWAB);
  665. lpCreateEIDsWAB = NULL;
  666. }
  667. return(hResult);
  668. }
  669. /***************************************************************************
  670. Name : CreateCSVFile
  671. Purpose : Creates a CSV file for export
  672. Parameters: hwnd = main dialog window
  673. lpFileName = filename to create
  674. lphFile -> returned file handle
  675. Returns : HRESULT
  676. Comment :
  677. ***************************************************************************/
  678. HRESULT CreateCSVFile(HWND hwnd, LPTSTR lpFileName, LPHANDLE lphFile) {
  679. LPTSTR lpFilter;
  680. TCHAR szFileName[MAX_PATH + 1] = "";
  681. OPENFILENAME ofn;
  682. HANDLE hFile = INVALID_HANDLE_VALUE;
  683. HRESULT hResult = hrSuccess;
  684. if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
  685. GENERIC_WRITE,
  686. 0, // sharing
  687. NULL,
  688. CREATE_NEW,
  689. FILE_FLAG_SEQUENTIAL_SCAN,
  690. NULL))) {
  691. if (GetLastError() == ERROR_FILE_EXISTS) {
  692. // Ask user if they want to overwrite
  693. switch (ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_EXISTS, MB_ICONEXCLAMATION | MB_YESNO | MB_SETFOREGROUND, lpFileName)) {
  694. case IDYES:
  695. if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpFileName,
  696. GENERIC_WRITE,
  697. 0, // sharing
  698. NULL,
  699. CREATE_ALWAYS,
  700. FILE_FLAG_SEQUENTIAL_SCAN,
  701. NULL))) {
  702. ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
  703. hResult = ResultFromScode(MAPI_E_NOT_FOUND);
  704. }
  705. break;
  706. default:
  707. DebugTrace("ShowMessageBoxParam gave unknown return\n");
  708. case IDNO:
  709. // nothing to do here
  710. hResult = ResultFromScode(MAPI_E_USER_CANCEL);
  711. break;
  712. }
  713. } else {
  714. ShowMessageBoxParam(hwnd, IDE_CSV_EXPORT_FILE_ERROR, MB_ICONERROR, lpFileName);
  715. hResult = ResultFromScode(MAPI_E_NOT_FOUND);
  716. }
  717. }
  718. if (! hResult) {
  719. *lphFile = hFile;
  720. }
  721. return(hResult);
  722. }
  723. /***************************************************************************
  724. Name : WriteCSV
  725. Purpose : Writes a string to a CSV file with fixups for special characters
  726. Parameters: hFile = file handle
  727. fFixup = TRUE if we should check for special characters
  728. lpString = nul-terminated string to write
  729. szSep = list separator (only needed if fFixup is TRUE)
  730. Returns : HRESULT
  731. Comment : CSV special characters are szSep, CR and LF. If they occur in
  732. the string, we should wrap the entire string in quotes.
  733. ***************************************************************************/
  734. HRESULT WriteCSV(HANDLE hFile, BOOL fFixup, const UCHAR * lpString, LPTSTR szSep) {
  735. HRESULT hResult = hrSuccess;
  736. ULONG cWrite = lstrlen((LPTSTR)lpString);
  737. ULONG cbWritten;
  738. BOOL fQuote = FALSE;
  739. register ULONG i;
  740. ULONG ec;
  741. LPTSTR szSepT;
  742. // Is there a szSep, a CR or a LF in the string?
  743. // If so, enclose the string in quotes.
  744. if (fFixup) {
  745. szSepT = szSep;
  746. for (i = 0; i < cWrite && ! fQuote; i++) {
  747. if (lpString[i] == (UCHAR)(*szSepT)) {
  748. szSepT++;
  749. if (*szSepT == '\0')
  750. fQuote = TRUE;
  751. } else {
  752. szSepT = szSep;
  753. if ((lpString[i] == '\n') || (lpString[i] == '\r'))
  754. fQuote = TRUE;
  755. }
  756. }
  757. }
  758. if (fQuote) {
  759. if (! WriteFile(hFile,
  760. szQuote,
  761. 1,
  762. &cbWritten,
  763. NULL)) {
  764. ec = GetLastError();
  765. DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
  766. if (ec == ERROR_HANDLE_DISK_FULL ||
  767. ec == ERROR_DISK_FULL) {
  768. hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
  769. } else {
  770. hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  771. }
  772. goto exit;
  773. }
  774. }
  775. if (! WriteFile(hFile,
  776. lpString,
  777. cWrite,
  778. &cbWritten,
  779. NULL)) {
  780. ec = GetLastError();
  781. DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
  782. if (ec == ERROR_HANDLE_DISK_FULL ||
  783. ec == ERROR_DISK_FULL) {
  784. hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
  785. } else {
  786. hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  787. }
  788. goto exit;
  789. }
  790. if (fQuote) {
  791. if (! WriteFile(hFile,
  792. szQuote,
  793. 1,
  794. &cbWritten,
  795. NULL)) {
  796. ec = GetLastError();
  797. DebugTrace("WriteCSV:WriteFile -> %u\n", ec);
  798. if (ec == ERROR_HANDLE_DISK_FULL ||
  799. ec == ERROR_DISK_FULL) {
  800. hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_DISK);
  801. } else {
  802. hResult = ResultFromScode(MAPI_E_DISK_ERROR);
  803. }
  804. goto exit;
  805. }
  806. }
  807. exit:
  808. return(hResult);
  809. }
  810. HRESULT ExportCSVMailUser(HANDLE hFile,
  811. ULONG ulPropNames,
  812. ULONG ulLastProp,
  813. LPPROP_NAME lpPropNames,
  814. LPSPropTagArray lppta,
  815. LPTSTR szSep,
  816. LPADRBOOK lpAdrBook,
  817. ULONG cbEntryID,
  818. LPENTRYID lpEntryID) {
  819. HRESULT hResult = hrSuccess;
  820. LPMAILUSER lpMailUser = NULL;
  821. ULONG ulObjType;
  822. ULONG cProps;
  823. LPSPropValue lpspv = NULL;
  824. ULONG i;
  825. const UCHAR szCRLF[] = "\r\n";
  826. UCHAR szBuffer[11] = "";
  827. if (hResult = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
  828. cbEntryID,
  829. lpEntryID,
  830. NULL,
  831. 0,
  832. &ulObjType,
  833. (LPUNKNOWN *)&lpMailUser)) {
  834. DebugTrace("WAB OpenEntry(mailuser) -> %x\n", GetScode(hResult));
  835. goto exit;
  836. }
  837. if ((HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser,
  838. lppta,
  839. 0,
  840. &cProps,
  841. &lpspv)))) {
  842. DebugTrace("ExportCSVMailUser: GetProps() -> %x\n", GetScode(hResult));
  843. goto exit;
  844. }
  845. for (i = 0; i < ulPropNames; i++) {
  846. if (rgPropNames[i].fChosen) {
  847. // Output the value
  848. switch (PROP_TYPE(lpspv[i].ulPropTag)) {
  849. case PT_TSTRING:
  850. if (hResult = WriteCSV(hFile, TRUE, lpspv[i].Value.LPSZ, szSep)) {
  851. goto exit;
  852. }
  853. break;
  854. case PT_LONG:
  855. wnsprintf(szBuffer, ARRAYSIZE(szBuffer), "%u", lpspv[i].Value.l);
  856. if (hResult = WriteCSV(hFile, TRUE, szBuffer, szSep)) {
  857. goto exit;
  858. }
  859. break;
  860. default:
  861. DebugTrace("CSV export: unsupported property 0x%08x\n", lpspv[i].ulPropTag);
  862. Assert(FALSE);
  863. // fall through to skip
  864. case PT_ERROR:
  865. // skip it.
  866. break;
  867. }
  868. if (i != ulLastProp) {
  869. // Output the seperator
  870. if (hResult = WriteCSV(hFile, FALSE, szSep, NULL)) {
  871. goto exit;
  872. }
  873. }
  874. }
  875. }
  876. if (hResult = WriteCSV(hFile, FALSE, szCRLF, NULL)) {
  877. goto exit;
  878. }
  879. exit:
  880. if (lpspv) {
  881. WABFreeBuffer(lpspv);
  882. }
  883. if (lpMailUser) {
  884. lpMailUser->lpVtbl->Release(lpMailUser);
  885. }
  886. return(hResult);
  887. }
  888. HRESULT CSVExport(HWND hWnd,
  889. LPADRBOOK lpAdrBook,
  890. LPWABOBJECT lpWABObject,
  891. LPWAB_PROGRESS_CALLBACK lpProgressCB,
  892. LPWAB_EXPORT_OPTIONS lpOptions) {
  893. HRESULT hResult = hrSuccess;
  894. register ULONG i;
  895. ULONG cbWABEID, ulObjType;
  896. ULONG ulLastChosenProp = 0;
  897. WAB_PROGRESS Progress;
  898. ULONG cRows = 0;
  899. LPSRowSet lpRow = NULL;
  900. ULONG ulCount = 0;
  901. SRestriction restrictObjectType;
  902. SPropValue spvObjectType;
  903. LPENTRYID lpWABEID = NULL;
  904. LPABCONT lpContainer = NULL;
  905. LPMAPITABLE lpContentsTable = NULL;
  906. HANDLE hFile = INVALID_HANDLE_VALUE;
  907. LPSPropTagArray lppta = NULL;
  908. const UCHAR szCRLF[] = "\r\n";
  909. TCHAR szSep[MAX_SEP];
  910. TCHAR rgFileName[MAX_PATH + 1] = "";
  911. SetGlobalBufferFunctions(lpWABObject);
  912. // Read in the Property Name strings
  913. for (i = 0; i < NUM_EXPORT_PROPS; i++) {
  914. rgPropNames[i].lpszName = LoadAllocString(rgPropNames[i].ids);
  915. Assert(rgPropNames[i].lpszName);
  916. DebugTrace("Property 0x%08x name: %s\n", rgPropNames[i].ulPropTag, rgPropNames[i].lpszName);
  917. }
  918. // Present UI Wizard
  919. if (hResult = ExportWizard(hWnd, rgFileName, ARRAYSIZE(rgFileName), rgPropNames)) {
  920. goto exit;
  921. }
  922. // Find the last prop name chosen
  923. for (i = NUM_EXPORT_PROPS - 1; i > 0; i--) {
  924. if (rgPropNames[i].fChosen) {
  925. ulLastChosenProp = i;
  926. break;
  927. }
  928. }
  929. //
  930. // Open the WAB's PAB container
  931. //
  932. if (hResult = lpAdrBook->lpVtbl->GetPAB(lpAdrBook,
  933. &cbWABEID,
  934. &lpWABEID)) {
  935. DebugTrace("WAB GetPAB -> %x\n", GetScode(hResult));
  936. goto exit;
  937. } else {
  938. if (hResult = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook,
  939. cbWABEID, // size of EntryID to open
  940. lpWABEID, // EntryID to open
  941. NULL, // interface
  942. 0, // flags
  943. &ulObjType,
  944. (LPUNKNOWN *)&lpContainer)) {
  945. DebugTrace("WAB OpenEntry(PAB) -> %x\n", GetScode(hResult));
  946. goto exit;
  947. }
  948. }
  949. //
  950. // All set... now loop through the WAB's entries, exporting each one
  951. //
  952. if (HR_FAILED(hResult = lpContainer->lpVtbl->GetContentsTable(lpContainer,
  953. 0, // ulFlags
  954. &lpContentsTable))) {
  955. DebugTrace("WAB GetContentsTable(PAB Table) -> %x\n", GetScode(hResult));
  956. goto exit;
  957. }
  958. // Set the columns to those we're interested in
  959. if (hResult = lpContentsTable->lpVtbl->SetColumns(lpContentsTable,
  960. (LPSPropTagArray)&ptaColumns,
  961. 0)) {
  962. DebugTrace("WAB SetColumns(PAB Table) -> %x\n", GetScode(hResult));
  963. goto exit;
  964. }
  965. // Restrict the table to MAPI_MAILUSERs
  966. spvObjectType.ulPropTag = PR_OBJECT_TYPE;
  967. spvObjectType.Value.l = MAPI_MAILUSER;
  968. restrictObjectType.rt = RES_PROPERTY;
  969. restrictObjectType.res.resProperty.relop = RELOP_EQ;
  970. restrictObjectType.res.resProperty.ulPropTag = PR_OBJECT_TYPE;
  971. restrictObjectType.res.resProperty.lpProp = &spvObjectType;
  972. if (HR_FAILED(hResult = lpContentsTable->lpVtbl->Restrict(lpContentsTable,
  973. &restrictObjectType,
  974. 0))) {
  975. DebugTrace("WAB Restrict (MAPI_MAILUSER) -> %x\n", GetScode(hResult));
  976. goto exit;
  977. }
  978. // How many MailUser entries are there?
  979. ulcEntries = CountRows(lpContentsTable, FALSE);
  980. DebugTrace("WAB contains %u MailUser entries\n", ulcEntries);
  981. if (ulcEntries == 0) {
  982. DebugTrace("WAB has no entries, nothing to export.\n");
  983. goto exit;
  984. }
  985. // Initialize the Progress Bar
  986. Progress.denominator = max(ulcEntries, 1);
  987. Progress.numerator = 0;
  988. Progress.lpText = NULL;
  989. lpProgressCB(hWnd, &Progress);
  990. // Write out the property names
  991. GetListSeparator(szSep);
  992. // Create the file (and handle error UI)
  993. if (hResult = CreateCSVFile(hWnd, rgFileName, &hFile)) {
  994. goto exit;
  995. }
  996. for (i = 0; i < NUM_EXPORT_PROPS; i++) {
  997. // Output the name
  998. if (rgPropNames[i].fChosen) {
  999. if (hResult = WriteCSV(hFile, TRUE, rgPropNames[i].lpszName, szSep)) {
  1000. goto exit;
  1001. }
  1002. if (i != ulLastChosenProp) {
  1003. // Output the seperator
  1004. if (hResult = WriteCSV(hFile, FALSE, szSep, NULL)) {
  1005. goto exit;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. if (hResult = WriteCSV(hFile, FALSE, szCRLF, NULL)) {
  1011. goto exit;
  1012. }
  1013. // Map the prop name array to a SPropTagArray.
  1014. lppta = LocalAlloc(LPTR, CbNewSPropTagArray(NUM_EXPORT_PROPS));
  1015. lppta->cValues = NUM_EXPORT_PROPS;
  1016. for (i = 0; i < lppta->cValues; i++) {
  1017. lppta->aulPropTag[i] = rgPropNames[i].ulPropTag;
  1018. }
  1019. cRows = 1;
  1020. while (cRows && hResult == hrSuccess) {
  1021. // Get the next WAB entry
  1022. if (hResult = lpContentsTable->lpVtbl->QueryRows(lpContentsTable,
  1023. 1, // one row at a time
  1024. 0, // ulFlags
  1025. &lpRow)) {
  1026. DebugTrace("QueryRows -> %x\n", GetScode(hResult));
  1027. goto exit;
  1028. }
  1029. if (lpRow) {
  1030. if (cRows = lpRow->cRows) { // Yes, single '='
  1031. Assert(lpRow->cRows == 1);
  1032. Assert(lpRow->aRow[0].cValues == iptaColumnsMax);
  1033. Assert(lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].ulPropTag == PR_ENTRYID);
  1034. Assert(lpRow->aRow[0].lpProps[iptaColumnsPR_OBJECT_TYPE].ulPropTag == PR_OBJECT_TYPE);
  1035. if (cRows = lpRow->cRows) { // yes, single '='
  1036. // Export mailuser
  1037. if (hResult = ExportCSVMailUser(hFile,
  1038. NUM_EXPORT_PROPS,
  1039. ulLastChosenProp,
  1040. rgPropNames,
  1041. lppta,
  1042. szSep,
  1043. lpAdrBook,
  1044. lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].Value.bin.cb,
  1045. (LPENTRYID)lpRow->aRow[0].lpProps[iptaColumnsPR_ENTRYID].Value.bin.lpb)) {
  1046. goto exit;
  1047. }
  1048. // Update progress bar
  1049. Progress.numerator++;
  1050. lpProgressCB(hWnd, &Progress);
  1051. if (hResult) {
  1052. if (HandleExportError(hWnd,
  1053. 0,
  1054. hResult,
  1055. lpRow->aRow[0].lpProps[iptaColumnsPR_DISPLAY_NAME].Value.LPSZ,
  1056. PropStringOrNULL(&lpRow->aRow[0].lpProps[iptaColumnsPR_EMAIL_ADDRESS]),
  1057. lpOptions)) {
  1058. hResult = ResultFromScode(MAPI_E_USER_CANCEL);
  1059. } else {
  1060. hResult = hrSuccess;
  1061. }
  1062. }
  1063. } // else, drop out of loop, we're done.
  1064. }
  1065. WABFreeProws(lpRow);
  1066. }
  1067. }
  1068. exit:
  1069. if (hFile) {
  1070. CloseHandle(hFile);
  1071. }
  1072. if (lppta) {
  1073. LocalFree(lppta);
  1074. }
  1075. // Free the WAB objects
  1076. WABFreeBuffer(lpWABEID);
  1077. lpWABEID = NULL;
  1078. if (lpContainer) {
  1079. lpContainer->lpVtbl->Release(lpContainer);
  1080. lpContainer = NULL;
  1081. }
  1082. if (lpContentsTable) {
  1083. lpContentsTable->lpVtbl->Release(lpContentsTable);
  1084. lpContentsTable = NULL;
  1085. }
  1086. // Free the prop name strings
  1087. for (i = 0; i < NUM_EXPORT_PROPS; i++) {
  1088. if (rgPropNames[i].lpszName) {
  1089. LocalFree(rgPropNames[i].lpszName);
  1090. }
  1091. }
  1092. return(hResult);
  1093. }