//////////////////////////////////////////////////////////////////////////////// // // UIMISC.C - common miscellaneous functions used by the UI // // //////////////////////////////////////////////////////////////////////////////// #include "_apipch.h" const LPTSTR szLDAPDefaultCountryValue = TEXT("LDAP Default Country"); const LPTSTR szTrailingDots = TEXT("..."); const LPTSTR szArrow = TEXT(" ->"); const LPTSTR szBackSlash = TEXT("\\"); extern BOOL bDNisByLN; extern BOOL bIsPasteData(); HINSTANCE ghCommDlgInst = NULL; extern HINSTANCE ghCommCtrlDLLInst; extern ULONG gulCommCtrlDLLRefCount; extern void SetVirtualPABEID(LPIAB lpIAB, ULONG * lpcb, LPENTRYID * lppb); extern void vTurnOffAllNotifications(); extern void vTurnOnAllNotifications(); LP_INITCOMMONCONTROLSEX gpfnInitCommonControlsEx = NULL; LPIMAGELIST_SETBKCOLOR gpfnImageList_SetBkColor = NULL; LPIMAGELIST_DRAW gpfnImageList_Draw = NULL; LPIMAGELIST_DESTROY gpfnImageList_Destroy = NULL; LPIMAGELIST_LOADIMAGE_A gpfnImageList_LoadImageA = NULL; LPPROPERTYSHEET_A gpfnPropertySheetA = NULL; LP_CREATEPROPERTYSHEETPAGE_A gpfnCreatePropertySheetPageA = NULL; LPIMAGELIST_LOADIMAGE_W gpfnImageList_LoadImageW = NULL; LPPROPERTYSHEET_W gpfnPropertySheetW = NULL; LP_CREATEPROPERTYSHEETPAGE_W gpfnCreatePropertySheetPageW = NULL; // CommCtrl function names static const TCHAR cszCommCtrlClientDLL[] = TEXT("COMCTL32.DLL"); static const char cszInitCommonControlsEx[] = "InitCommonControlsEx"; static const char cszImageList_SetBkColor[] = "ImageList_SetBkColor"; static const char cszImageList_LoadImageA[] = "ImageList_LoadImageA"; static const char cszPropertySheetA[] = "PropertySheetA"; static const char cszCreatePropertySheetPageA[] = "CreatePropertySheetPageA"; static const char cszImageList_LoadImageW[] = "ImageList_LoadImageW"; static const char cszPropertySheetW[] = "PropertySheetW"; static const char cszCreatePropertySheetPageW[] = "CreatePropertySheetPageW"; static const char cszImageList_Draw[] = "ImageList_Draw"; static const char cszImageList_Destroy[] = "ImageList_Destroy"; // API table for CommonControl function addresses to fetch #define NUM_CommCtrlAPI_PROCS 10 APIFCN CommCtrlAPIList[NUM_CommCtrlAPI_PROCS] = { { (PVOID *) &gpfnInitCommonControlsEx, cszInitCommonControlsEx}, { (PVOID *) &gpfnImageList_SetBkColor, cszImageList_SetBkColor}, { (PVOID *) &gpfnImageList_Draw, cszImageList_Draw}, { (PVOID *) &gpfnImageList_Destroy, cszImageList_Destroy}, { (PVOID *) &gpfnImageList_LoadImageA, cszImageList_LoadImageA}, { (PVOID *) &gpfnPropertySheetA, cszPropertySheetA}, { (PVOID *) &gpfnCreatePropertySheetPageA, cszCreatePropertySheetPageA}, { (PVOID *) &gpfnImageList_LoadImageW, cszImageList_LoadImageW}, { (PVOID *) &gpfnPropertySheetW, cszPropertySheetW}, { (PVOID *) &gpfnCreatePropertySheetPageW, cszCreatePropertySheetPageW} }; #ifdef COLSEL_MENU // for menu->column selection mapping #define MAXNUM_MENUPROPS 12 const ULONG MenuToPropTagMap[] = { PR_HOME_TELEPHONE_NUMBER, PR_BUSINESS_TELEPHONE_NUMBER, PR_PAGER_TELEPHONE_NUMBER, PR_CELLULAR_TELEPHONE_NUMBER, PR_BUSINESS_FAX_NUMBER, PR_HOME_FAX_NUMBER, PR_COMPANY_NAME, PR_TITLE, PR_DEPARTMENT_NAME, PR_OFFICE_LOCATION, PR_BIRTHDAY, PR_WEDDING_ANNIVERSARY }; #endif // COLSEL_MENU void CleanAddressString(TCHAR * szAddress); static const LPTSTR g_szComDlg32 = TEXT("COMDLG32.DLL"); // Delay load substitutes for commdlg functions // BOOL (*pfnGetOpenFileNameA)(LPOPENFILENAMEA pof); BOOL (*pfnGetOpenFileNameW)(LPOPENFILENAMEW pof); BOOL GetOpenFileName(LPOPENFILENAME pof) { // static BOOL (*pfnGetOpenFileName)(LPOPENFILENAME pof); if(!ghCommDlgInst) ghCommDlgInst = LoadLibrary(g_szComDlg32); if(ghCommDlgInst) { if ( pfnGetOpenFileNameA == NULL ) pfnGetOpenFileNameA = (BOOL (*)(LPOPENFILENAMEA))GetProcAddress(ghCommDlgInst, "GetOpenFileNameA"); if ( pfnGetOpenFileNameW == NULL ) pfnGetOpenFileNameW = (BOOL (*)(LPOPENFILENAMEW))GetProcAddress(ghCommDlgInst, "GetOpenFileNameW"); if (pfnGetOpenFileNameA && pfnGetOpenFileNameW) return pfnGetOpenFileName(pof); } return -1; } BOOL (*pfnGetSaveFileNameA)(LPOPENFILENAMEA pof); BOOL (*pfnGetSaveFileNameW)(LPOPENFILENAMEW pof); BOOL GetSaveFileName(LPOPENFILENAME pof) { // static BOOL (*pfnGetSaveFileName)(LPOPENFILENAME pof); if(!ghCommDlgInst) ghCommDlgInst = LoadLibrary(g_szComDlg32); if(ghCommDlgInst) { if ( pfnGetSaveFileNameA == NULL ) pfnGetSaveFileNameA = (BOOL (*)(LPOPENFILENAMEA))GetProcAddress(ghCommDlgInst, "GetSaveFileNameA"); if ( pfnGetSaveFileNameW == NULL ) pfnGetSaveFileNameW = (BOOL (*)(LPOPENFILENAMEW))GetProcAddress(ghCommDlgInst, "GetSaveFileNameW"); if ( pfnGetSaveFileNameA && pfnGetSaveFileNameW ) return pfnGetSaveFileName(pof); } return -1; } BOOL (*pfnPrintDlgA)(LPPRINTDLGA lppd); BOOL (*pfnPrintDlgW)(LPPRINTDLGW lppd); BOOL PrintDlg(LPPRINTDLG lppd) { // static BOOL (*pfnPrintDlg)(LPPRINTDLG lppd); if(!ghCommDlgInst) ghCommDlgInst = LoadLibrary(g_szComDlg32); if(ghCommDlgInst) { if ( pfnPrintDlgA == NULL ) pfnPrintDlgA = (BOOL (*)(LPPRINTDLGA))GetProcAddress(ghCommDlgInst, "PrintDlgA"); if ( pfnPrintDlgW == NULL ) pfnPrintDlgW = (BOOL (*)(LPPRINTDLGW))GetProcAddress(ghCommDlgInst, "PrintDlgW"); if ( pfnPrintDlgA && pfnPrintDlgW ) return pfnPrintDlg(lppd); } return -1; } /* - PrintDlgEx - - Loads the PrintDlgEx from the ComDlg32.dll - If lppdex is NULL, then just loads and returns S_OK (this way we test for support for PrintDlgEx - on the current system .. instead of trying to look at the OS version etc) - - Returns MAPI_E_NOT_FOUND if no support on OS - */ HRESULT (*pfnPrintDlgExA)(LPPRINTDLGEXA lppdex); HRESULT (*pfnPrintDlgExW)(LPPRINTDLGEXW lppdex); HRESULT PrintDlgEx(LPPRINTDLGEX lppdex) { // static HRESULT (*pfnPrintDlgEx)(LPPRINTDLGEX lppdex); if(!ghCommDlgInst) ghCommDlgInst = LoadLibrary(g_szComDlg32); if(ghCommDlgInst) { if ( pfnPrintDlgExA == NULL ) pfnPrintDlgExA = (HRESULT (*)(LPPRINTDLGEXA))GetProcAddress(ghCommDlgInst, "PrintDlgExA"); if ( pfnPrintDlgExW == NULL ) pfnPrintDlgExW = (HRESULT (*)(LPPRINTDLGEXW))GetProcAddress(ghCommDlgInst, "PrintDlgExW"); if (!pfnPrintDlgExA || !pfnPrintDlgExW) { DebugTrace( TEXT("PrintDlgEx not found - %d\n"),GetLastError()); return MAPI_E_NOT_FOUND; } if(!lppdex) return S_OK; //just testing for presence of this function return pfnPrintDlgEx(lppdex); } return E_FAIL; } extern void DeinitCommDlgLib() { if(ghCommDlgInst) { FreeLibrary(ghCommDlgInst); ghCommDlgInst = NULL; } } //$$ // // HandleSaveChangedInsufficientDiskSpace - Called when savechanges returns // insufficient disk space. If user selects to proceed // // HRESULT HandleSaveChangedInsufficientDiskSpace(HWND hWnd, LPMAILUSER lpMailUser) { HRESULT hr = MAPI_E_NOT_ENOUGH_DISK; while(hr == MAPI_E_NOT_ENOUGH_DISK) { if(IDOK == ShowMessageBox( hWnd, idsNotEnoughDiskSpace, MB_OKCANCEL | MB_ICONEXCLAMATION)) { // try saving again hr = lpMailUser->lpVtbl->SaveChanges( lpMailUser, KEEP_OPEN_READWRITE); } else hr = MAPI_E_USER_CANCEL; } return hr; } //$$//////////////////////////////////////////////////////////////// // // SetRecipColumns - sets the columns we want to populate the // RECIPIENTINFO item structures with // ////////////////////////////////////////////////////////////////// #define RECIPCOLUMN_CONTACT_EMAIL_ADDRESSES 7 // Keep this in sync with ptaRecipArray below HRESULT SetRecipColumns(LPMAPITABLE lpContentsTable) { HRESULT hr = S_OK; SizedSPropTagArray(16, ptaRecipArray) = { 16, { PR_DISPLAY_NAME, PR_SURNAME, PR_GIVEN_NAME, PR_MIDDLE_NAME, PR_COMPANY_NAME, PR_NICKNAME, PR_EMAIL_ADDRESS, PR_CONTACT_EMAIL_ADDRESSES, // [PaulHi] Use for PR_EMAIL_ADDRESS if no PR_EMAIL_ADDRESS exists PR_ENTRYID, PR_OBJECT_TYPE, PR_USER_X509_CERTIFICATE, PR_HOME_TELEPHONE_NUMBER, PR_OFFICE_TELEPHONE_NUMBER, PR_WAB_THISISME, PR_WAB_YOMI_FIRSTNAME, //keep these ruby props at the end of the list PR_WAB_YOMI_LASTNAME, } }; if(PR_WAB_CUSTOMPROP1) ptaRecipArray.aulPropTag[11] = PR_WAB_CUSTOMPROP1; if(PR_WAB_CUSTOMPROP2) ptaRecipArray.aulPropTag[12] = PR_WAB_CUSTOMPROP2; if(!bIsRubyLocale()) // Don't ask for Ruby Props if we don't need em ptaRecipArray.cValues -= 2; hr =lpContentsTable->lpVtbl->SetColumns(lpContentsTable, (LPSPropTagArray)&ptaRecipArray, 0); return hr; } //$$//////////////////////////////////////////////////////////////// // // GetABContentsList Gets a contents list // // hPropertyStore handle to property store - this can be null for // non-property store containers // cbContEntryID entryid of container // lpContEntryID cont entry id // lpPTA, Array of prop tags to fill in the list view // Can be null - in which case default array will be used // lpPropRes Filter which caller can supply - if null TEXT("DisplayName") is the default // ulFlags Used with Filter - either 0 or AB_MATCH_PROP_ONLY // bGetProfileContents - If TRUE and profiles, gets full list of profile contents - if false // IF FALSE, checks if profiles are ON and gets container contents.. // lppContentsList Returned Contents list pointing off to entries // ////////////////////////////////////////////////////////////////// HRESULT HrGetWABContentsList( LPIAB lpIAB, SORT_INFO SortInfo, LPSPropTagArray lpPTA, LPSPropertyRestriction lpPropRes, ULONG ulFlags, LPSBinary lpsbContainer, BOOL bGetProfileContents, LPRECIPIENT_INFO * lppContentsList) { HRESULT hr = hrSuccess; ULONG i = 0,j=0; LPRECIPIENT_INFO lpItem = NULL; LPRECIPIENT_INFO lpLastListItem = NULL; HANDLE hPropertyStore = lpIAB->lpPropertyStore->hPropertyStore; SPropertyRestriction PropRes = {0}; ULONG ulContentsTableFlags = MAPI_UNICODE | WAB_CONTENTTABLE_NODATA; ULONG ulcPropCount = 0; LPULONG lpPropTagArray = NULL; LPCONTENTLIST lpContentList = NULL; /****/ LPCONTAINER lpContainer = NULL; LPMAPITABLE lpContentsTable = NULL; LPSRowSet lpSRowSet = NULL; ULONG cbContainerEID = 0; LPENTRYID lpContainerEID = NULL; ULONG ulObjectType = 0; if(lpsbContainer) { cbContainerEID = lpsbContainer->cb; lpContainerEID = (LPENTRYID)lpsbContainer->lpb; } if(!cbContainerEID || !lpContainerEID) { // When calling GetPAB, this will normally return the users contact folder // In this case (where we havent been asked to get all the profile contents, // this implies that without container info, we should get the virtual // folder contents if(!bGetProfileContents) SetVirtualPABEID((LPIAB)lpIAB, &cbContainerEID, &lpContainerEID); hr = lpIAB->lpVtbl->GetPAB(lpIAB, &cbContainerEID, &lpContainerEID); if(HR_FAILED(hr)) goto out; } // // First we need to open the container object corresponding to this Container EntryID // hr = lpIAB->lpVtbl->OpenEntry( lpIAB, cbContainerEID, lpContainerEID, NULL, 0, &ulObjectType, (LPUNKNOWN *) &lpContainer); if(HR_FAILED(hr)) { DebugPrintError(( TEXT("OpenEntry Failed: %x\n"),hr)); goto out; } if(bIsWABSessionProfileAware(lpIAB)) { ulContentsTableFlags |= WAB_ENABLE_PROFILES; if(bGetProfileContents) ulContentsTableFlags |= WAB_PROFILE_CONTENTS; } // // Now we do a get contents table on this container ... // hr = lpContainer->lpVtbl->GetContentsTable( lpContainer, ulContentsTableFlags, &lpContentsTable); if(HR_FAILED(hr)) { DebugPrintError(( TEXT("GetContentsTable Failed: %x\n"),hr)); goto out; } // the default set of columns does not have all the information we are seeking // so we do a set columns hr = SetRecipColumns(lpContentsTable); if(HR_FAILED(hr)) goto out; if(lpPropRes) { SRestriction sr = {0}; sr.rt = RES_PROPERTY; sr.res.resProperty = *lpPropRes; if(HR_FAILED(hr = lpContentsTable->lpVtbl->Restrict(lpContentsTable,&sr,0))) goto out; } hr = HrQueryAllRows(lpContentsTable, NULL, NULL, NULL, 0, &lpSRowSet); if (HR_FAILED(hr)) { DebugPrintError(( TEXT("HrQueryAllRows Failed: %x\n"),hr)); goto out; } // // if there's anything in the contents list flush it away // if (*lppContentsList) { lpItem = (*lppContentsList); (*lppContentsList) = lpItem->lpNext; FreeRecipItem(&lpItem); } *lppContentsList = NULL; lpItem = NULL; for(i=0;icRows;i++) { LPSPropValue lpPropArray = lpSRowSet->aRow[i].lpProps; ULONG ulcPropCount = lpSRowSet->aRow[i].cValues; lpItem = LocalAlloc(LMEM_ZEROINIT, sizeof(RECIPIENT_INFO)); if (!lpItem) { DebugPrintError(( TEXT("LocalAlloc Failed \n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } GetRecipItemFromPropArray(ulcPropCount, lpPropArray, &lpItem); // The critical prop is display name - without it we are nothing ... // If no display name, junk this entry and continue .. if (!lstrlen(lpItem->szDisplayName) || (lpItem->cbEntryID == 0)) //This entry id is not allowed { FreeRecipItem(&lpItem); continue; } // The entryids are in sorted order by display name // Depending on the sort order - we want this list to also be sorted by display // name or by reverse display name ... if (SortInfo.bSortByLastName) StrCpyN(lpItem->szDisplayName,lpItem->szByLastName,ARRAYSIZE(lpItem->szDisplayName)); if(!SortInfo.bSortAscending) { //Add it to the contents linked list lpItem->lpNext = (*lppContentsList); if (*lppContentsList) (*lppContentsList)->lpPrev = lpItem; lpItem->lpPrev = NULL; *lppContentsList = lpItem; } else { if(*lppContentsList == NULL) (*lppContentsList) = lpItem; if(lpLastListItem) lpLastListItem->lpNext = lpItem; lpItem->lpPrev = lpLastListItem; lpItem->lpNext = NULL; lpLastListItem = lpItem; } lpItem = NULL; } //for i .... /*****/ out: /****/ if(lpSRowSet) FreeProws(lpSRowSet); if(lpContentsTable) lpContentsTable->lpVtbl->Release(lpContentsTable); if(lpContainer) lpContainer->lpVtbl->Release(lpContainer); if( (!lpsbContainer || !lpsbContainer->lpb) && lpContainerEID) MAPIFreeBuffer(lpContainerEID); /****/ if (lpContentList) FreePcontentlist(hPropertyStore, lpContentList); if (HR_FAILED(hr)) { while(*lppContentsList) { lpItem = *lppContentsList; *lppContentsList=lpItem->lpNext; FreeRecipItem(&lpItem); } } return hr; } //$$//////////////////////////////////////////////////////////////// // // FreeRecipItem - frees a RECIPIENT_INFO structure // // lppItem - pointer to the lpItem to free. It is set to NULL // ////////////////////////////////////////////////////////////////// void FreeRecipItem(LPRECIPIENT_INFO * lppItem) { LocalFreeAndNull(&(*lppItem)->lpEntryID); LocalFreeAndNull(&(*lppItem)->lpByRubyFirstName); LocalFreeAndNull(&(*lppItem)->lpByRubyLastName); LocalFreeAndNull((lppItem)); return; } //$$//////////////////////////////////////////////////////////////// // // InitListView - initializes a list view with style, columns, // image lists, headers etc // // // HWND hWndLV - Handle of ListView Control // dwStyle - style for list view // bShowHeaders - Show or hide the headers // ////////////////////////////////////////////////////////////////// HRESULT HrInitListView( HWND hWndLV, DWORD dwStyle, BOOL bShowHeaders) { HRESULT hr = hrSuccess; LV_COLUMN lvC; // list view column structure TCHAR szText [MAX_PATH]; // place to store some text RECT rc; HIMAGELIST hSmall=NULL,hLarge=NULL; HFONT hFnt = GetStockObject(DEFAULT_GUI_FONT); DWORD dwLVStyle; ULONG nCols=0; ULONG index=0; if (!hWndLV) { hr = MAPI_E_INVALID_PARAMETER; goto out; } SendMessage(hWndLV, WM_SETFONT, (WPARAM) hFnt, (LPARAM) TRUE); ListView_SetExtendedListViewStyle(hWndLV, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP); dwLVStyle = GetWindowLong(hWndLV,GWL_STYLE); if(( dwLVStyle & LVS_TYPEMASK) != dwStyle) SetWindowLong(hWndLV,GWL_STYLE,(dwLVStyle & ~LVS_TYPEMASK) | dwStyle); dwLVStyle = GetWindowLong(hWndLV,GWL_STYLE); if(( dwLVStyle & LVS_EDITLABELS) != dwStyle) SetWindowLong(hWndLV,GWL_STYLE,(dwLVStyle & ~LVS_EDITLABELS) | dwStyle); hSmall = gpfnImageList_LoadImage( hinstMapiX, MAKEINTRESOURCE(IDB_BITMAP_SMALL), //(LPCTSTR) ((DWORD) ((WORD) (IDB_BITMAP_SMALL))), S_BITMAP_WIDTH, 0, RGB_TRANSPARENT, IMAGE_BITMAP, 0); hLarge = gpfnImageList_LoadImage( hinstMapiX, MAKEINTRESOURCE(IDB_BITMAP_LARGE), //(LPCTSTR) ((DWORD) ((WORD) (IDB_BITMAP_LARGE))), L_BITMAP_WIDTH, 0, RGB_TRANSPARENT, IMAGE_BITMAP, 0); // Associate the image lists with the list view control. ListView_SetImageList (hWndLV, hSmall, LVSIL_SMALL); ListView_SetImageList (hWndLV, hLarge, LVSIL_NORMAL); // make the columns all the same width // Later on in life we will make it so users preferences are stored and then // played back ... nCols = NUM_COLUMNS; if (nCols==0) { DebugPrintError(( TEXT("Zero number of cols??\n"))); hr = E_FAIL; goto out; } GetWindowRect(hWndLV,&rc); lvC.mask = LVCF_FMT | LVCF_WIDTH; lvC.fmt = LVCFMT_LEFT; // left-align column if (bShowHeaders) { lvC.mask |= LVCF_TEXT | LVCF_SUBITEM; // lvC.cx = (rc.right-rc.left)/nCols; // width of column in pixels // if (lvC.cx == 0) lvC.cx = 150; // fix these limits somewhere ... lvC.pszText = szText; } else { // if no headers, we want these to be wide enough to fit all the info lvC.cx = 250; // - change this hardcoding lvC.pszText = NULL; } // Add the columns. for (index = 0; index < nCols; index++) { lvC.iSubItem = index; LoadString (hinstMapiX, lprgAddrBookColHeaderIDs[index], szText, ARRAYSIZE(szText)); if(index == colHomePhone && PR_WAB_CUSTOMPROP1 && lstrlen(szCustomProp1)) StrCpyN(szText, szCustomProp1,ARRAYSIZE(szText)); if(index == colOfficePhone && PR_WAB_CUSTOMPROP2 && lstrlen(szCustomProp2)) StrCpyN(szText, szCustomProp2,ARRAYSIZE(szText)); if((index == colDisplayName) || (index == colEmailAddress)) lvC.cx = 150; else lvC.cx = 100; if (ListView_InsertColumn (hWndLV, index, &lvC) == -1) { DebugPrintError(( TEXT("ListView_InsertColumn Failed\n"))); hr = E_FAIL; goto out; } } out: return hr; } //$$//////////////////////////////////////////////////////////////// /// /// HrFillListView - fills a list view from an lpcontentslist /// /// hWndLV - Handle of List View control to fill /// lpContentsList - LPRECIPIENT_INFO linked list. We walk the list and /// add each item to the list view /// ////////////////////////////////////////////////////////////////// HRESULT HrFillListView( HWND hWndLV, LPRECIPIENT_INFO lpContentsList) { LPRECIPIENT_INFO lpItem = lpContentsList; LV_ITEM lvI = {0}; int index = 0; if ((!hWndLV) || (!lpContentsList)) return MAPI_E_INVALID_PARAMETER; lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_PARAM ; lvI.cchTextMax = MAX_DISPLAY_NAME_LENGTH; lvI.iItem = 0; while(lpItem) { lvI.iImage = GetWABIconImage(lpItem); lvI.iSubItem = colDisplayName; lvI.lParam = (LPARAM) lpItem; lvI.pszText = lpItem->szDisplayName; index = ListView_InsertItem (hWndLV, &lvI); if (index != -1) { if(lstrlen(lpItem->szOfficePhone)) ListView_SetItemText (hWndLV, index, colOfficePhone, lpItem->szOfficePhone); if(lstrlen(lpItem->szHomePhone)) ListView_SetItemText (hWndLV, index, colHomePhone, lpItem->szHomePhone); if(lstrlen(lpItem->szEmailAddress)) ListView_SetItemText (hWndLV, index, colEmailAddress, lpItem->szEmailAddress); } lpItem = lpItem->lpNext; lvI.iItem++; } LVSelectItem(hWndLV, 0); return S_OK; } //$$////////////////////////////////////////////////////////////////////////////// // // TrimSpaces - strips a string of leading and trailing blanks // // szBuf - pointer to buffer containing the string we want to strip spaces off. // //////////////////////////////////////////////////////////////////////////////// BOOL TrimSpaces(TCHAR * szBuf) { register LPTSTR lpTemp = szBuf; if(!szBuf || !lstrlen(szBuf)) return FALSE; // Trim leading spaces while (IsSpace(lpTemp)) { lpTemp = CharNext(lpTemp); } if (lpTemp != szBuf) { // Leading spaces to trim StrCpyN(szBuf, lpTemp, lstrlen(lpTemp)+1); lpTemp = szBuf; } if (*lpTemp == '\0') { // empty string return(TRUE); } // Move to the end lpTemp += lstrlen(lpTemp); lpTemp--; // Walk backwards, triming spaces while (IsSpace(lpTemp) && lpTemp > szBuf) { *lpTemp = '\0'; lpTemp = CharPrev(szBuf, lpTemp); } return(TRUE); } //$$/**************************************************************************** /* * FUNCTION: ListViewSort(LPARAM, LPARAM, LPARAM) * * PURPOSE: Callback function that sorts depending on the column click * * lParam1, lParam2 - lParam of the elements being compared * lParamSort - User defined data that identifies the sort criteria * ****************************************************************************/ int CALLBACK ListViewSort(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { LPRECIPIENT_INFO lp1 = (LPRECIPIENT_INFO)lParam1; LPRECIPIENT_INFO lp2 = (LPRECIPIENT_INFO)lParam2; LPTSTR lpStr1, lpStr2, lpF1, lpL1, lpF2, lpL2; int iResult = 0; LPSORT_INFO lpSortInfo = (LPSORT_INFO) lParamSort; if (lp1 && lp2) { switch( lpSortInfo->iOldSortCol) { case colDisplayName: // sort by Address lpF1 = lp1->lpByRubyFirstName ? lp1->lpByRubyFirstName : lp1->szByFirstName; lpL1 = lp1->lpByRubyLastName ? lp1->lpByRubyLastName : lp1->szByLastName; lpF2 = lp2->lpByRubyFirstName ? lp2->lpByRubyFirstName : lp2->szByFirstName; lpL2 = lp2->lpByRubyLastName ? lp2->lpByRubyLastName : lp2->szByLastName; lpStr1 = lpSortInfo->bSortByLastName ? lpL1 : lpF1; lpStr2 = lpSortInfo->bSortByLastName ? lpL2 : lpF2; iResult = lstrcmpi(lpStr1, lpStr2); break; case colEmailAddress: // sort by Address lpStr1 = lp1->szEmailAddress; lpStr2 = lp2->szEmailAddress; iResult = lstrcmpi(lpStr1, lpStr2); break; case colHomePhone: // sort by Address lpStr1 = lp1->szHomePhone; lpStr2 = lp2->szHomePhone; iResult = lstrcmpi(lpStr1, lpStr2); break; case colOfficePhone: // sort by Address lpStr1 = lp1->szOfficePhone; lpStr2 = lp2->szOfficePhone; iResult = lstrcmpi(lpStr1, lpStr2); break; default: iResult = 0; break; } } return(lpSortInfo->bSortAscending ? iResult : -1*iResult); } //$$**************************************************************************** /* * SetColumnHeaderBmp * * PURPOSE: Sets the bmp on the ListView Column header to indicate sorting * * hWndLV - handle of List View * SortInfo - The current Sort Information structure. It is used to determine * where to put the sort header bitmap ****************************************************************************/ void SetColumnHeaderBmp(HWND hWndLV, SORT_INFO SortInfo) { LV_COLUMN lvc = {0}; HIMAGELIST hHeader = NULL; HWND hWndLVHeader = NULL; //POINT pt; // we will try to get the hWnd for the ListView header and set its image lists //pt.x = 1; //pt.y = 1; //hWndLVHeader = ChildWindowFromPoint (hWndLV, pt); hWndLVHeader = ListView_GetHeader(hWndLV); // NULL hChildWnd means R-CLICKED outside the listview. // hChildWnd == ghwndLV means listview got clicked: NOT the // header. if ((hWndLVHeader) /*&& (hWndLVHeader != hWndLV)*/) { hHeader = (HIMAGELIST) SendMessage(hWndLVHeader,HDM_GETIMAGELIST,0,0); gpfnImageList_SetBkColor(hHeader, GetSysColor(COLOR_BTNFACE)); SendMessage(hWndLVHeader, HDM_SETIMAGELIST, 0, (LPARAM) hHeader); } if (SortInfo.iOlderSortCol != SortInfo.iOldSortCol) { //Get rid of image from old column lvc.mask = LVCF_FMT; lvc.fmt = LVCFMT_LEFT; ListView_SetColumn(hWndLV, SortInfo.iOlderSortCol, &lvc); } // Set new column icon. lvc.mask = LVCF_IMAGE | LVCF_FMT; lvc.fmt = LVCFMT_IMAGE | LVCFMT_BITMAP_ON_RIGHT; lvc.iImage = SortInfo.bSortAscending ? imageSortAscending : imageSortDescending; ListView_SetColumn(hWndLV, SortInfo.iOldSortCol, &lvc); return; } //$$////////////////////////////////////////////////////////////////////////// /// /// ClearListView - Clears all the list view items and associated contents list /// /// hWndLV - list view to clear out /// lppContentsList - contents list correponding to the contents in the /// list view /// /////////////////////////////////////////////////////////////////////////////// void ClearListView(HWND hWndLV, LPRECIPIENT_INFO * lppContentsList) { /* LPRECIPIENT_INFO lpItem = *lppContentsList; int i =0; int iItemIndex = ListView_GetItemCount(hWndLV); //OutputDebugString( TEXT("ClearListView entry\n")); if (iItemIndex <=0 ) goto out; for(i=0;ilpNext) lpItem->lpNext->lpPrev = lpItem->lpPrev; if(lpItem->lpPrev) lpItem->lpPrev->lpNext = lpItem->lpNext; if (*lppContentsList == lpItem) *lppContentsList = lpItem->lpNext; if (lpItem) FreeRecipItem(&lpItem); } } ListView_DeleteAllItems(hWndLV); *lppContentsList = NULL; out: //OutputDebugString( TEXT("ClearListView exit\n")); */ ListView_DeleteAllItems(hWndLV); FreeRecipList(lppContentsList); return; }; //$$////////////////////////////////////////////////////////////////////// // // DeleteSelectedItems - Delete all the selected items from the List View // // hWndLV -handle of List View // lpIAB - handle to current AdrBook object - used for certificate stuff // hPropertyStore - Handle of PropertyStore - change this function to // call deleteEntries instead of delete record. // lpftLast - WAB file time at last update // ////////////////////////////////////////////////////////////////////////// void DeleteSelectedItems(HWND hWndLV, LPADRBOOK lpAdrBook, HANDLE hPropertyStore, LPFILETIME lpftLast) { int iItemIndex; int nSelected; LV_ITEM LVItem; HWND hDlg = GetParent(hWndLV); HRESULT hr = hrSuccess; ULONG cbWABEID = 0; LPENTRYID lpWABEID = NULL; LPABCONT lpWABCont = NULL; ULONG ulObjType,i=0; SBinaryArray SBA = {0}; nSelected = ListView_GetSelectedCount(hWndLV); if (nSelected <= 0) { ShowMessageBox(GetParent(hWndLV), IDS_ADDRBK_MESSAGE_NO_ITEMS_DELETE, MB_ICONEXCLAMATION); hr = E_FAIL; goto out; } hr = lpAdrBook->lpVtbl->GetPAB(lpAdrBook, &cbWABEID, &lpWABEID); if(HR_FAILED(hr)) goto out; hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, cbWABEID, // size of EntryID to open lpWABEID, // EntryID to open NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *)&lpWABCont); if(HR_FAILED(hr)) goto out; if (IDYES == ShowMessageBox(hDlg, IDS_ADDRBK_MESSAGE_DELETE, MB_ICONEXCLAMATION | MB_YESNO)) { int iLastDeletedItemIndex; BOOL bDeletedItem = FALSE; DWORD dwLVStyle = 0; BOOL bWasShowSelAlwaysStyle = FALSE; HCURSOR hOldCur = SetCursor(LoadCursor(NULL,IDC_WAIT)); ULONG ulCount = 0; SendMessage(hWndLV, WM_SETREDRAW, FALSE, 0); // The list view may be set to ShowSelAlways style - // When deleting, we normally look for the selected entries and // delete them - but with this style, the list view automatically selects the // next entry - which is problematic because then we end up deleting that // one also ... so we need to unset the style now and set it later dwLVStyle = GetWindowLong(hWndLV,GWL_STYLE); if( dwLVStyle & LVS_SHOWSELALWAYS) { SetWindowLong(hWndLV,GWL_STYLE,dwLVStyle & ~LVS_SHOWSELALWAYS); bWasShowSelAlwaysStyle = TRUE; } if(!(SBA.lpbin = LocalAlloc(LMEM_ZEROINIT, sizeof(SBinary)*nSelected))) goto out; iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); do { // otherwise get the entry id of this thing LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex); if (lpItem) { SetSBinary(&(SBA.lpbin[ulCount]), lpItem->cbEntryID, (LPBYTE)lpItem->lpEntryID); ulCount++; } iLastDeletedItemIndex = iItemIndex; iItemIndex = ListView_GetNextItem(hWndLV,iItemIndex,LVNI_SELECTED); } while (iItemIndex != -1); SBA.cValues = ulCount; hr = lpWABCont->lpVtbl->DeleteEntries( lpWABCont, (LPENTRYLIST) &SBA, 0); // Ideally DeleteEntries will skip over errors silently so we have a dilemma here // that if there are errors,do we knock out the corresponding items out of the UI or not .. // For now, lets knock them out of the UI .. when the UI refreshes, this will sort itself out .. // iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); do { ListView_DeleteItem(hWndLV,iItemIndex); iLastDeletedItemIndex = iItemIndex; iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); } while (iItemIndex != -1); bDeletedItem = TRUE; /* 33751 - need to fail silently here .. else { ShowMessageBoxParam(hDlg, IDS_ADDRBK_MESSAGE_DELETING_ERROR, MB_ICONERROR, lpItem->szDisplayName); iLastDeletedItemIndex = iItemIndex; break; } */ // reset the style if we changed it if(bWasShowSelAlwaysStyle ) SetWindowLong(hWndLV,GWL_STYLE,dwLVStyle); SetCursor(hOldCur); // select the previous or next item ... if (iLastDeletedItemIndex >= ListView_GetItemCount(hWndLV)) iLastDeletedItemIndex = ListView_GetItemCount(hWndLV)-1; LVSelectItem(hWndLV, iLastDeletedItemIndex); } out: SendMessage(hWndLV, WM_SETREDRAW, TRUE, 0); if(SBA.lpbin && SBA.cValues) { for(i=0;i= cchFileType1); if (cchTemp >= cchFileType1) { StrCpyN(lpTemp, lpFileType1, cchFileType1); lpTemp += cchFileType1; cchTemp -= cchFileType1; Assert(cchTemp >= cchFilter1); if (cchTemp >= cchFilter1) { StrCpyN(lpTemp, lpFilter1, cchFilter1); lpTemp += cchFilter1; cchTemp -= cchFilter1; LocalFree(lpFileType1); if (cchFileType2 && cchFilter2) { Assert(cchTemp >= (cchFileType2 + cchFilter2)); if (cchTemp >= (cchFileType2 + cchFilter2)) { StrCpyN(lpTemp, lpFileType2, cchFileType2); lpTemp += cchFileType2; cchTemp -= cchFileType2; StrCpyN(lpTemp, lpFilter2, cchFilter2); lpTemp += cchFilter2; cchTemp -= cchFilter2; LocalFree(lpFileType2); } } if (cchFileType3 && cchFilter3) { Assert(cchTemp >= (cchFileType3 + cchFilter3)); if (cchTemp >= (cchFileType3 + cchFilter3)) { StrCpyN(lpTemp, lpFileType3, cchFileType3); lpTemp += cchFileType3; cchTemp -= cchFileType3; StrCpyN(lpTemp, lpFilter3, cchFilter3); lpTemp += cchFilter3; cchTemp -= cchFilter3; LocalFree(lpFileType3); } } } } } if (lpTemp) { if ((cchTemp >0) && (cchTemp < cchBuffer)) // ensure we have room and didn't wrap *lpTemp = '\0'; else { Assert(FALSE); LocalFree(lpBuffer); lpBuffer = NULL; } } return(lpBuffer); } const LPTSTR szVCardFilter = TEXT("*.vcf"); /*************************************************************************** Name : VCardCreate Purpose : Creates a vCard file from the given Mailuser and filename Parameters: hwnd = hwndParent lpIAB -> IAddrBook object, ulFlags can be 0 or MAPI_DIALOG - MAPI_DIALOG means report error messages in a dialog box, else work silently .. lpszFileNAme - vCard file name to create lpMailUser - object to create vCard file from Returns : HRESULT Comment : ***************************************************************************/ HRESULT VCardCreate( LPADRBOOK lpAdrBook, HWND hWndParent, ULONG ulFlags, LPTSTR lpszFileName, LPMAILUSER lpMailUser) { HRESULT hr = E_FAIL; HANDLE hVCard = NULL; if (INVALID_HANDLE_VALUE == (hVCard = CreateFile( lpszFileName, GENERIC_WRITE, 0, // sharing NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL))) { if(ulFlags & MAPI_DIALOG) { ShowMessageBoxParam(hWndParent, IDE_VCARD_EXPORT_FILE_ERROR, MB_ICONERROR, lpszFileName); } goto out; } if (hr = WriteVCard(hVCard, FileWriteFn, lpMailUser)) { switch (GetScode(hr)) { case WAB_E_VCARD_NOT_ASCII: if(ulFlags & MAPI_DIALOG) { ShowMessageBoxParam(hWndParent, IDS_VCARD_EXPORT_NOT_ASCII, MB_ICONEXCLAMATION, lpszFileName); } CloseHandle(hVCard); hVCard = NULL; DeleteFile(lpszFileName); hr = E_FAIL; break; default: if(ulFlags & MAPI_DIALOG) { ShowMessageBoxParam(hWndParent, IDE_VCARD_EXPORT_FILE_ERROR, MB_ICONERROR, lpszFileName); } break; } } out: if (hVCard) CloseHandle(hVCard); return hr; } //$$////////////////////////////////////////////////////////////////////// // // VCardExportSelectedItems - Export all the selected items from the List View // to vCard files. // // hWndLV - handle of List view. We look up the selected item in this list // view, get its lParam structure, then get its EntryID and // call details // lpIAB - handle to current AdrBook object - used for calling details // ////////////////////////////////////////////////////////////////////////// HRESULT VCardExportSelectedItems(HWND hWndLV, LPADRBOOK lpAdrBook) { HRESULT hr = E_FAIL; int iItemIndex; HWND hWndParent = GetParent(hWndLV); HANDLE hVCard = NULL; OPENFILENAME ofn; LPMAILUSER lpEntry = NULL; LPTSTR lpFilter = NULL; TCHAR szFileName[MAX_PATH + 1] = TEXT(""); LPTSTR lpTitle = NULL; LPTSTR lpTitleFormat = NULL; ULONG ulObjType; LPTSTR lpszArg[1]; TCHAR szTmp[MAX_PATH]; // Open props if only 1 item is selected iItemIndex = ListView_GetSelectedCount(hWndLV); if (iItemIndex == 1) { // Get index of selected item iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); if (iItemIndex != -1) { LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);; if(lpItem && lpItem->cbEntryID != 0) { StrCpyN(szFileName, lpItem->szDisplayName, ARRAYSIZE(szFileName)); TrimIllegalFileChars(szFileName); if(lstrlen(szFileName)) StrCatBuff(szFileName, TEXT(".vcf"), ARRAYSIZE(szFileName)); if (hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, lpItem->cbEntryID, lpItem->lpEntryID, NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *)&lpEntry)) { DebugTraceResult( TEXT("VCardExportSelectedItems:OpenEntry"), hr); goto exit; } if (ulObjType == MAPI_DISTLIST) { ShowMessageBox(hWndParent, IDE_VCARD_EXPORT_DISTLIST, MB_ICONEXCLAMATION); goto exit; } lpFilter = FormatAllocFilter(IDS_VCARD_FILE_SPEC, szVCardFilter, 0, NULL, 0, NULL); lpTitleFormat = LoadAllocString(IDS_VCARD_EXPORT_TITLE); // Win9x bug FormatMessage cannot have more than 1023 chars CopyTruncate(szTmp, lpItem->szDisplayName, MAX_PATH - 1); lpszArg[0] = szTmp; if (! FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, lpTitleFormat, 0, // stringid 0, // dwLanguageId (LPTSTR)&lpTitle, // output buffer 0, //MAX_UI_STR (va_list *)lpszArg)) { DebugTrace( TEXT("FormatMessage -> %u\n"), GetLastError()); } ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hWndParent; ofn.hInstance = hinstMapiX; ofn.lpstrFilter = lpFilter; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 0; ofn.lpstrFile = szFileName; ofn.nMaxFile = ARRAYSIZE(szFileName); ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = lpTitle; ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = TEXT("vcf"); ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (GetSaveFileName(&ofn)) { //Check if file already exists .. if(0xFFFFFFFF != GetFileAttributes(szFileName)) { // Ask user if they want to overwrite if(IDNO == ShowMessageBoxParam(hWndParent, IDE_VCARD_EXPORT_FILE_EXISTS, MB_ICONEXCLAMATION | MB_YESNO | MB_SETFOREGROUND, szFileName)) { hr = MAPI_E_USER_CANCEL; goto exit; } } // Go ahead and overwrite the file if user said yes.. if(hr = VCardCreate(lpAdrBook, hWndParent, MAPI_DIALOG, szFileName, lpEntry)) { goto exit; } } // if GetSaveFileName... } // if (lpItem->cbEntryID)... } } else { if (iItemIndex <= 0) { // nothing selected ShowMessageBox(GetParent(hWndLV), IDS_ADDRBK_MESSAGE_NO_ITEM, MB_ICONEXCLAMATION); } else { //multiple selected ShowMessageBox(GetParent(hWndLV), IDS_ADDRBK_MESSAGE_ACTION, MB_ICONEXCLAMATION); } hr = E_FAIL; goto exit; } hr = S_OK; exit: UlRelease(lpEntry); LocalFreeAndNull(&lpFilter); LocalFree(lpTitleFormat); if(lpTitle) LocalFree(lpTitle); return(hr); } /*************************************************************************** Name : VCardRetrive Purpose : Retrieves a MailUser object from a given file name Parameters: hwnd = hwndParent lpIAB -> IAddrBook object, ulFlags can be 0 or MAPI_DIALOG - MAPI_DIALOG means report error messages in a dialog box, else work silently .. lpszFileNAme - vCard file name (file must exist) lpszBuf - a memory buffer containing the vCard file which can be specified instead of the filename Must be a null terminated string lppMailUser, returned MailUser ... Returns : HRESULT Comment : ***************************************************************************/ HRESULT VCardRetrieve(LPADRBOOK lpAdrBook, HWND hWndParent, ULONG ulFlags, LPTSTR lpszFileName, LPSTR lpszBuf, LPMAILUSER * lppMailUser) { HRESULT hResult = E_FAIL; HANDLE hFile = NULL; LPSTR lpBuf = NULL; SBinary sb = {0}; LPMAILUSER lpMailUser = NULL; // We will convert the vCard to a memory buffer and parse that buffer as needed // Somewhere in the buffer we need to track how much of the buffer has already // been parsed .. we'll polymorph a SBinary struct here so we can use the cb param // to track how much buffer has been parsed and the lpb to store the buffer SBinary buf = {0}; if(!VCardGetBuffer(lpszFileName, lpszBuf, &lpBuf)) { if(ulFlags & MAPI_DIALOG) { // couldn't open file. ShowMessageBoxParam(hWndParent, IDE_VCARD_IMPORT_FILE_ERROR, MB_ICONEXCLAMATION, lpszFileName); } goto out; } if(hResult = lpAdrBook->lpVtbl->GetPAB(lpAdrBook, &sb.cb, (LPENTRYID *)&sb.lpb)) goto out; if (hResult = HrCreateNewObject( lpAdrBook, &sb, MAPI_MAILUSER, CREATE_CHECK_DUP_STRICT, (LPMAPIPROP *) &lpMailUser)) { goto out; } buf.cb = 0; buf.lpb = (LPBYTE) lpBuf; //if (hResult = ReadVCard(hFile, FileReadFn, *lppMailUser)) if (hResult = ReadVCard((HANDLE) &buf, BufferReadFn, lpMailUser)) { if(ulFlags & MAPI_DIALOG) { switch (GetScode(hResult)) { case MAPI_E_INVALID_OBJECT: ShowMessageBoxParam(hWndParent, IDE_VCARD_IMPORT_FILE_BAD, MB_ICONEXCLAMATION, lpszFileName); goto out; default: ShowMessageBoxParam(hWndParent, IDE_VCARD_IMPORT_PARTIAL, MB_ICONEXCLAMATION, lpszFileName); break; } } } out: if(lpBuf) LocalFree(lpBuf); if(sb.lpb) MAPIFreeBuffer(sb.lpb); if(lpMailUser) { if(HR_FAILED(hResult)) lpMailUser->lpVtbl->Release(lpMailUser); else *lppMailUser = lpMailUser; } return hResult; } /*************************************************************************** Name : VCardImport Purpose : Reads a vCard from a file to a new MAILUSER object. Parameters: hwnd = hwnd lpIAB -> IAddrBook object szVCardFile - name of the file to import if we already know it in which case there is no OpenFileName dialog The entryids of the newly added objects are added to the SPropValue which is a dummy prop of type MV_BINARY Returns : HRESULT Comment : ***************************************************************************/ HRESULT VCardImport(HWND hWnd, LPADRBOOK lpAdrBook, LPTSTR szVCardFile, LPSPropValue * lppProp) { HRESULT hResult = hrSuccess; OPENFILENAME ofn; LPTSTR lpFilter = FormatAllocFilter(IDS_VCARD_FILE_SPEC, szVCardFilter, 0, NULL, 0, NULL); TCHAR szFileName[MAX_PATH + 1] = TEXT(""); HANDLE hFile = NULL; ULONG ulObjType; ULONG cProps; LPMAILUSER lpMailUser = NULL, lpMailUserNew = NULL; ULONG ulCreateFlags = CREATE_CHECK_DUP_STRICT; BOOL bChangesMade = FALSE; LPSPropValue lpspvEID = NULL; LPSTR lpBuf = NULL, lpVCardStart = NULL; LPSTR lpVCard = NULL, lpNext = NULL; LPSPropValue lpProp = NULL; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hWnd; ofn.hInstance = hinstMapiX; ofn.lpstrFilter = lpFilter; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 0; ofn.lpstrFile = szFileName; ofn.nMaxFile = ARRAYSIZE(szFileName); ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = LoadAllocString(IDS_VCARD_IMPORT_TITLE); ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = TEXT("vcf"); ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if(szVCardFile && lstrlen(szVCardFile)) StrCpyN(szFileName, szVCardFile, ARRAYSIZE(szFileName)); else if (!GetOpenFileName(&ofn)) goto exit; if(lstrlen(szFileName)) { if(!VCardGetBuffer(szFileName, NULL, &lpBuf)) { // couldn't open file. ShowMessageBoxParam(hWnd, IDE_VCARD_IMPORT_FILE_ERROR, MB_ICONEXCLAMATION, szFileName); goto exit; } lpVCardStart = lpBuf; // Loop through showing all the nested vCards one by one .. while(VCardGetNextBuffer(lpVCardStart, &lpVCard, &lpNext) && lpVCard) { if(!HR_FAILED( hResult = VCardRetrieve( lpAdrBook, hWnd, MAPI_DIALOG, szFileName, lpVCard, &lpMailUser))) { if (!HR_FAILED(hResult = HrShowDetails(lpAdrBook, hWnd, NULL, 0, NULL, NULL, NULL, (LPMAPIPROP)lpMailUser, SHOW_OBJECT, MAPI_MAILUSER, &bChangesMade))) { if (hResult = lpMailUser->lpVtbl->SaveChanges(lpMailUser, KEEP_OPEN_READONLY)) { switch(hResult) { case MAPI_E_COLLISION: { LPSPropValue lpspv1 = NULL, lpspv2 = NULL; if (! (hResult = HrGetOneProp((LPMAPIPROP)lpMailUser, PR_DISPLAY_NAME, &lpspv1))) { switch (ShowMessageBoxParam(hWnd, IDS_VCARD_IMPORT_COLLISION, MB_YESNOCANCEL | MB_ICONEXCLAMATION | MB_APPLMODAL | MB_SETFOREGROUND, lpspv1->Value.LPSZ, szFileName)) { case IDYES: // Yes, replace // Create a new one with the right flags, copy the old one's props and save. ulCreateFlags |= ( CREATE_REPLACE | CREATE_MERGE ); if(!HR_FAILED(hResult = HrCreateNewObject(lpAdrBook, ((LPMailUser)lpMailUser)->pmbinOlk, MAPI_MAILUSER, ulCreateFlags, (LPMAPIPROP *)&lpMailUserNew))) { if (!HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser,NULL,MAPI_UNICODE,&cProps,&lpspv2))) { if (!HR_FAILED(hResult = lpMailUserNew->lpVtbl->SetProps(lpMailUserNew,cProps,lpspv2,NULL))) { hResult = lpMailUserNew->lpVtbl->SaveChanges(lpMailUserNew,KEEP_OPEN_READONLY); } } } break; case IDCANCEL: hResult = ResultFromScode(MAPI_E_USER_CANCEL); break; // no, don't replace default: hResult = E_FAIL; break; } } FreeBufferAndNull(&lpspv1); FreeBufferAndNull(&lpspv2); } break; case MAPI_E_NOT_ENOUGH_DISK: hResult = HandleSaveChangedInsufficientDiskSpace(hWnd, lpMailUser); break; default: if(HR_FAILED(hResult)) ShowMessageBoxParam(hWnd, IDE_VCARD_IMPORT_FILE_BAD, MB_ICONEXCLAMATION, szFileName); break; } } } } if(!lpProp && !HR_FAILED(hResult)) { SCODE sc; if(sc = MAPIAllocateBuffer(sizeof(SPropValue), (LPVOID *)&lpProp)) { hResult = MAPI_E_NOT_ENOUGH_MEMORY; goto exit; } lpProp->ulPropTag = PR_WAB_DL_ENTRIES; // Doesnt matter what we set this to as long as its MV_BINARY lpProp->Value.MVbin.cValues = 0; lpProp->Value.MVbin.lpbin = NULL; } if(lpProp && !HR_FAILED(hResult)) { LPMAILUSER lpMU = (lpMailUserNew) ? lpMailUserNew : lpMailUser; if (! (hResult = HrGetOneProp((LPMAPIPROP)lpMU, PR_ENTRYID, &lpspvEID))) { AddPropToMVPBin(lpProp, 0 , lpspvEID->Value.bin.lpb, lpspvEID->Value.bin.cb, TRUE); FreeBufferAndNull(&lpspvEID); } } if(lpMailUserNew) lpMailUserNew->lpVtbl->Release(lpMailUserNew); if(lpMailUser) lpMailUser->lpVtbl->Release(lpMailUser); lpMailUser = NULL; lpMailUserNew = NULL; if(hResult == MAPI_E_USER_CANCEL) break; lpVCard = NULL; lpVCardStart = lpNext; } } // getopenfilename ... *lppProp = lpProp; exit: LocalFreeAndNull(&lpBuf); LocalFree(lpFilter); LocalFree((LPVOID)ofn.lpstrTitle); if (hFile) CloseHandle(hFile); if(lpMailUser) UlRelease(lpMailUser); if(lpMailUserNew) UlRelease(lpMailUserNew); return(hResult); } #endif //$$////////////////////////////////////////////////////////////////////// // HrShowLVEntryProperties // // Shows the properties of an entry in the list view ... // Assumes that all list views are based on lpRecipientInfo Structures // // hWndLV - handle of List view. We look up the selected item in this list // view, get its lParam structure, then get its EntryID and // call details // lpIAB - handle to current AdrBook object - used for calling details // lpftLast - WAB file time at last update // // Returns:MAPI_E_USER_CANCEL on cancel // MAPI_E_OBJECT_CHANGED if object was modified // S_OK if no changes and nothing modified ////////////////////////////////////////////////////////////////////////// HRESULT HrShowLVEntryProperties(HWND hWndLV, ULONG ulFlags, LPADRBOOK lpAdrBook, LPFILETIME lpftLast) { HRESULT hr = E_FAIL; int iItemIndex; HWND hWndParent = GetParent(hWndLV); LPRECIPIENT_INFO lpNewItem=NULL; // Open props if only 1 item is selected iItemIndex = ListView_GetSelectedCount(hWndLV); if (iItemIndex == 1) { // Get index of selected item iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); if (iItemIndex != -1) { LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);; if(lpItem && lpItem->cbEntryID != 0) { hr = lpAdrBook->lpVtbl->Details(lpAdrBook, (PULONG_PTR) &hWndParent, // ulUIParam NULL, NULL, lpItem->cbEntryID, lpItem->lpEntryID, NULL, NULL, NULL, ulFlags); // if details changed something - our event and semaphores should // notify us so we can update ourselves from the property store or // whatever ... // this is TBD - at this point there is no way to refresh anything .. if(HR_FAILED(hr)) { goto out; } else //if(!HR_FAILED(hr)) { //Open the item again and compare its UI props to see //if anything changed ... if(0 != IsWABEntryID(lpItem->cbEntryID, lpItem->lpEntryID, NULL, NULL, NULL, NULL, NULL)) { // the above test may not be good enough .. // we really need to check if its a wab entryid .. // // This is not a WAB entry ID // For now assume this is a read only contact and so // we dont need to check it again for changes // goto out; } if(!ReadSingleContentItem( lpAdrBook, lpItem->cbEntryID, lpItem->lpEntryID, &lpNewItem)) goto out; // Compare the new item with the old item // If anything changed, we need to update the item in the list view if (lstrcmp(lpItem->szDisplayName,lpNewItem->szDisplayName)) { hr = MAPI_E_OBJECT_CHANGED; ListView_SetItemText(hWndLV,iItemIndex,colDisplayName,lpNewItem->szDisplayName); StrCpyN(lpItem->szDisplayName,lpNewItem->szDisplayName,ARRAYSIZE(lpItem->szDisplayName)); } if (lstrcmp(lpItem->szEmailAddress,lpNewItem->szEmailAddress)) { hr = MAPI_E_OBJECT_CHANGED; ListView_SetItemText(hWndLV,iItemIndex,colEmailAddress,lpNewItem->szEmailAddress); StrCpyN(lpItem->szEmailAddress,lpNewItem->szEmailAddress,ARRAYSIZE(lpItem->szEmailAddress)); } if (lstrcmp(lpItem->szHomePhone,lpNewItem->szHomePhone)) { hr = MAPI_E_OBJECT_CHANGED; ListView_SetItemText(hWndLV,iItemIndex,colHomePhone,lpNewItem->szHomePhone); StrCpyN(lpItem->szHomePhone,lpNewItem->szHomePhone,ARRAYSIZE(lpItem->szHomePhone)); } if (lstrcmp(lpItem->szOfficePhone,lpNewItem->szOfficePhone)) { hr = MAPI_E_OBJECT_CHANGED; ListView_SetItemText(hWndLV,iItemIndex,colOfficePhone,lpNewItem->szOfficePhone); StrCpyN(lpItem->szOfficePhone,lpNewItem->szOfficePhone,ARRAYSIZE(lpItem->szOfficePhone)); } if (lstrcmp(lpItem->szByLastName,lpNewItem->szByLastName)) { hr = MAPI_E_OBJECT_CHANGED; StrCpyN(lpItem->szByLastName,lpNewItem->szByLastName,ARRAYSIZE(lpItem->szByLastName)); } if (lstrcmp(lpItem->szByFirstName,lpNewItem->szByFirstName)) { hr = MAPI_E_OBJECT_CHANGED; StrCpyN(lpItem->szByFirstName,lpNewItem->szByFirstName,ARRAYSIZE(lpItem->szByFirstName)); } { LVITEM lvI = {0}; lvI.mask = LVIF_IMAGE; lvI.iItem = iItemIndex; lvI.iSubItem = 0; lpItem->bHasCert = lpNewItem->bHasCert; lpItem->bIsMe = lpNewItem->bIsMe; lvI.iImage = GetWABIconImage(lpItem); ListView_SetItem(hWndLV, &lvI); } // Update the wab file write time so the timer doesn't // catch this change and refresh. //if (lpftLast && // lpItem->ulObjectType == MAPI_MAILUSER) // refresh for distlists not for mailusers (because distlists can cause further modifications) //{ // CheckChangedWAB(((LPIAB)lpIAB)->lpPropertyStore, lpftLast); //} } } } } else { if (iItemIndex <= 0) { // nothing selected ShowMessageBox(GetParent(hWndLV), IDS_ADDRBK_MESSAGE_NO_ITEM, MB_ICONEXCLAMATION); } else { //multiple selected ShowMessageBox(GetParent(hWndLV), IDS_ADDRBK_MESSAGE_ACTION, MB_ICONEXCLAMATION); } hr = E_FAIL; goto out; } out: if(hr == MAPI_E_NOT_FOUND) ShowMessageBox(GetParent(hWndLV), idsEntryNotFound, MB_OK | MB_ICONEXCLAMATION); if(lpNewItem) FreeRecipItem(&lpNewItem); return hr; } //$$////////////////////////////////////////////////////////////////////// // // LVSelectItem - Selects a list view item and ensures it is visible // // hWndList - handle of list view control // iItemIndex - index of item to select // //////////////////////////////////////////////////////////////////////// void LVSelectItem(HWND hWndList, int iItemIndex) { DWORD dwStyle; // Hopefully, we only want to select a single item // So we cheat by making the ListView single select and // set our item, reseting everything else dwStyle = GetWindowLong(hWndList, GWL_STYLE); SetWindowLong(hWndList, GWL_STYLE, dwStyle | LVS_SINGLESEL); ListView_SetItemState ( hWndList, // handle to listview iItemIndex, // index to listview item LVIS_FOCUSED | LVIS_SELECTED, // item state LVIS_FOCUSED | LVIS_SELECTED); // mask ListView_EnsureVisible (hWndList, // handle to listview iItemIndex, FALSE); //reset back to the original style .. SetWindowLong(hWndList, GWL_STYLE, dwStyle); return; } //$$////////////////////////////////////////////////////////////////////////////// /// /// AddWABEntryToListView - Adds a wab entry to a list view given a entryid /// /// lpIAB - handle to AdrBook object /// hWndLV - list view of interest /// lpEID - EntryID of entry. Assumes size of entryid is WAB_ENTRY_ID /// lppContentsList - List into which the entry is also linked /// /// //////////////////////////////////////////////////////////////////////////////// BOOL AddWABEntryToListView( LPADRBOOK lpAdrBook, HWND hWndLV, ULONG cbEID, LPENTRYID lpEID, LPRECIPIENT_INFO * lppContentsList) { BOOL bRet = FALSE; LPRECIPIENT_INFO lpItem = NULL; LV_ITEM lvi = {0}; int index = 0; if (!lpEID) goto out; if (!ReadSingleContentItem( lpAdrBook, cbEID, lpEID, &lpItem)) goto out; AddSingleItemToListView(hWndLV, lpItem); //we added to the end - so this is the last item //select it ... index = ListView_GetItemCount(hWndLV); LVSelectItem(hWndLV, index-1); // // Hook in the lpItem into the lpContentsList so we can free it later // lpItem->lpPrev = NULL; lpItem->lpNext = *lppContentsList; if (*lppContentsList) (*lppContentsList)->lpPrev = lpItem; (*lppContentsList) = lpItem; bRet = TRUE; out: if (!bRet && lpItem) FreeRecipItem(&lpItem); return bRet; } //$$//////////////////////////////////////////////////////////////////////////// // // AddSingleItemToListView - Takes a single lpItem and adds it to alist view // // hWndLV - handle of List View // lpItem - Recipient Info corresponding to a single entry // ////////////////////////////////////////////////////////////////////////////// void AddSingleItemToListView(HWND hWndLV, LPRECIPIENT_INFO lpItem) { LV_ITEM lvI = {0}; int index = 0; // Add just a single item ... lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_PARAM ; lvI.cchTextMax = MAX_DISPLAY_NAME_LENGTH; lvI.iImage = GetWABIconImage(lpItem); lvI.iItem = ListView_GetItemCount(hWndLV); lvI.iSubItem = colDisplayName; lvI.lParam = (LPARAM) lpItem; lvI.pszText = lpItem->szDisplayName; index = ListView_InsertItem (hWndLV, &lvI); if (index == -1) { DebugPrintError(( TEXT("ListView_InsertItem Failed\n"))); goto out; } // TBD - this is assuming that all the fields exist and are filled in if(lstrlen(lpItem->szOfficePhone)) ListView_SetItemText (hWndLV, index, colOfficePhone, lpItem->szOfficePhone); if(lstrlen(lpItem->szHomePhone)) ListView_SetItemText (hWndLV, index, colHomePhone, lpItem->szHomePhone); if(lstrlen(lpItem->szEmailAddress)) ListView_SetItemText (hWndLV, index, colEmailAddress, lpItem->szEmailAddress); out: return; } //$$//////////////////////////////////////////////////////////////////////////// // // ReadSingeContentItem - reads a specified record from the prop store // and creates a single pointer item for the Address Linked list and // content window. // // lpIAB - pointer to AdrBook Object // cbEntryID - EntryID byte count of object of interest // lpEntryID - EntryID of object of interest // lppItem - returned lppItem // ////////////////////////////////////////////////////////////////////////////// BOOL ReadSingleContentItem( LPADRBOOK lpAdrBook, ULONG cbEntryID, LPENTRYID lpEntryID, LPRECIPIENT_INFO * lppItem) { LPSPropValue lpPropArray = NULL; ULONG ulcProps = 0; ULONG nLen = 0; ULONG i = 0; BOOL bDisplayNameSet = FALSE; BOOL bEmailAddressSet = FALSE; BOOL bRet = FALSE; (*lppItem) = LocalAlloc(LMEM_ZEROINIT,sizeof(RECIPIENT_INFO)); if(!(*lppItem)) { DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n"))); goto out; } (*lppItem)->lpNext = NULL; (*lppItem)->lpPrev = NULL; if (HR_FAILED( HrGetPropArray( lpAdrBook, NULL, cbEntryID, lpEntryID, MAPI_UNICODE, &ulcProps, &lpPropArray) ) ) { DebugPrintError(( TEXT("HrGetPropArray failed\n"))); goto out; } GetRecipItemFromPropArray(ulcProps, lpPropArray, lppItem); //Bug- // 3/31/97 - vikramm // on NTDSDC5.0, we are getting no attributes back in some cases // and later on gpf when we try to look at the attributes .. // make a check here if (!lstrlen((*lppItem)->szDisplayName) || ((*lppItem)->cbEntryID == 0)) //This entry id is not allowed { goto out; } bRet = TRUE; out: if (lpPropArray) MAPIFreeBuffer(lpPropArray); if (!bRet) { if (*lppItem) FreeRecipItem(lppItem); } return bRet; } /* - - bIsRubyLocale - Checks if the current locale is Rubyenabled - */ BOOL bIsRubyLocale() { static LCID lcid = 0; if(!lcid) { lcid = GetUserDefaultLCID(); //DebugTrace( TEXT("UserDefaultLCID = 0x%.4x\n"), lcid); } switch(lcid) { //case 0x0409: // us for testing case 0x0804: //chinese case 0x0411: //japanese // case 0x0412: //korean - not use Ruby (YST) case 0x0404: //chinese - taiwan case 0x0c04: //chinese - hongkong return TRUE; break; } return FALSE; } /* - TimeToString - * Converts a FileTime prop into a short string */ void TimeToString(LPTSTR szTime, FILETIME ft,ULONG cb ) { SYSTEMTIME st = {0}; static TCHAR szFormat[64]; szTime[0] = TEXT('\0'); if(!lstrlen(szFormat)) LoadString(hinstMapiX, idsLVDateFormatString, szFormat, ARRAYSIZE(szFormat)); if(FileTimeToSystemTime(&ft, &st)) GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, szFormat, szTime, cb); } //$$//////////////////////////////////////////////////////////////////////////// // // GetRcipItemFromPropArray - Scans a lpPropArray structure for the props of // interest and puts them in an recipientInfo structure // // ulcPropCount - count of Props in the LPSPropValue array // rgPropVals - LPSPropValue array // lppItem - returned lppItem // ////////////////////////////////////////////////////////////////////////////// void GetRecipItemFromPropArray( ULONG ulcPropCount, LPSPropValue rgPropVals, LPRECIPIENT_INFO * lppItem) { ULONG j=0,nLen=0; LPRECIPIENT_INFO lpItem = *lppItem; LPTSTR lpszDisplayName = NULL, lpszNickName = NULL, lpszCompanyName = NULL; LPTSTR lpszFirstName = NULL, lpszLastName = NULL, lpszMiddleName = NULL; LPTSTR lpszRubyFirstName = NULL, lpszRubyLastName = NULL; TCHAR szBufDisplayName[MAX_DISPLAY_NAME_LENGTH]; TCHAR szBufOppositeName[MAX_DISPLAY_NAME_LENGTH]; LPVOID lpBuffer = NULL; ULONG ulProp1, ulProp2; BOOL bRuby = bIsRubyLocale(); ulProp1 = (PR_WAB_CUSTOMPROP1 ? PR_WAB_CUSTOMPROP1 : PR_HOME_TELEPHONE_NUMBER); ulProp2 = (PR_WAB_CUSTOMPROP2 ? PR_WAB_CUSTOMPROP2 : PR_OFFICE_TELEPHONE_NUMBER); for(j=0;jszHomePhone, rgPropVals[j].Value.LPSZ, MAX_DISPLAY_NAME_LENGTH); } else // for birthday, anniversary etc if(PROP_TYPE(rgPropVals[j].ulPropTag) == PT_SYSTIME) TimeToString(lpItem->szHomePhone, rgPropVals[j].Value.ft, MAX_DISPLAY_NAME_LENGTH-1); } else if(rgPropVals[j].ulPropTag == ulProp2) { if(PROP_TYPE(rgPropVals[j].ulPropTag) == PT_TSTRING) { nLen = CopyTruncate(lpItem->szOfficePhone, rgPropVals[j].Value.LPSZ, MAX_DISPLAY_NAME_LENGTH); } else // for birthday, anniversary etc if(PROP_TYPE(rgPropVals[j].ulPropTag) == PT_SYSTIME) TimeToString(lpItem->szOfficePhone, rgPropVals[j].Value.ft,MAX_DISPLAY_NAME_LENGTH-1); } switch(rgPropVals[j].ulPropTag) { case PR_DISPLAY_NAME: lpszDisplayName = rgPropVals[j].Value.LPSZ; break; case PR_SURNAME: lpszLastName = rgPropVals[j].Value.LPSZ; break; case PR_GIVEN_NAME: lpszFirstName = rgPropVals[j].Value.LPSZ; break; case PR_MIDDLE_NAME: lpszMiddleName = rgPropVals[j].Value.LPSZ; break; case PR_COMPANY_NAME: lpszCompanyName = rgPropVals[j].Value.LPSZ; break; case PR_NICKNAME: lpszNickName = rgPropVals[j].Value.LPSZ; break; case PR_EMAIL_ADDRESS: nLen = CopyTruncate(lpItem->szEmailAddress, rgPropVals[j].Value.LPSZ, MAX_DISPLAY_NAME_LENGTH); break; case PR_ENTRYID: lpItem->cbEntryID = rgPropVals[j].Value.bin.cb; lpItem->lpEntryID = LocalAlloc(LMEM_ZEROINIT,lpItem->cbEntryID); if(!(lpItem->lpEntryID)) { DebugPrintError(( TEXT("LocalAlloc failed to allocate memory\n"))); goto out; } CopyMemory(lpItem->lpEntryID,rgPropVals[j].Value.bin.lpb,lpItem->cbEntryID); break; case PR_OBJECT_TYPE: lpItem->ulObjectType = rgPropVals[j].Value.l; break; case PR_USER_X509_CERTIFICATE: lpItem->bHasCert = TRUE; break; default: if(rgPropVals[j].ulPropTag == PR_WAB_THISISME) lpItem->bIsMe = TRUE; else if(rgPropVals[j].ulPropTag == PR_WAB_YOMI_FIRSTNAME) lpszRubyFirstName = rgPropVals[j].Value.LPSZ; else if(rgPropVals[j].ulPropTag == PR_WAB_YOMI_LASTNAME) lpszRubyLastName = rgPropVals[j].Value.LPSZ; break; } } // [PaulHi] 3/12/99 Raid 63006 Use the PR_CONTACT_EMAIL_ADDRESSES email // name if a PR_EMAIL_ADDRESS doesn't exist if ( lpItem->szEmailAddress && (*lpItem->szEmailAddress == '\0') ) { if (rgPropVals[RECIPCOLUMN_CONTACT_EMAIL_ADDRESSES].ulPropTag == PR_CONTACT_EMAIL_ADDRESSES) { // Just grap the first one in multi-valued list if (rgPropVals[RECIPCOLUMN_CONTACT_EMAIL_ADDRESSES].Value.MVSZ.cValues != 0) { nLen = CopyTruncate(lpItem->szEmailAddress, rgPropVals[RECIPCOLUMN_CONTACT_EMAIL_ADDRESSES].Value.MVSZ.LPPSZ[0], MAX_DISPLAY_NAME_LENGTH); } } } // Reduce display name to 32 char or less ... if(!lpszDisplayName) // should never happen lpszDisplayName = szEmpty; nLen = CopyTruncate(szBufDisplayName, lpszDisplayName, MAX_DISPLAY_NAME_LENGTH); // The display name will be either by first name or last name // so all we have to do is generate the other name and we'll // be all set szBufOppositeName[0]='\0'; if(lpItem->ulObjectType == MAPI_DISTLIST) { StrCpyN(szBufOppositeName, szBufDisplayName, ARRAYSIZE(szBufOppositeName)); } else { // if there is no first/middle/last (there will always be a display name) // and the display name does not match company name or nick name, // then we shall try to parse the display name into first/middle/last if( !lpszFirstName && !lpszMiddleName && !lpszLastName && !(lpszCompanyName && !lstrcmp(lpszDisplayName, lpszCompanyName)) && !(lpszNickName && !lstrcmp(lpszDisplayName, lpszNickName)) ) { ParseDisplayName( lpszDisplayName, &lpszFirstName, &lpszLastName, NULL, // Root WAB allocation &lpBuffer); // lppLocalFree } if (lpszFirstName || lpszMiddleName || lpszLastName) { LPTSTR lpszTmp = szBufOppositeName; SetLocalizedDisplayName( lpszFirstName, bRuby ? NULL : lpszMiddleName, lpszLastName, NULL, //company NULL, //nickname (LPTSTR *) &lpszTmp, //&szBufOppositeName, MAX_DISPLAY_NAME_LENGTH, !bDNisByLN, NULL, NULL); } } if(!lstrlen(szBufOppositeName)) { // There is only 1 type of name so use it everywhere StrCpyN(lpItem->szByFirstName,szBufDisplayName,ARRAYSIZE(lpItem->szByFirstName)); StrCpyN(lpItem->szByLastName,szBufDisplayName,ARRAYSIZE(lpItem->szByLastName)); } else if(bDNisByLN) { // Display Name is by Last Name StrCpyN(lpItem->szByFirstName,szBufOppositeName,ARRAYSIZE(lpItem->szByFirstName)); StrCpyN(lpItem->szByLastName,szBufDisplayName,ARRAYSIZE(lpItem->szByLastName)); } else { // Display Name is by First Name StrCpyN(lpItem->szByLastName,szBufOppositeName,ARRAYSIZE(lpItem->szByLastName)); StrCpyN(lpItem->szByFirstName,szBufDisplayName,ARRAYSIZE(lpItem->szByFirstName)); } StrCpyN(lpItem->szDisplayName, szBufDisplayName,ARRAYSIZE(lpItem->szDisplayName)); if(bRuby) { if(lpszRubyFirstName) SetLocalizedDisplayName(lpszRubyFirstName, NULL, lpszRubyLastName ? lpszRubyLastName : (lpszLastName ? lpszLastName : szEmpty), NULL, NULL, NULL, 0, FALSE, //DNbyFN NULL, &lpItem->lpByRubyFirstName); if(lpszRubyLastName) SetLocalizedDisplayName(lpszRubyFirstName ? lpszRubyFirstName : (lpszFirstName ? lpszFirstName : szEmpty), NULL, lpszRubyLastName, NULL, NULL, NULL, 0, TRUE, //DNbyFN NULL, &lpItem->lpByRubyLastName); } // default object type to mailuser if(!lpItem->ulObjectType) lpItem->ulObjectType = MAPI_MAILUSER; out: if(lpBuffer) LocalFree(lpBuffer); return; } /* - AddEntryToGroupEx - * Adds an entry to a group * */ HRESULT AddEntryToGroupEx(LPADRBOOK lpAdrBook, ULONG cbGroupEntryID, LPENTRYID lpGroupEntryID, DWORD cbEID, LPENTRYID lpEID) { HRESULT hr = E_FAIL; LPMAPIPROP lpMailUser = NULL; ULONG ulObjType; ULONG cValues = 0; LPSPropValue lpPropArray = NULL; LPSPropValue lpSProp = NULL; ULONG ulcNewProp = 0; LPSPropValue lpNewProp = NULL; SCODE sc; ULONG i,j; BOOL bDLFound = FALSE; BOOL bIsOneOff = (WAB_ONEOFF == IsWABEntryID(cbEID, lpEID, NULL, NULL, NULL, NULL, NULL)); LPPTGDATA lpPTGData=GetThreadStoragePointer(); if(pt_bIsWABOpenExSession) bIsOneOff = FALSE; // [PaulHi] Raid 67581 First thing to do is check for cyclical references. // This was done as a special case below and is now moved up to the top of // the function. if(!bIsOneOff) { if(CheckForCycle(lpAdrBook, lpEID, cbEID, lpGroupEntryID, cbGroupEntryID)) { hr = MAPI_E_FOLDER_CYCLE; goto out; } } if (HR_FAILED(hr = lpAdrBook->lpVtbl->OpenEntry( lpAdrBook, cbGroupEntryID, // cbEntryID lpGroupEntryID, // entryid NULL, // interface MAPI_MODIFY, // ulFlags &ulObjType, // returned object type (LPUNKNOWN *)&lpMailUser))) { // Failed! Hmmm. DebugPrintError(( TEXT("IAB->OpenEntry: %x"), hr)); goto out; } Assert(lpMailUser); if(ulObjType != MAPI_DISTLIST) goto out; if (HR_FAILED(hr = lpMailUser->lpVtbl->GetProps(lpMailUser, // this NULL, MAPI_UNICODE, &cValues, // cValues &lpPropArray))) { DebugPrintError(( TEXT("lpMailUser->Getprops failed: %x\n"),hr)); goto out; } for(i=0;i %x\n"), GetScode(hr))); goto out; } break; } } if(!bDLFound) { // This item is empty and doesnt have a PR_WAB_DL_PROPS or PR_WAB_FOLDER_PROPS.. // Add a new prop to this object .. MAPIAllocateBuffer(sizeof(SPropValue), &lpSProp); lpSProp->ulPropTag = (bIsOneOff ? PR_WAB_DL_ONEOFFS : PR_WAB_DL_ENTRIES); lpSProp->Value.MVbin.cValues = 0; lpSProp->Value.MVbin.lpbin = NULL; if (HR_FAILED(hr = AddPropToMVPBin( lpSProp, 0, lpEID, cbEID, FALSE))) { DebugPrintError(( TEXT("AddPropToMVPBin -> %x\n"), GetScode(hr))); goto out; } sc = ScMergePropValues( 1, lpSProp, cValues, lpPropArray, &ulcNewProp, &lpNewProp); if (sc != S_OK) { hr = ResultFromScode(sc); goto out; } if(lpPropArray) MAPIFreeBuffer(lpPropArray); lpPropArray = lpNewProp; cValues = ulcNewProp; lpNewProp = NULL; } if (HR_FAILED(hr = lpMailUser->lpVtbl->SetProps(lpMailUser, cValues, lpPropArray, NULL))) { DebugPrintError(( TEXT("lpMailUser->Setprops failed\n"))); goto out; } hr = lpMailUser->lpVtbl->SaveChanges( lpMailUser, KEEP_OPEN_READWRITE); if (HR_FAILED(hr)) { DebugPrintError(( TEXT("SaveChanges failed\n"))); goto out; } out: if(lpPropArray) MAPIFreeBuffer(lpPropArray); if(lpNewProp) MAPIFreeBuffer(lpNewProp); if(lpSProp) MAPIFreeBuffer(lpSProp); if(lpMailUser) lpMailUser->lpVtbl->Release(lpMailUser); return hr; } /* - RemoveEntryFromFolder - * * */ HRESULT RemoveEntryFromFolder(LPIAB lpIAB, LPSBinary lpsbFolder, ULONG cbEIDItem, LPENTRYID lpEIDItem) { HRESULT hr = E_FAIL; ULONG ulObjType = 0, cValues = 0, i = 0, j = 0, k =0; int nIndex = -1; BOOL bRemoved = TRUE; LPSPropValue lpPropArray = NULL; // open the Folder if(HR_FAILED(hr = ReadRecord(lpIAB->lpPropertyStore->hPropertyStore, lpsbFolder, 0, &cValues, &lpPropArray))) goto out; for(i=0;ilpPropertyStore->hPropertyStore, NULL, &lpsbFolder, 0, RECORD_CONTAINER, cValues, lpPropArray); } out: ReadRecordFreePropArray(NULL, cValues, &lpPropArray); return hr; } /* - AddEIDToNewFolderParent - * Adds the given EID to a given Folder */ HRESULT AddItemEIDToFolderParent( LPIAB lpIAB, ULONG cbFolderEntryId, LPENTRYID lpFolderEntryId, ULONG cbEID, LPENTRYID lpEID) { HRESULT hr = S_OK; SBinary sb = {0}; ULONG cValues = 0,i; LPSPropValue lpPropArray = NULL; // ignore additions to folders in non-profile mode ... if(!bIsWABSessionProfileAware(lpIAB)) return S_OK; sb.cb = cbFolderEntryId; sb.lpb = (LPBYTE)lpFolderEntryId; if(HR_FAILED(hr = ReadRecord( lpIAB->lpPropertyStore->hPropertyStore, &sb, 0, &cValues, &lpPropArray))) goto out; for(i=0;ilpPropertyStore->hPropertyStore, NULL, &lpsb, 0, RECORD_CONTAINER, cValues, lpPropArray))) goto out; } out: ReadRecordFreePropArray(NULL, cValues, &lpPropArray); return hr; } /* - AddFolderParentEIDToItem - * Adds the Folders EID to given Item * */ HRESULT AddFolderParentEIDToItem(LPIAB lpIAB, ULONG cbFolderEntryID, LPENTRYID lpFolderEntryID, LPMAPIPROP lpMU, ULONG cbEID, LPENTRYID lpEID) { LPSPropValue lpspvMU = NULL; ULONG ulcPropsMU = 0,i; HRESULT hr = S_OK; // ignore additions to folders in non-profile mode ... if(!bIsWABSessionProfileAware(lpIAB)) return S_OK; if(!HR_FAILED(hr = lpMU->lpVtbl->GetProps(lpMU, NULL, MAPI_UNICODE, &ulcPropsMU, &lpspvMU))) { // Look for PR_WAB_FOLDER_PARENT BOOL bFound = FALSE; if(cbEID && lpEID) // means this is a preexisting entry not a new one { for(i=0;icb && !memcmp(lpFolderEntryID, lpsbOldParent->lpb, cbFolderEntryID)) { //old is same as new .. don't need to do anything hr = S_OK; goto out; } // Remove this item from its old Parents list of contents RemoveEntryFromFolder(lpIAB, lpsbOldParent, cbEID, lpEID); // an item can only have one folder parent if(lpFolderEntryID && cbFolderEntryID) { LPBYTE lpb = NULL; // overwrite the old setting if(!MAPIAllocateMore(cbFolderEntryID, lpspvMU, (LPVOID *)&lpb)) { lpspvMU[i].Value.MVbin.lpbin[0].cb = cbFolderEntryID; lpspvMU[i].Value.MVbin.lpbin[0].lpb = lpb; CopyMemory(lpspvMU[i].Value.MVbin.lpbin[0].lpb, lpFolderEntryID, cbFolderEntryID); lpMU->lpVtbl->SetProps(lpMU, ulcPropsMU, lpspvMU, NULL); } } bFound = TRUE; break; } } } if(!bFound) { // Didnt find an old parent in which case, if this is a valid folder we // are dropping it on (and not a root item) then add a new property // with new parent if(lpFolderEntryID && cbFolderEntryID) { LPSPropValue lpPropFP = NULL; if(!MAPIAllocateBuffer(sizeof(SPropValue), (LPVOID *)&lpPropFP)) { lpPropFP->ulPropTag = PR_WAB_FOLDER_PARENT; lpPropFP->Value.MVbin.cValues = 0; lpPropFP->Value.MVbin.lpbin = NULL; if(!HR_FAILED(AddPropToMVPBin( lpPropFP, 0, lpFolderEntryID, cbFolderEntryID, FALSE))) lpMU->lpVtbl->SetProps(lpMU, 1, lpPropFP, NULL); } if(lpPropFP) MAPIFreeBuffer(lpPropFP); } } else { // We did find an old parent // If the new parent is the root, then we basically need to remove the // old parent property SizedSPropTagArray(2, tagaFolderParent) = { 2, { PR_WAB_FOLDER_PARENT, PR_WAB_FOLDER_PARENT_OLDPROP } }; if(!lpFolderEntryID || !cbFolderEntryID) lpMU->lpVtbl->DeleteProps(lpMU, (LPSPropTagArray) &tagaFolderParent, NULL); } } out: FreeBufferAndNull(&lpspvMU); return hr; } /* - AddEntryToFolder - * * */ HRESULT AddEntryToFolder(LPADRBOOK lpAdrBook, LPMAPIPROP lpMailUser, ULONG cbFolderEntryId, LPENTRYID lpFolderEntryId, DWORD cbEID, LPENTRYID lpEID) { HRESULT hr = E_FAIL; ULONG ulObjType; SCODE sc; ULONG i; SBinary sb = {0}; LPIAB lpIAB = (LPIAB) lpAdrBook; // ignore additions to folders in non-profile mode ... if(!bIsWABSessionProfileAware(lpIAB)) return S_OK; // Check for a cycle of a folder being added to itself .. this is possible if(cbEID && lpEID && cbFolderEntryId && lpFolderEntryId) { SBinary sb = {0}; IsWABEntryID(cbFolderEntryId, lpFolderEntryId, (LPVOID*)&sb.lpb,(LPVOID*)&sb.cb,NULL,NULL,NULL); if( sb.cb == cbEID && !memcmp(lpEID, sb.lpb, cbEID) ) return S_OK; } if(cbFolderEntryId && lpFolderEntryId) { if(HR_FAILED(hr = AddItemEIDToFolderParent(lpIAB, cbFolderEntryId, lpFolderEntryId, cbEID, lpEID))) goto out; } // 2. Open the object we added to this folder // Need to update its folder parent and also need to remove it from the old folder parent // if(lpMailUser || (cbEID && lpEID)) { LPMAPIPROP lpMU = NULL; if(lpMailUser) lpMU = lpMailUser; else { if (HR_FAILED(hr = lpIAB->lpVtbl->OpenEntry( lpIAB, cbEID, lpEID, NULL, MAPI_MODIFY, &ulObjType, (LPUNKNOWN *)&lpMU))) { DebugPrintError(( TEXT("IAB->OpenEntry: %x"), hr)); goto out; } } if(!HR_FAILED(hr = AddFolderParentEIDToItem(lpIAB, cbFolderEntryId, lpFolderEntryId, lpMU, cbEID, lpEID))) { // if we were given a mailuser to work with, don't bother calling SaveChanges from here just yet if(lpMU && lpMU!=lpMailUser) { lpMU->lpVtbl->SaveChanges(lpMU, KEEP_OPEN_READWRITE); lpMU->lpVtbl->Release(lpMU); } } } out: return hr; } //$$//////////////////////////////////////////////////////////////////////////// // // AddEntryToGroup - Adds given entryID to given group or folder // // cbGroupEntryID,cbGroupEntryID - entryid of group // cbEID, lpEID, - entryid of new entry // ulObjectType = MAPI_ABCONT or MAPI_DISTLIST // ////////////////////////////////////////////////////////////////////////////// HRESULT AddEntryToContainer(LPADRBOOK lpAdrBook, ULONG ulObjectType, ULONG cbGEID, LPENTRYID lpGEID, DWORD cbEID, LPENTRYID lpEID) { if(ulObjectType == MAPI_ABCONT) return AddEntryToFolder(lpAdrBook, NULL, cbGEID, lpGEID, cbEID, lpEID); else return AddEntryToGroupEx(lpAdrBook, cbGEID, lpGEID, cbEID, lpEID); } //$$//////////////////////////////////////////////////////////////////////////// // // AddNewObjectTOListViewEx - Triggered by the NewContact menus and buttons - // calls newentry and then adds the returned item to // the list view // // lpIAB - AddrBook object // hWndLV - handle of List View // ulObjectType - MailUser or DistList // SortInfo - Current Sort parameters // lppContentsList - Current ContentsList // lpftLast - WAB file time at last update // LPULONG - lpcbEID // LPPENTRYID - lppEntryID ////////////////////////////////////////////////////////////////////////////// HRESULT AddNewObjectToListViewEx(LPADRBOOK lpAdrBook, HWND hWndLV, HWND hWndTV, HTREEITEM hSelItem, LPSBinary lpsbContainerEID, ULONG ulObjectType, SORT_INFO * lpSortInfo, LPRECIPIENT_INFO * lppContentsList, LPFILETIME lpftLast, LPULONG lpcbEID, LPENTRYID * lppEID) { ULONG cbEID=0, cbEIDContainer = 0; LPENTRYID lpEID=NULL, lpEIDContainer = NULL; HRESULT hr = hrSuccess; ULONG cbTplEID = 0; LPENTRYID lpTplEID = NULL; ULONG ulObjTypeCont = 0; SBinary sbContEID = {0}; SBinary sbGroupEID = {0}; LPIAB lpIAB = (LPIAB)lpAdrBook; ULONG ulEIDPAB = 0; LPENTRYID lpEIDPAB = NULL; if (ulObjectType!=MAPI_MAILUSER && ulObjectType!=MAPI_DISTLIST) goto out; // Check if the currently selected TV item is a container or a group // and get the corresponding entryid // if(lpsbContainerEID) { SetSBinary(&sbContEID, lpsbContainerEID->cb, lpsbContainerEID->lpb); } else if(hWndTV) { HTREEITEM hItem = hSelItem ? hSelItem : TreeView_GetSelection(hWndTV); TV_ITEM tvI = {0}; tvI.mask = TVIF_PARAM | TVIF_HANDLE; tvI.hItem = hItem; TreeView_GetItem(hWndTV, &tvI); if(tvI.lParam) { LPTVITEM_STUFF lptvStuff = (LPTVITEM_STUFF) tvI.lParam; if(lptvStuff) { ulObjTypeCont = lptvStuff->ulObjectType; if(lptvStuff->ulObjectType == MAPI_DISTLIST) { // Bug 50029 if(lptvStuff->lpsbEID) SetSBinary(&sbGroupEID, lptvStuff->lpsbEID->cb, lptvStuff->lpsbEID->lpb); if(lptvStuff->lpsbParent) SetSBinary(&sbContEID, lptvStuff->lpsbParent->cb, lptvStuff->lpsbParent->lpb); } else // current selection is a container { if(lptvStuff->lpsbEID) SetSBinary(&sbContEID, lptvStuff->lpsbEID->cb, lptvStuff->lpsbEID->lpb); } } } } else { if(HR_FAILED(hr = lpAdrBook->lpVtbl->GetPAB(lpAdrBook, &ulEIDPAB, &lpEIDPAB))) goto out; sbContEID.cb = ulEIDPAB; sbContEID.lpb = (LPBYTE)lpEIDPAB; } if(HR_FAILED(hr = HrGetWABTemplateID( lpAdrBook, ulObjectType, &cbTplEID, &lpTplEID))) { DebugPrintError(( TEXT("HrGetWABTemplateID failed: %x\n"), hr)); goto out; } if(sbContEID.cb && sbContEID.lpb) { cbEIDContainer = sbContEID.cb; lpEIDContainer = (LPENTRYID) sbContEID.lpb; } if (HR_FAILED(hr = lpAdrBook->lpVtbl->NewEntry( lpAdrBook, (ULONG_PTR) GetParent(hWndLV), 0, cbEIDContainer, lpEIDContainer, cbTplEID,lpTplEID, &cbEID,&lpEID))) { DebugPrintError(( TEXT("NewEntry failed: %x\n"),hr)); goto out; } // Update the wab file write time so the timer doesn't // catch this change and refresh. //if (lpftLast) { // CheckChangedWAB(((LPIAB)lpIAB)->lpPropertyStore, lpftLast); //} if (cbEID && lpEID) { if( AddWABEntryToListView( lpAdrBook, hWndLV, cbEID, lpEID, lppContentsList)) { if(lpSortInfo) SortListViewColumn( lpIAB, hWndLV, 0, lpSortInfo, TRUE); } } if(sbGroupEID.cb != 0 && ulObjectType==MAPI_MAILUSER) { // Need to add this new object to the currently selected distribution list // Only if this item is a mailuser AddEntryToGroupEx(lpAdrBook, sbGroupEID.cb, (LPENTRYID) sbGroupEID.lpb, cbEID, lpEID); } if(lpcbEID) *lpcbEID = cbEID; if(lppEID) *lppEID = lpEID; // Callers responsibility to free out: LocalFreeAndNull((LPVOID *) (&sbGroupEID.lpb)); // [PaulHi] 12/16/98 Crash fix hack. If lpEIDPAB is non-NULL then // this means lpEIDPAB == sbContEID.lpb and is MAPIAllocBuffer allocated. // Don't deallocate twice and make sure we deallocate with correct function. // Otherwise sbContEID.lpb is a LocalAlloc allocation. if (lpEIDPAB) { FreeBufferAndNull(&lpEIDPAB); sbContEID.lpb = NULL; } else LocalFreeAndNull((LPVOID *) (&sbContEID.lpb)); if(!lppEID) FreeBufferAndNull(&lpEID); FreeBufferAndNull(&lpTplEID); return hr; } /* - AddExtendedSendMailToItems - * If there is only 1 item selected in the ListView and that item has * multiple email addresses, we populate the Send Mail To item with * the multiple email addresses .. * If there is more than 1 item selected or the item doesn't have * multiple email addresses, we will hide the Send Mail To item * The SendMailTo item should be the second last in the list ... * * bAddItems - if TRUE means attempt to add items; if FALSE means remove the SendMailTo item */ void AddExtendedSendMailToItems(LPADRBOOK lpAdrBook, HWND hWndLV, HMENU hMenuAction, BOOL bAddItems) { int nSendMailToPos = 1; // assumes IDM_SENDMAILTO is the second item in the list int nSelected = ListView_GetSelectedCount(hWndLV); HMENU hMenuSMT = GetSubMenu(hMenuAction, nSendMailToPos); int nMenuSMT = GetMenuItemCount(hMenuSMT); BOOL bEnable = FALSE; if(nMenuSMT > 0) // Assumes there is only 1 default item in the SendMailTO popup menu { // there is some left over garbage here which we need to clear int j = 0; for(j=nMenuSMT-1;j>=0;j--) RemoveMenu(hMenuSMT, j, MF_BYPOSITION); } if(bAddItems && nSelected == 1) { LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, ListView_GetNextItem(hWndLV, -1, LVNI_SELECTED)); if (lpItem) { ULONG ulcValues = 0; LPSPropValue lpPropArray = NULL; SizedSPropTagArray(3, MUContactAddresses)= { 3, { PR_CONTACT_EMAIL_ADDRESSES, PR_OBJECT_TYPE, PR_EMAIL_ADDRESS } }; if(!HR_FAILED(HrGetPropArray(lpAdrBook, (LPSPropTagArray)&MUContactAddresses, lpItem->cbEntryID, lpItem->lpEntryID, MAPI_UNICODE, &ulcValues, &lpPropArray))) { if(ulcValues && lpPropArray) { if( lpPropArray[1].ulPropTag == PR_OBJECT_TYPE && lpPropArray[1].Value.l == MAPI_MAILUSER ) { if( lpPropArray[0].ulPropTag == PR_CONTACT_EMAIL_ADDRESSES && lpPropArray[0].Value.MVbin.cValues > 1) { ULONG i; LPTSTR lpDefEmail = (lpPropArray[2].ulPropTag == PR_EMAIL_ADDRESS) ? lpPropArray[2].Value.LPSZ : szEmpty; for(i=0;ilpWABFolders; int nPos = 0; int nCount = GetMenuItemCount(hMenu); if(!bDoesThisWABHaveAnyUsers(lpIAB)) return; while(nCount>0) RemoveMenu(hMenu, --nCount, MF_BYPOSITION); while(lpFolder) { BOOL bChecked = lpFolder->bShared; InsertMenu( hMenu, nPos, MF_STRING | MF_BYPOSITION | (bChecked ? MF_CHECKED : MF_UNCHECKED), lpFolder->nMenuCmdID, lpFolder->lpFolderName); lpFolder = lpFolder->lpNext; nPos++; } } //$$//////////////////////////////////////////////////////////////////////////// // // ShowLVContextMenu - Customizes and displays the context menu for various list // views in the UI // // LV - app defined constant identifing the List View // hWndLV - Handle of List View // hWndLVContainer - Handle of the List containing the containers // lParam - WM_CONTEXTMENU lParam passed on to this function // lpVoid - some List Views need more parameters than other list views - pass them // in this parameter // lpIAB - AdrBook object // ////////////////////////////////////////////////////////////////////////////// int ShowLVContextMenu(int LV, // idicates which list view this is HWND hWndLV, HWND hWndLVContainer, LPARAM lParam, // contains the mouse pos info when called from WM_CONTEXTMENU LPVOID lpVoid, LPADRBOOK lpAdrBook, HWND hWndTV) //misc stuff we want to pass in { int idMenu = 0, nPosAction = 0, nPosNew = 0; LPIAB lpIAB = (LPIAB) lpAdrBook; HMENU hMenu = NULL;//LoadMenu(hinstMapiX, MAKEINTRESOURCE(IDR_MENU_LVCONTEXT)); HMENU hMenuTrackPopUp = NULL;//GetSubMenu(hMenu, 0); HMENU hMenuAction = NULL;//GetSubMenu(hMenuTrackPopUp, posAction); HMENU hMenuNewEntry = NULL;//GetSubMenu(hMenuTrackPopUp, posNew); HMENU hm = NULL; int nret = 0; BOOL bState[tbMAX]; int i=0; TCHAR tszBuf[MAX_UI_STR]; switch(LV) /**WARNING - these menu sub pop up positions are HARDCODED so should be in sync with the resource**/ { case lvToolBarAction: case lvToolBarNewEntry: case lvMainABView: idMenu = IDR_MENU_LVCONTEXT_BROWSE_LV; nPosAction = 5; nPosNew = 0; break; case lvDialogABContents: // Modeless address view LV case lvDialogModalABContents: // Modal addres vuew LV idMenu = IDR_MENU_LVCONTEXT_SELECT_LIST; nPosAction = 6; nPosNew = 4; break; case lvDialogABTo: // To Well LV case lvDialogABCC: // CC Well LV case lvDialogABBCC: // BCC Well LV case lvDialogDistList: // Disttribution list UI LV case lvDialogResolve: idMenu = IDR_MENU_LVCONTEXT_DL_LV; nPosAction = 0; nPosNew = -1; break; case lvDialogFind: // Find dialog results LV idMenu = IDR_MENU_LVCONTEXT_FIND_LV; nPosNew = -1; nPosAction = 0; break; case lvMainABTV: idMenu = IDR_MENU_LVCONTEXT_TV; nPosNew = 0; nPosAction = -1; break; #ifdef COLSEL_MENU case lvMainABHeader: idMenu = IDR_MENU_LVCONTEXTMENU_COLSEL; nPosNew = 0; nPosAction = -1; #endif } hMenu = LoadMenu(hinstMapiX, MAKEINTRESOURCE(idMenu)); hMenuTrackPopUp = GetSubMenu(hMenu, 0); if (!hMenu || !hMenuTrackPopUp) { DebugPrintError(( TEXT("LoadMenu failed: %x\n"),GetLastError())); goto out; } if(nPosAction != -1) hMenuAction = GetSubMenu(hMenuTrackPopUp, nPosAction); if(nPosNew != -1) hMenuNewEntry = GetSubMenu(hMenuTrackPopUp, nPosNew); if(hMenuAction) AddExtendedMenuItems(lpAdrBook, hWndLV, hMenuAction, FALSE, (LV != lvMainABTV)); // this is the condition for updating SendMailTo items if(LV == lvMainABTV) { // everything on except Copy for(i=0;ilpWABFolders) // no sub-folders at all { EnableMenuItem(hMenuTrackPopUp, 2, MF_BYPOSITION | MF_GRAYED); //Folder item EnableMenuItem(hMenuTrackPopUp, 3, MF_BYPOSITION | MF_GRAYED); //Folder item } else { int nFolder = 2; #ifdef FUTURE HMENU hMenuFolders = GetSubMenu(hMenuTrackPopUp, nFolder); //idmFolders AddFolderListToMenu(hMenuFolders, lpIAB); #endif // FUTURE } } break; case lvMainABView: //main view // For this one - we dont need the wells and if(!bState[tbPaste]) EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_PASTE,MF_BYCOMMAND | MF_GRAYED); if(!bState[tbCopy]) EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_COPY,MF_BYCOMMAND | MF_GRAYED); if ((!bState[tbProperties])) EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_PROPERTIES,MF_BYCOMMAND | MF_GRAYED); if((!bState[tbDelete])) EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_DELETE,MF_BYCOMMAND | MF_GRAYED); break; case lvDialogModalABContents: case lvDialogABContents: //address book dialog contents list view //here we want the option to put it in to,cc,bcc //in the menu - we also want new contact/new group/properties // no delete unless this is DialogModal if(LV != lvDialogModalABContents) RemoveMenu(hMenuTrackPopUp, IDM_LVCONTEXT_DELETE, MF_BYCOMMAND); // figure out some way to read the items on the dlg to figure out // how many wells to show and what to put in them ... { LPADRPARM lpAP = (LPADRPARM) lpVoid; if (lpAP) { switch(lpAP->cDestFields) { case 0: RemoveMenu(hMenuTrackPopUp, 3, MF_BYPOSITION); //seperator RemoveMenu(hMenuTrackPopUp, IDM_LVCONTEXT_ADDWELL1, MF_BYCOMMAND); case 1: RemoveMenu(hMenuTrackPopUp, IDM_LVCONTEXT_ADDWELL2, MF_BYCOMMAND); case 2: RemoveMenu(hMenuTrackPopUp, IDM_LVCONTEXT_ADDWELL3, MF_BYCOMMAND); break; } if((lpAP->cDestFields > 0) && lpAP->lppszDestTitles) { ULONG i; // update the text of the menu with the button text for(i=0;icDestFields;i++) { int id; switch(i) { case 0: id = IDM_LVCONTEXT_ADDWELL1; break; case 1: id = IDM_LVCONTEXT_ADDWELL2; break; case 2: id = IDM_LVCONTEXT_ADDWELL3; break; } // [PaulHi] 2/15/99 Check whether lpAP is ANSI or UNICODE { LPTSTR lptszDestTitle = NULL; BOOL bDestAllocated = FALSE; if (lpAP->ulFlags & MAPI_UNICODE) lptszDestTitle = lpAP->lppszDestTitles[i]; else { // Convert single byte string to double byte lptszDestTitle = ConvertAtoW((LPSTR)lpAP->lppszDestTitles[i]); bDestAllocated = TRUE; } if (lptszDestTitle) { ULONG iLen = TruncatePos(lptszDestTitle, MAX_UI_STR - 5); CopyMemory(tszBuf, lptszDestTitle, sizeof(TCHAR)*iLen); tszBuf[iLen] = '\0'; StrCatBuff(tszBuf, szArrow, ARRAYSIZE(tszBuf)); if (bDestAllocated) LocalFreeAndNull(&lptszDestTitle); } else *tszBuf = '\0'; } ModifyMenu( hMenuTrackPopUp, /*posTo + */i, MF_BYPOSITION | MF_STRING, id, tszBuf); } } } } break; case lvDialogABTo: //address book dialog To well case lvDialogABCC: //CC well case lvDialogABBCC: //BCC well { int iItemIndex = 0; iItemIndex = ListView_GetSelectedCount(hWndLV); if (iItemIndex!=1) { EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_PROPERTIES,MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_SENDMAIL,MF_BYCOMMAND | MF_GRAYED); } if (iItemIndex<=0) { EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_DELETE,MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_COPY,MF_BYCOMMAND | MF_GRAYED); } // // The wells may contain unresolved items without entryids .. // If the item does not have an entryid, we want to disable properties // if (iItemIndex == 1) { // we are potentially looking at the properties of this thing // get the items lParam iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); if(iItemIndex != -1) { LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);; if(lpItem && ((lpItem->cbEntryID == 0) || (lpItem->lpEntryID == NULL))) { EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_PROPERTIES,MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_COPY,MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenuTrackPopUp,IDM_LVCONTEXT_SENDMAIL,MF_BYCOMMAND | MF_GRAYED); } } } } break; case lvDialogResolve: //Resolve dialog list view //Rename the TEXT("delete") to TEXT("Show More Names .. ") LoadString(hinstMapiX, idsShowMoreNames, tszBuf, ARRAYSIZE(tszBuf)); ModifyMenu( hMenuTrackPopUp, IDM_LVCONTEXT_DELETE, MF_BYCOMMAND | MF_STRING, IDM_LVCONTEXT_DELETE, tszBuf); //And we want a seperator before TEXT("Show More Names") InsertMenu( hMenuTrackPopUp, IDM_LVCONTEXT_DELETE, MF_BYCOMMAND | MF_SEPARATOR, IDM_LVCONTEXT_DELETE, NULL); break; #ifdef COLSEL_MENU case lvMainABHeader: { UINT iIndex = PtrToUlong(lpVoid); ULONG ulShowingColTag; ULONG ulOtherColTag; UINT i = 0, j; // this will always be called with iIndex == colHomePhone or colOfficePhone Assert( iIndex == colHomePhone || iIndex == colOfficePhone ); if( PR_WAB_CUSTOMPROP1 == 0 ) PR_WAB_CUSTOMPROP1 = PR_HOME_TELEPHONE_NUMBER; if( PR_WAB_CUSTOMPROP2 == 0) PR_WAB_CUSTOMPROP2 = PR_OFFICE_TELEPHONE_NUMBER; ulShowingColTag = (colHomePhone == iIndex) ? PR_WAB_CUSTOMPROP1 : PR_WAB_CUSTOMPROP2; ulOtherColTag = (ulShowingColTag == PR_WAB_CUSTOMPROP1) ? PR_WAB_CUSTOMPROP2 : PR_WAB_CUSTOMPROP1; // lets remove the tag that is displayed in the other col for( i = 0; i < MAXNUM_MENUPROPS; i++) { if( MenuToPropTagMap[i] == ulOtherColTag ) { if( RemoveMenu( hMenuTrackPopUp, i, MF_BYPOSITION) ) break; else DebugTrace( TEXT("could not remove menu: %x\n"), GetLastError() ); } } if( i == MAXNUM_MENUPROPS ) DebugTrace( TEXT("Did not find other col's prop tag\n")); if( ulShowingColTag != ulOtherColTag ) { UINT iMenuEntry; // potential bug, if someone sets value in registry // then could have two columns with the same name and that // would be bad because we would be looking for an entry // that does not exist for( j = 0; j < MAXNUM_MENUPROPS; j++) { if( ulShowingColTag == MenuToPropTagMap[j] ) { // num of items that can be in column heads Assert( j != i ); // both cols have same value, bad! iMenuEntry = ( j > i ) ? j - 1 : j; CheckMenuRadioItem( hMenuTrackPopUp, 0, MAXNUM_MENUPROPS - 1, // minus one because there will be one missing iMenuEntry, MF_BYPOSITION); break; } } if( j == MAXNUM_MENUPROPS ) { DebugTrace( TEXT("Did not find match for checkbutton \n")); } } } #endif // COLSEL_MENU } // // Popup the menu - if this was a toolbar action just pop up the submenu // if(LV == lvToolBarAction) hm = hMenuAction; else if(LV == lvToolBarNewEntry) hm = hMenuNewEntry; else hm = hMenuTrackPopUp; if(hMenuNewEntry) { if(!bIsWABSessionProfileAware((LPIAB)lpIAB) || LV == lvDialogABTo || LV == lvDialogABCC || LV == lvDialogABBCC || LV == lvDialogModalABContents || LV == lvDialogABContents ) { RemoveMenu(hMenuNewEntry, 2, MF_BYPOSITION); // remove new folder option } else { // Since this could be a rt-click menu, check the drophighlight else the selection //EnableMenuItem(hMenuNewEntry,2,MF_BYPOSITION | MF_ENABLED); //if(hWndTV && bDoesThisWABHaveAnyUsers((LPIAB)lpIAB)) //{ // if(TreeView_GetDropHilight(hWndTV)) // EnableMenuItem( hMenuNewEntry,2, // MF_BYPOSITION | (TreeView_GetDropHilight(hWndTV)!=TreeView_GetRoot(hWndTV) ? MF_ENABLED : MF_GRAYED)); //else if(TreeView_GetSelection(hWndTV) == TreeView_GetRoot(hWndTV)) // EnableMenuItem(hMenuNewEntry,2,MF_BYPOSITION | MF_GRAYED); //} } } nret = TrackPopupMenu( hm, TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam), 0, GetParent(hWndLV), NULL); DestroyMenu(hMenu); /* nret = TrackPopupMenuEx(hm, TPM_RETURNCMD | TPM_LEFTALIGN | TPM_RIGHTBUTTON, LOWORD(lParam), HIWORD(lParam), GetParent(hWndLV), NULL); DestroyMenu(hMenu); */ out: return nret; } //$$///////////////////////////////////////////////////////////// // // GetChildClientRect - Gets the child's coordinates in its parents // client units // // hWndChild - handle of child // lprc - returned RECT. // /////////////////////////////////////////////////////////////// void GetChildClientRect(HWND hWndChild, LPRECT lprc) { RECT rc; POINT ptTop,ptBottom; HWND hWndParent; ZeroMemory(lprc, sizeof(*lprc)); if(!hWndChild) goto out; hWndParent = GetParent(hWndChild); if(!hWndParent) goto out; GetWindowRect(hWndChild,&rc); // //This api working in both mirrored and unmirrored windows. // MapWindowPoints(NULL, hWndParent, (LPPOINT)&rc, 2); ptTop.x = rc.left; ptTop.y = rc.top; ptBottom.x = rc.right; ptBottom.y = rc.bottom; (*lprc).left = ptTop.x; (*lprc).top = ptTop.y; (*lprc).right = ptBottom.x; (*lprc).bottom = ptBottom.y; out: return; } //$$///////////////////////////////////////////////////////////// // // DoLVQuickFind - Simple quick find routine for matching edit box contents to // List view entries // // hWndEdit - handle of Edit Box // hWndLV - handle of List View // /////////////////////////////////////////////////////////////// void DoLVQuickFind(HWND hWndEdit, HWND hWndLV) { TCHAR szBuf[MAX_PATH] = TEXT(""); int iItemIndex = 0; LV_FINDINFO lvF = {0}; lvF.flags = LVFI_PARTIAL | LVFI_STRING | LVFI_WRAP; if(!GetWindowText(hWndEdit,szBuf,ARRAYSIZE(szBuf))) return; TrimSpaces(szBuf); if(lstrlen(szBuf)) { lvF.psz = szBuf; iItemIndex = ListView_FindItem(hWndLV,-1, &lvF); //if (iItemIndex < 0) iItemIndex = 0; if(iItemIndex != -1) { ULONG cSel=0; cSel = ListView_GetSelectedCount(hWndLV); if(cSel) { // is there anything else selected ? - deselect and and // select this item ... int iOldItem = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); while(iOldItem != -1) { ListView_SetItemState ( hWndLV, // handle to listview iOldItem, // index to listview item 0, // item state LVIS_FOCUSED | LVIS_SELECTED); iOldItem = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); } } LVSelectItem ( hWndLV, iItemIndex); } } return; } //$$//////////////////////////////////////////////////////////////////////////////////////////////// // // HrGetPropArray - for a selected resolved property (either in select_recipient or // pick_user mode ... get the list of minimum required props as well // as desired props (if they exist) // // lpIAB - AddrBook Object // hPropertyStore - handle to prop store // lpPTA - Array of props to return - NULL to return ALL the props // cbEntryID, lpEntryID - id of object // ulFlags - 0 or MAPI_UNICODE // cValues, lppPropArray - returned props // ////////////////////////////////////////////////////////////////////////////////////////////////// HRESULT HrGetPropArray( LPADRBOOK lpAdrBook, LPSPropTagArray lpPTA, ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulFlags, ULONG * lpcValues, LPSPropValue * lppPropArray) { HRESULT hr = hrSuccess; LPMAPIPROP lpMailUser = NULL; LPSPropValue lpPropArray = NULL; ULONG ulObjType; ULONG cValues; *lppPropArray = NULL; if (HR_FAILED(hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, cbEntryID, // cbEntryID lpEntryID, // entryid NULL, // interface 0, // ulFlags &ulObjType, // returned object type (LPUNKNOWN *)&lpMailUser))) { // Failed! Hmmm. DebugTraceResult( TEXT("Address: IAB->OpenEntry:"), hr); goto exit; } Assert(lpMailUser); //TBD - Check ObjectType here if (HR_FAILED(hr = lpMailUser->lpVtbl->GetProps(lpMailUser, (LPSPropTagArray)lpPTA, // lpPropTagArray ulFlags, &cValues, // how many properties were there? &lpPropArray))) { DebugTraceResult( TEXT("Address: IAB->GetProps:"), hr); goto exit; } *lppPropArray = lpPropArray; *lpcValues = cValues; exit: if (HR_FAILED(hr)) { if (lpPropArray) MAPIFreeBuffer(lpPropArray); } if (lpMailUser) lpMailUser->lpVtbl->Release(lpMailUser); return hr; } //$$/////////////////////////////////////////////////////////////// // // SubStringSearchEx - Same as SubStringSearch except it does some // language related processing and mapping of dbcs input // strings etc // // pszTarget - Target string // pszSearch - SubString to Search for // // returns - TRUE if match found // FALSE if no match // ///////////////////////////////////////////////////////////////// BOOL SubstringSearchEx(LPTSTR pszTarget, LPTSTR pszSearch, LCID lcid) { if(!pszTarget && !pszSearch) return TRUE; if(!pszTarget || !pszSearch) return FALSE; if(lcid) { LPTSTR lpTH = NULL, lpSH = NULL; int nLenTH = 0, nLenSH = 0; LPTSTR lpT = NULL, lpS = NULL; BOOL bRet = FALSE; // Looks like this will have to be a two step process // First convert all half-width characters to full-width characters // Then convert all full-width katakana to full-width hirangana // Step 1. Convert half width and full width katakana to hiragana to full width int nLenT = LCMapString(lcid, LCMAP_FULLWIDTH | LCMAP_HIRAGANA, pszTarget, lstrlen(pszTarget)+1, lpT, 0); int nLenS = LCMapString(lcid, LCMAP_FULLWIDTH | LCMAP_HIRAGANA, pszSearch, lstrlen(pszSearch)+1, lpS, 0); lpT = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(nLenT+1)); lpS = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(nLenS+1)); if(!lpT || !lpS) goto err; LCMapString(lcid, LCMAP_FULLWIDTH | LCMAP_HIRAGANA, pszTarget, lstrlen(pszTarget)+1, lpT, nLenT); LCMapString(lcid, LCMAP_FULLWIDTH | LCMAP_HIRAGANA, pszSearch, lstrlen(pszSearch)+1, lpS, nLenS); lpS[nLenS]=lpT[nLenT]='\0'; // Step 2. Convert all to Half Width Hirangana nLenTH = LCMapString(lcid, LCMAP_HALFWIDTH | LCMAP_HIRAGANA, lpT, lstrlen(lpT)+1, lpTH, 0); nLenSH = LCMapString(lcid, LCMAP_HALFWIDTH | LCMAP_HIRAGANA, lpS, lstrlen(lpS)+1, lpSH, 0); lpTH = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(nLenTH+1)); lpSH = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*(nLenSH+1)); if(!lpTH || !lpSH) goto err; LCMapString(lcid, LCMAP_HALFWIDTH | LCMAP_HIRAGANA, lpT, lstrlen(lpT)+1, lpTH, nLenTH); LCMapString(lcid, LCMAP_HALFWIDTH | LCMAP_HIRAGANA, lpS, lstrlen(lpS)+1, lpSH, nLenSH); lpSH[nLenSH]=lpTH[nLenTH]='\0'; // by now, all strings should be in Full Width Hirangana .. bRet = SubstringSearch(lpTH, lpSH); err: if(lpT) LocalFree(lpT); if(lpS) LocalFree(lpS); if(lpTH) LocalFree(lpTH); if(lpSH) LocalFree(lpSH); return bRet; } else return(SubstringSearch(pszTarget, pszSearch)); } //$$/////////////////////////////////////////////////////////////// // // SubStringSearch - Used for doing partial resolves - Brute force // search routine stolen from Athena // // TBD - Is this DBCS safe .. ??? // // pszTarget - Target string // pszSearch - SubString to Search for // // returns - TRUE if match found // FALSE if no match // ///////////////////////////////////////////////////////////////// BOOL SubstringSearch(LPTSTR pszTarget, LPTSTR pszSearch) { LPTSTR pszT = pszTarget; LPTSTR pszS = pszSearch; if(!pszTarget && !pszSearch) return TRUE; if(!pszTarget || !pszSearch) return FALSE; if(!lstrlen(pszTarget) && !lstrlen(pszSearch)) return TRUE; if(!lstrlen(pszTarget) || !lstrlen(pszSearch)) return FALSE; while (*pszT && *pszS) { if (*pszT != *pszS && (TCHAR) CharLower((LPTSTR)(DWORD_PTR)MAKELONG(*pszT, 0)) != *pszS && (TCHAR) CharUpper((LPTSTR)(DWORD_PTR)MAKELONG(*pszT, 0)) != *pszS) { pszT -= (pszS - pszSearch); pszT = CharNext(pszT); // dont start searching at half chars pszS = pszSearch; } else { pszS++; pszT++; // as long as the search is going on, do byte comparisons } } return (*pszS == 0); } //$$ /**************************************************************************** FUNCTION: GetThreadStoragePointer() PURPOSE: gets the private storage pointer for a thread, allocating one if it does not exist (i.e. the thread didn't go through LibMain THREAD_ATTACH) PARAMETERS: none RETURNS: a pointer to the thread's private storage NULL, if there was a failure (usually memory allocation failure) ****************************************************************************/ LPPTGDATA __fastcall GetThreadStoragePointer() { LPPTGDATA lpPTGData=TlsGetValue(dwTlsIndex); // if the thread does not have a private storage, it did not go through // THREAD_ATTACH and we need to do this here. if (!lpPTGData) { DebugPrintTrace(( TEXT("GetThreadStoragePointer: no private storage for this thread 0x%.8x\n"),GetCurrentThreadId())); lpPTGData = (LPPTGDATA) LocalAlloc(LPTR, sizeof(PTGDATA)); if (lpPTGData) TlsSetValue(dwTlsIndex, lpPTGData); } return lpPTGData; } //$$//////////////////////////////////////////////////////////////////////// // // HrCreateNewEntry - Creates a new mailuser or DistList // // lpIAB - handle to AdrBook object // hWndParent - hWnd for showing dialogs // ulCreateObjectType - MailUser or DistList // ulFlags = CREATE_DUP_CHECK_STRICT or 0 // cValues - PropCount of New properties from which to create // the object // lpPropArray - Props for this new object // lpcbEntryID, lppEntryID - returned, new entryid for newly created object // cbEIDContainer, lpEIDContainer - container in which to create this entry // ulContObjType - The container object type - this could be a DISTLIST of an ABCONT // if this is an ABCONT, we open the container and create the entry in the container // If it is a DISTLIST, we open the PAB, create the entry in the PAB and then // add the entry to the specified entryid // //////////////////////////////////////////////////////////////////////////// HRESULT HrCreateNewEntry( LPADRBOOK lpIAB, // AdrBook Object HWND hWndParent, // hWnd for Dialogs ULONG ulCreateObjectType, //MAILUSER or DISTLIST ULONG cbEIDCont, LPENTRYID lpEIDCont, ULONG ulContObjType, ULONG ulFlags, BOOL bShowBeforeAdding, ULONG cValues, LPSPropValue lpPropArray, ULONG *lpcbEntryID, LPENTRYID *lppEntryID ) { LPABCONT lpContainer = NULL; LPMAPIPROP lpMailUser = NULL; HRESULT hr = hrSuccess; ULONG ulObjType = 0; ULONG cbWABEID = 0; LPENTRYID lpWABEID = NULL; LPSPropValue lpCreateEIDs = NULL; LPSPropValue lpNewProps = NULL; ULONG cNewProps; SCODE sc = S_OK; ULONG nIndex; ULONG cbTplEID = 0; LPENTRYID lpTplEID = NULL; BOOL bFirst = TRUE; BOOL bChangesMade = FALSE; ULONG cbEIDContainer = 0; LPENTRYID lpEIDContainer = NULL; DebugPrintTrace(( TEXT("HrCreateNewEntry: entry\n"))); if ( (!lpIAB) || ((ulFlags != 0) && (ulFlags != CREATE_CHECK_DUP_STRICT)) || ((ulCreateObjectType != MAPI_MAILUSER) && (ulCreateObjectType != MAPI_DISTLIST)) ) { hr = MAPI_E_INVALID_PARAMETER; goto out; } if(lpcbEntryID) *lpcbEntryID = 0; if(lppEntryID) *lppEntryID = NULL; if(ulContObjType == MAPI_ABCONT && cbEIDCont && lpEIDCont) { cbEIDContainer = cbEIDCont; lpEIDContainer = lpEIDCont; } if(!cbEIDContainer || !lpEIDContainer) { SetVirtualPABEID((LPIAB)lpIAB, &cbWABEID, &lpWABEID); if (HR_FAILED(hr = lpIAB->lpVtbl->GetPAB( lpIAB, &cbWABEID, &lpWABEID))) { DebugPrintError(( TEXT("GetPAB Failed\n"))); goto out; } } if (HR_FAILED(hr = lpIAB->lpVtbl->OpenEntry(lpIAB, (cbWABEID ? cbWABEID : cbEIDContainer), (lpWABEID ? lpWABEID : lpEIDContainer), // EntryID to open NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *)&lpContainer))) { DebugPrintError(( TEXT("OpenEntry Failed\n"))); goto out; } // Opened PAB container OK // Get us the default creation entryids if (HR_FAILED(hr = lpContainer->lpVtbl->GetProps( lpContainer, (LPSPropTagArray)&ptaCreate, MAPI_UNICODE, &cNewProps, &lpCreateEIDs) ) ) { DebugPrintError(( TEXT("Can't get container properties for WAB\n"))); // Bad stuff here! goto out; } // Validate the properites if ( lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].ulPropTag != PR_DEF_CREATE_MAILUSER || lpCreateEIDs[icrPR_DEF_CREATE_DL].ulPropTag != PR_DEF_CREATE_DL) { DebugPrintError(( TEXT("Container Property Errors\n"))); goto out; } if(ulCreateObjectType == MAPI_DISTLIST) nIndex = icrPR_DEF_CREATE_DL; else nIndex = icrPR_DEF_CREATE_MAILUSER; cbTplEID = lpCreateEIDs[nIndex].Value.bin.cb; lpTplEID = (LPENTRYID) lpCreateEIDs[nIndex].Value.bin.lpb; //Retry: if (HR_FAILED(hr = lpContainer->lpVtbl->CreateEntry( lpContainer, cbTplEID, lpTplEID, ulFlags, &lpMailUser))) { DebugPrintError(( TEXT("Creating DISTLIST failed\n"))); goto out; } if (HR_FAILED(hr = lpMailUser->lpVtbl->SetProps(lpMailUser, // this cValues, // cValues lpPropArray, // property array NULL))) { DebugPrintError(( TEXT("Setprops failed\n"))); goto out; } if ( bFirst && bShowBeforeAdding && HR_FAILED(hr = HrShowDetails( lpIAB, hWndParent, NULL, 0, NULL, NULL, NULL, (LPMAPIPROP)lpMailUser, SHOW_OBJECT, MAPI_MAILUSER, &bChangesMade))) { goto out; } hr = lpMailUser->lpVtbl->SaveChanges( lpMailUser, KEEP_OPEN_READWRITE); if(HR_FAILED(hr)) { switch(hr) { case MAPI_E_NOT_ENOUGH_DISK: hr = HandleSaveChangedInsufficientDiskSpace(hWndParent, (LPMAILUSER) lpMailUser); break; case MAPI_E_COLLISION: { LPSPropValue lpspv = NULL; if (bFirst && !HR_FAILED(HrGetOneProp((LPMAPIPROP)lpMailUser, PR_DISPLAY_NAME, &lpspv))) { switch( ShowMessageBoxParam( hWndParent, idsEntryAlreadyExists, MB_YESNO | MB_ICONEXCLAMATION, lpspv->Value.LPSZ)) { /***/ case IDNO: FreeBufferAndNull(&lpspv); hr = MAPI_W_ERRORS_RETURNED; //S_OK; goto out; break; /***/ case IDYES: // At this point the user may have modified the properties // of this MailUser. Hence we can't discard the mailuser // Instead we'll just cheat a little, change the save // flag on the mailuser directly and do a SaveChanges ((LPMailUser) lpMailUser)->ulCreateFlags |= (CREATE_REPLACE | CREATE_MERGE); hr = lpMailUser->lpVtbl->SaveChanges( lpMailUser, KEEP_OPEN_READWRITE); if(hr == MAPI_E_NOT_ENOUGH_DISK) hr = HandleSaveChangedInsufficientDiskSpace(hWndParent, (LPMAILUSER) lpMailUser); FreeBufferAndNull(&lpspv); //UlRelease(lpMailUser); //lpMailUser = NULL; //bFirst = FALSE; //goto Retry; break; } } } break; default: DebugPrintError(( TEXT("SaveChanges failed: %x\n"),hr)); goto out; break; } } DebugObjectProps((LPMAPIPROP)lpMailUser, TEXT("New Entry")); // Get the EntryID so we can return it... if (HR_FAILED(hr = lpMailUser->lpVtbl->GetProps( lpMailUser, (LPSPropTagArray)&ptaEid, MAPI_UNICODE, &cNewProps, &lpNewProps))) { DebugPrintError(( TEXT("Can't get EntryID\n"))); // Bad stuff here! goto out; } if(lpcbEntryID && lppEntryID) { *lpcbEntryID = lpNewProps[ieidPR_ENTRYID].Value.bin.cb; sc = MAPIAllocateBuffer(*lpcbEntryID, lppEntryID); if (sc != S_OK) { DebugPrintError(( TEXT("MAPIAllocateBuffer failed\n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } CopyMemory(*lppEntryID, lpNewProps[ieidPR_ENTRYID].Value.bin.lpb, *lpcbEntryID); } if(ulContObjType == MAPI_DISTLIST && *lpcbEntryID && *lppEntryID && cbEIDContainer && lpEIDContainer) AddEntryToGroupEx(lpIAB, *lpcbEntryID, *lppEntryID, cbEIDCont, lpEIDCont); hr = hrSuccess; out: if (lpMailUser) lpMailUser->lpVtbl->Release(lpMailUser); if (lpNewProps) MAPIFreeBuffer(lpNewProps); if (lpCreateEIDs) MAPIFreeBuffer(lpCreateEIDs); if (lpContainer) lpContainer->lpVtbl->Release(lpContainer); if (lpWABEID) MAPIFreeBuffer(lpWABEID); return hr; } //$$///////////////////////////////////////////////////////////////////// // // HrGetWABTemplateID - Gets the WABs default Template ID for MailUsers // or DistLists // // lpIAB - AdrBook Object // ulObjectType - MailUser or DistList // cbEntryID, lpEntryID - returned EntryID of this template // ///////////////////////////////////////////////////////////////////////// HRESULT HrGetWABTemplateID( LPADRBOOK lpAdrBook, ULONG ulObjectType, ULONG * lpcbEID, LPENTRYID * lppEID) { LPABCONT lpContainer = NULL; HRESULT hr = hrSuccess; SCODE sc = ERROR_SUCCESS; ULONG ulObjType = 0; ULONG cbWABEID = 0; LPENTRYID lpWABEID = NULL; LPSPropValue lpCreateEIDs = NULL; LPSPropValue lpNewProps = NULL; ULONG cNewProps; ULONG nIndex; DebugPrintTrace(( TEXT("HrGetWABTemplateID: entry\n"))); if ( (!lpAdrBook) || ((ulObjectType != MAPI_MAILUSER) && (ulObjectType != MAPI_DISTLIST)) ) { hr = MAPI_E_INVALID_PARAMETER; goto out; } *lpcbEID = 0; *lppEID = NULL; if (HR_FAILED(hr = lpAdrBook->lpVtbl->GetPAB(lpAdrBook, &cbWABEID, &lpWABEID))) { DebugPrintError(( TEXT("GetPAB Failed\n"))); goto out; } if (HR_FAILED(hr = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, cbWABEID, // size of EntryID to open lpWABEID, // EntryID to open NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *)&lpContainer))) { DebugPrintError(( TEXT("OpenEntry Failed\n"))); goto out; } // Opened PAB container OK // Get us the default creation entryids if (HR_FAILED(hr = lpContainer->lpVtbl->GetProps( lpContainer, (LPSPropTagArray)&ptaCreate, MAPI_UNICODE, &cNewProps, &lpCreateEIDs) ) ) { DebugPrintError(( TEXT("Can't get container properties for WAB\n"))); // Bad stuff here! goto out; } // Validate the properites if ( lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].ulPropTag != PR_DEF_CREATE_MAILUSER || lpCreateEIDs[icrPR_DEF_CREATE_DL].ulPropTag != PR_DEF_CREATE_DL) { DebugPrintError(( TEXT("Container Property Errors\n"))); goto out; } if(ulObjectType == MAPI_DISTLIST) nIndex = icrPR_DEF_CREATE_DL; else nIndex = icrPR_DEF_CREATE_MAILUSER; *lpcbEID = lpCreateEIDs[nIndex].Value.bin.cb; sc = MAPIAllocateBuffer(*lpcbEID,lppEID); if (sc != S_OK) { DebugPrintError(( TEXT("MAPIAllocateBuffer failed\n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } CopyMemory(*lppEID,lpCreateEIDs[nIndex].Value.bin.lpb,*lpcbEID); out: if (lpCreateEIDs) MAPIFreeBuffer(lpCreateEIDs); if (lpContainer) lpContainer->lpVtbl->Release(lpContainer); if (lpWABEID) MAPIFreeBuffer(lpWABEID); return hr; } //$$///////////////////////////////////////////////////////////////////// // // UpdateListViewItemsByName - Updates the displayed name corresponding to // each entry - by First Name or by Last Name depending on Sort criteria // Called by the Sort routine ... // // ///////////////////////////////////////////////////////////////////////// void UpdateListViewItemsByName(HWND hWndLV, BOOL bSortByLastName) { LV_ITEM lvi = {0}; ULONG ulCount = 0; ULONG i; lvi.mask = LVIF_PARAM; lvi.iSubItem = colDisplayName; lvi.lParam = 0; ulCount = ListView_GetItemCount(hWndLV); if (ulCount<=0) return; for(i=0;iszDisplayName, lpItem->szByLastName,ARRAYSIZE(lpItem->szDisplayName)); else StrCpyN(lpItem->szDisplayName, lpItem->szByFirstName,ARRAYSIZE(lpItem->szDisplayName)); ListView_SetItem(hWndLV, &lvi); ListView_SetItemText(hWndLV,i,colDisplayName,lpItem->szDisplayName); } return; } //$$----------------------------------------------------------------------- //| //| SortListViewColumn - Sorting Routine for the List View //| //| HWndLV - handle of List View //| iSortCol - ColumnSorted by ... //| lpSortInfo - this particular dialogs sort info structure ... //| bUseCurrentSettings - sometimes we want to call this function but dont want //| to change the sort settings - those times we set this to true, in which //| case, the iSortCol parameter is ignored //| //*------------------------------------------------------------------------ void SortListViewColumn(LPIAB lpIAB, HWND hWndLV, int iSortCol, LPSORT_INFO lpSortInfo, BOOL bUseCurrentSettings) { HCURSOR hOldCur = NULL; if(!bUseCurrentSettings) { lpSortInfo->iOlderSortCol = lpSortInfo->iOldSortCol; if (lpSortInfo->iOldSortCol == iSortCol) { // if we previously sorted by this column then toggle the sort mode if(iSortCol == colDisplayName) { // For Display Name, the sort order is // LastName Ascending // False True // False False // True True // True False if(lpSortInfo->bSortByLastName && !lpSortInfo->bSortAscending) lpSortInfo->bSortByLastName = FALSE; else if(!lpSortInfo->bSortByLastName && !lpSortInfo->bSortAscending) lpSortInfo->bSortByLastName = TRUE; } lpSortInfo->bSortAscending = !lpSortInfo->bSortAscending; } else { // this is a new column - sort ascending lpSortInfo->bSortAscending = TRUE; lpSortInfo->iOldSortCol = iSortCol; } } hOldCur = SetCursor(LoadCursor(NULL, IDC_WAIT)); UpdateListViewItemsByName(hWndLV,lpSortInfo->bSortByLastName); ListView_SortItems( hWndLV, ListViewSort, (LPARAM) lpSortInfo); SetColumnHeaderBmp(hWndLV, *lpSortInfo); SetCursor(hOldCur); // Highlight the first selected item we can find if (ListView_GetSelectedCount(hWndLV) > 0) ListView_EnsureVisible(hWndLV, ListView_GetNextItem(hWndLV, -1, LVNI_SELECTED), FALSE); WriteRegistrySortInfo(lpIAB, *lpSortInfo); return; } const LPTSTR lpszRegSortKeyName = TEXT("Software\\Microsoft\\WAB\\WAB Sort State"); const LPTSTR lpszRegSortKeyValueName = TEXT("State"); const LPTSTR lpszRegPositionKeyValueName = TEXT("Position"); const LPTSTR lpszRegFindPositionKeyValueName = TEXT("FindPosition"); //$$ /************************************************************************************ - ReadRegistrySortInfo - * Purpose: * Getss the previously stored Sort Info into the registry so we can have * persistence between sessions. * * Arguments: * LPSORT_INFO lpSortInfo * * Returns: * BOOL * *************************************************************************************/ BOOL ReadRegistrySortInfo(LPIAB lpIAB, LPSORT_INFO lpSortInfo) { BOOL bRet = FALSE; HKEY hKey = NULL; HKEY hKeyRoot = (lpIAB && lpIAB->hKeyCurrentUser) ? lpIAB->hKeyCurrentUser : HKEY_CURRENT_USER; DWORD dwLenName = sizeof(SORT_INFO); DWORD dwDisposition = 0; DWORD dwType = 0; if (!lpSortInfo) goto out; // default value // lpSortInfo->iOldSortCol = colDisplayName; lpSortInfo->iOlderSortCol = colDisplayName; lpSortInfo->bSortAscending = TRUE; lpSortInfo->bSortByLastName = bDNisByLN; // Open key if (ERROR_SUCCESS != RegCreateKeyEx(hKeyRoot, lpszRegSortKeyName, 0, //reserved NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &dwDisposition)) { goto out; } if(dwDisposition == REG_CREATED_NEW_KEY) goto out; // Now Read this key if (ERROR_SUCCESS != RegQueryValueEx(hKey, lpszRegSortKeyValueName, NULL, &dwType, (LPBYTE) lpSortInfo, &dwLenName)) { DebugTrace( TEXT("RegQueryValueEx failed\n")); goto out; } bRet = TRUE; out: if (hKey) RegCloseKey(hKey); return(bRet); } //$$ /************************************************************************************* - WriteRegistrySortInfo - * Purpose: * Write the current Sort Info into the registry so we can have * persistence between sessions. * * Arguments: * SORT_INFO SortInfo * * Returns: * BOOL * *************************************************************************************/ BOOL WriteRegistrySortInfo(LPIAB lpIAB, SORT_INFO SortInfo) { BOOL bRet = FALSE; // const LPTSTR lpszRegSortKeyName = TEXT( TEXT("Software\\Microsoft\\WAB\\WAB Sort State")); HKEY hKey = NULL; HKEY hKeyRoot = (lpIAB && lpIAB->hKeyCurrentUser) ? lpIAB->hKeyCurrentUser : HKEY_CURRENT_USER; DWORD dwLenName = sizeof(SORT_INFO); DWORD dwDisposition =0; // Open key if (ERROR_SUCCESS != RegCreateKeyEx(hKeyRoot, lpszRegSortKeyName, 0, //reserved NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition)) { DebugTrace( TEXT("RegCreateKeyEx failed\n")); goto out; } // Now Write this key if (ERROR_SUCCESS != RegSetValueEx( hKey, lpszRegSortKeyValueName, 0, REG_BINARY, (LPBYTE) &SortInfo, dwLenName)) { DebugTrace( TEXT("RegSetValue failed\n")); goto out; } bRet = TRUE; out: if (hKey) RegCloseKey(hKey); return(bRet); } //$$************************************************************\ //* //* SetLocalizedDisplayName - sets the localized display name as //* per localization information/ //* //* szBuf points to a predefined buffer of length ulzBuf. //* lpszFirst/Middle/Last/Company can be NULL //* If szBuffer is null and ulszBuf=0, then we return the lpszBuffer //* created here in the lppRetBuf parameter... //* Caller has to LocalFree lppszRetBuf //* // // Rules for creating DisplayName // // - If there is no display name, and there is a first/middle/last name, // we make display name = localized(First/Middle/Last) // - If there is no DN or FML, but NN, we make DN = NickName // - If there is no DN, FML, NN but Company Name, we make // DN = Company Name // - If there is no DN, FML, NN, CN, we fail //\***************************************************************/ BOOL SetLocalizedDisplayName( LPTSTR lpszFirstName, LPTSTR lpszMiddleName, LPTSTR lpszLastName, LPTSTR lpszCompanyName, LPTSTR lpszNickName, LPTSTR * lppszBuf, ULONG ulszBuf, BOOL bDNbyLN, LPTSTR lpTemplate, LPTSTR * lppszRetBuf) { LPTSTR szBuf = NULL; LPTSTR szResource = NULL; LPTSTR lpszArg[3]; LPTSTR lpszFormatName = NULL; LPVOID lpszBuffer = NULL; BOOL bRet = FALSE; int idResource =0; if (!lpszFirstName && !lpszMiddleName && !lpszLastName && !lpszNickName && !lpszCompanyName) goto out; if (lppszBuf) szBuf = *lppszBuf; if(lpTemplate) szResource = lpTemplate; else szResource = bDNbyLN ? (bDNisByLN ? szResourceDNByLN : szResourceDNByCommaLN) : szResourceDNByFN; if (!lpszFirstName && !lpszMiddleName && !lpszLastName) { if(lpszNickName) { DWORD cchSize=lstrlen(lpszNickName) + 1; // Use the NickName if (! (lpszFormatName = LocalAlloc(LPTR, sizeof(TCHAR)*cchSize))) goto out; StrCpyN(lpszFormatName, lpszNickName, cchSize); } else if(lpszCompanyName) { DWORD cchSize=lstrlen(lpszCompanyName) + 1; // just use company name if (! (lpszFormatName = LocalAlloc(LPTR, sizeof(TCHAR)*cchSize))) goto out; StrCpyN(lpszFormatName, lpszCompanyName, cchSize); } else goto out; //shouldnt happen } else { //Bug #101350 - (erici) lstrlen will AV (and handle it) if passed a NULL if( (lpszFirstName && (lstrlen(lpszFirstName) >= MAX_UI_STR)) || (lpszMiddleName && (lstrlen(lpszMiddleName) >= MAX_UI_STR)) || (lpszLastName && (lstrlen(lpszLastName) >= MAX_UI_STR)) ) goto out; lpszArg[0] = lpszFirstName ? lpszFirstName : szEmpty; lpszArg[1] = lpszMiddleName? lpszMiddleName : szEmpty; lpszArg[2] = lpszLastName ? lpszLastName : szEmpty; // FormatMessage doesnt do partial copying .. so we need to assimilate the name // first and then squeeze it into our buffer ... // if(!FormatMessage( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, szResource, 0, //ignored 0, //ignored (LPTSTR) &lpszBuffer, MAX_UI_STR, (va_list *)lpszArg)) { DebugPrintError(( TEXT("FormatStringFailed: %d\n"),GetLastError())); goto out; } lpszFormatName = (LPTSTR) lpszBuffer; TrimSpaces(lpszFormatName); // If we dont have a last name and the sort is by last name, then // we will get an ugly looking comma in the beginning for the english // version only .. special case cheating here to remove that coma // if(bDNbyLN && (!lpszLastName || !lstrlen(lpszLastName))) { BOOL bSkipChar = FALSE; if (lpszFormatName[0]==',') { bSkipChar = TRUE; } else { LPTSTR lp = lpszFormatName; if(*lp == 0x81 && *(lp+1) == 0x41) // Japanese Comma is 0x810x41.. will this work ?? bSkipChar = TRUE; } if(bSkipChar) { LPTSTR lpszTmp = CharNext(lpszFormatName); StrCpyN(lpszFormatName, lpszTmp, MAX_UI_STR); TrimSpaces(lpszFormatName); } } // Whatever the localizers combination of first middle last names for the // display name (eg FML, LMF, LFM etc), if the middle element is missing, // we'll get 2 blank spaces in the display name and we need to remove that. // Search and replace double blanks. { LPTSTR lpsz=lpszFormatName,lpsz1=NULL; while(lpsz && (*lpsz!='\0')) { lpsz1 = CharNext(lpsz); if (IsSpace(lpsz) && IsSpace(lpsz1)) { StrCpyN(lpsz, lpsz1, lstrlen(lpsz1)+1); // this is safe because we are // copying the string over itself continue; // use same lpsz } else { lpsz = lpsz1; } } } } // If we were provided a buffer, use it ... if((lppszRetBuf) && (szBuf == NULL) && (ulszBuf == 0)) { *lppszRetBuf = lpszFormatName; } else { CopyTruncate(szBuf, lpszFormatName, ulszBuf); } bRet = TRUE; out: if((lpszFormatName) && (lppszRetBuf == NULL) && (ulszBuf != 0)) LocalFreeAndNull(&lpszFormatName); return bRet; } //$$ //*------------------------------------------------------------------------ //| SetChildDefaultGUIFont: Callback function that sets all the children of //| any window to the default GUI font - //| needed for localization. //| //| hWndChild - handle to child //| lParam - ignored //| //*------------------------------------------------------------------------ STDAPI_(BOOL) SetChildDefaultGUIFont(HWND hWndChild, LPARAM lParam) { // Code below is stolen from Shlwapi.dll // LPPTGDATA lpPTGData=GetThreadStoragePointer(); HFONT hfont; HFONT hfontDefault; LOGFONT lf; LOGFONT lfDefault; HWND hWndParent = GetParent(hWndChild); hfont = GetWindowFont(hWndParent ? hWndParent : hWndChild); GetObject(hfont, sizeof(LOGFONT), &lf); SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lfDefault, 0); if ( (lfDefault.lfCharSet == lf.lfCharSet) && (lfDefault.lfHeight == lf.lfHeight) && (PARENT_IS_DIALOG == lParam) ) { // if the dialog already has the correct character set and size // don't do anything. return TRUE; } if(PARENT_IS_DIALOG == lParam) hfontDefault = pt_hDlgFont; else hfontDefault = pt_hDefFont; // If we already have hfont created, use it. if(!hfontDefault) { // [bobn] Raid #88470: We should use the default size on dialogs if(PARENT_IS_DIALOG == lParam) lfDefault.lfHeight = lf.lfHeight; if (!(hfontDefault=CreateFontIndirect(&lfDefault))) { // restore back in failure hfontDefault = hfont; } if (hfontDefault != hfont) { if(PARENT_IS_DIALOG == lParam) pt_hDlgFont = hfontDefault; else pt_hDefFont = hfontDefault; } } if(!hfontDefault) hfontDefault = GetStockObject(DEFAULT_GUI_FONT); SetWindowFont(hWndChild, hfontDefault, FALSE); return TRUE; } //$$ //*------------------------------------------------------------------------ //| HrGetLDAPContentsList: Gets ContentsList from an LDAP container - Opens //| a container - populates it using the given restriction, //| and puts its contents in the List View //| //| lpIAB - Address Book object //| cbContainerEID, lpContainerEID - LDAP Container EntryID //| SortInfo - Current Sort State //| lpPropRes - Property Restriction with which to do the Search //| lpPTA - PropTagArray to return - currently ignored //| ulFlags - 0 - currently ignored //| lppContentsList - Returned, filled Contents List //| lpAdvFilter - alternate advanced filter used in place of the property restriction //*------------------------------------------------------------------------ HRESULT HrGetLDAPContentsList(LPADRBOOK lpAdrBook, ULONG cbContainerEID, LPENTRYID lpContainerEID, SORT_INFO SortInfo, LPSRestriction lpPropRes, LPTSTR lpAdvFilter, LPSPropTagArray lpPTA, ULONG ulFlags, LPRECIPIENT_INFO * lppContentsList) { HRESULT hr = hrSuccess; HRESULT hrSaveTmp = E_FAIL; // temporarily saves partial completion error so it can be propogated to calling funtion ULONG ulObjectType = 0; LPCONTAINER lpContainer = NULL; LPMAPITABLE lpContentsTable = NULL; LPSRowSet lpSRowSet = NULL; ULONG i = 0,j=0; LPRECIPIENT_INFO lpItem = NULL; LPRECIPIENT_INFO lpLastListItem = NULL; DebugPrintTrace(( TEXT("-----HrGetLDAPContentsList: entry\n"))); if(!lpPropRes && !lpAdvFilter) { DebugPrintError(( TEXT("No search restriction created\n"))); hr = E_FAIL; goto out; } // // First we need to open the container object corresponding to this Container EntryID // hr = lpAdrBook->lpVtbl->OpenEntry( lpAdrBook, cbContainerEID, lpContainerEID, NULL, 0, &ulObjectType, (LPUNKNOWN *) &lpContainer); if(HR_FAILED(hr)) { DebugPrintError(( TEXT("OpenEntry Failed: %x\n"),hr)); goto out; } // // Now we do a get contents table on this container ... // hr = lpContainer->lpVtbl->GetContentsTable( lpContainer, MAPI_UNICODE, &lpContentsTable); if(HR_FAILED(hr)) { DebugPrintError(( TEXT("GetContentsTable Failed: %x\n"),hr)); goto out; } /**** //$$$$$$$$$$$$ // Test code { SPropValue valAnr; SRestriction resAnr; LPSRowSet prws; LPTSTR lpsz = TEXT("Vikram"); // Set up the Ambiguous Name Resolution property value. valAnr.ulPropTag = PR_ANR; valAnr.Value.LPSZ = lpsz; // Set up the Ambiguous Name Resolution restriction. resAnr.rt = RES_PROPERTY; resAnr.res.resProperty.relop = RELOP_EQ; resAnr.res.resProperty.ulPropTag = valAnr.ulPropTag; resAnr.res.resProperty.lpProp = &valAnr; // Restrict the contents table. // Set columns and query rows to see what state we fall in. We ask for one more // row than the value of the Few/Many threshold. This allows us to tell whether // we are a Few/Many ambiguity. hr = lpContentsTable->lpVtbl->Restrict(lpContentsTable, &resAnr, TBL_BATCH); hr = lpContentsTable->lpVtbl->SetColumns(lpContentsTable, (LPSPropTagArray)&ptaResolveDefaults, TBL_BATCH); hr = lpContentsTable->lpVtbl->SeekRow(lpContentsTable, BOOKMARK_BEGINNING, 0, 0); hr = lpContentsTable->lpVtbl->QueryRows(lpContentsTable, 1, 0, &prws); FreeProws(prws); } //$$$$$$$$$$$$ /*****/ // If the user has specified an advanced filter, we need to figure out some way to // pass it to the LDAP routines while still taking advantage of our LDAP contents table // To do this we will do a hack and pass in the lpAdvFilter cast to a LPPropRes and then // recast back at the other end // This may break if any changes are made to the table implementation // // We now do the find rows hr = lpContentsTable->lpVtbl->FindRow( lpContentsTable, lpAdvFilter ? (LPSRestriction) lpAdvFilter : lpPropRes, BOOKMARK_BEGINNING, lpAdvFilter ? LDAP_USE_ADVANCED_FILTER : 0); //flags if(HR_FAILED(hr)) { DebugPrintError(( TEXT("FindRow Failed: %x\n"),hr)); goto out; } // if this was a partial completion error - we want to treat it as success // but also propogate it to the calling function if(hr == MAPI_W_PARTIAL_COMPLETION) hrSaveTmp = hr; // If we got this far, then we have a populated table // We should be able to do a Query Rows here ... hr = SetRecipColumns(lpContentsTable); if(HR_FAILED(hr)) goto out; hr = HrQueryAllRows(lpContentsTable, NULL, NULL, NULL, 0, &lpSRowSet); if (HR_FAILED(hr)) { DebugPrintError(( TEXT("HrQueryAllRows Failed: %x\n"),hr)); goto out; } // // if there's anything in the contents list flush it away // if (*lppContentsList) { lpItem = (*lppContentsList); (*lppContentsList) = lpItem->lpNext; FreeRecipItem(&lpItem); } *lppContentsList = NULL; lpItem = NULL; for(i=0;icRows;i++) { LPSPropValue lpPropArray = lpSRowSet->aRow[i].lpProps; ULONG ulcPropCount = lpSRowSet->aRow[i].cValues; lpItem = LocalAlloc(LMEM_ZEROINIT, sizeof(RECIPIENT_INFO)); if (!lpItem) { DebugPrintError(( TEXT("LocalAlloc Failed \n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } GetRecipItemFromPropArray(ulcPropCount, lpPropArray, &lpItem); // The critical prop is display name - without it we are nothing ... // If no display name, junk this entry and continue .. if (!lstrlen(lpItem->szDisplayName) || (lpItem->cbEntryID == 0)) //This entry id is not allowed { FreeRecipItem(&lpItem); continue; } // Tag this as an item from the contents and not from the original AdrList lpItem->ulOldAdrListEntryNumber = 0; // The entryids are in sorted order by display name // Depending on the sort order - we want this list to also be sorted by display // name or by reverse display name ... if (SortInfo.bSortByLastName) StrCpyN(lpItem->szDisplayName,lpItem->szByLastName,ARRAYSIZE(lpItem->szDisplayName)); if(!SortInfo.bSortAscending) { //Add it to the contents linked list lpItem->lpNext = (*lppContentsList); if (*lppContentsList) (*lppContentsList)->lpPrev = lpItem; lpItem->lpPrev = NULL; *lppContentsList = lpItem; } else { if(*lppContentsList == NULL) (*lppContentsList) = lpItem; if(lpLastListItem) lpLastListItem->lpNext = lpItem; lpItem->lpPrev = lpLastListItem; lpItem->lpNext = NULL; lpLastListItem = lpItem; } lpItem = NULL; } //for i .... // reset this error if applicable so calling function can treat it correctly if(hrSaveTmp == MAPI_W_PARTIAL_COMPLETION) hr = hrSaveTmp; out: if(lpSRowSet) FreeProws(lpSRowSet); if(lpContentsTable) lpContentsTable->lpVtbl->Release(lpContentsTable); if(lpContainer) lpContainer->lpVtbl->Release(lpContainer); if (HR_FAILED(hr)) { while(*lppContentsList) { lpItem = *lppContentsList; *lppContentsList=lpItem->lpNext; FreeRecipItem(&lpItem); } } return hr; } //$$ /****************************************************************************** // // HrGetWABContents - Gets and fills the current list view with contents from the // local store. // // hWndList - Handle to List View which we will populate // lpIAB - Handle to Address Bok object // SortInfo - Current Sort State // lppContentsList - linked list in which we will store info about entries // /******************************************************************************/ HRESULT HrGetWABContents( HWND hWndList, LPADRBOOK lpAdrBook, LPSBinary lpsbContainer, SORT_INFO SortInfo, LPRECIPIENT_INFO * lppContentsList) { HRESULT hr = hrSuccess; LPIAB lpIAB = (LPIAB) lpAdrBook; int nSelectedItem = ListView_GetNextItem(hWndList, -1, LVNI_SELECTED); if(nSelectedItem < 0) nSelectedItem = 0; SendMessage(hWndList, WM_SETREDRAW, FALSE, 0L); ClearListView(hWndList, lppContentsList); if (HR_FAILED(hr = HrGetWABContentsList( lpIAB, SortInfo, NULL, NULL, 0, lpsbContainer, FALSE, lppContentsList))) { goto out; } // There is a performance issue of filling names // If names are sorted by first name and are by first col, // we can show them updated - otherwise we cant if (HR_FAILED(hr = HrFillListView( hWndList, *lppContentsList))) { goto out; } /* if((SortInfo.iOldSortCol == colDisplayName) && (!SortInfo.bSortByLastName)) { // Already Sorted SetColumnHeaderBmp(hWndList, SortInfo); } else */ { // Otherwise sort SortListViewColumn(lpIAB, hWndList, colDisplayName, &SortInfo, TRUE); } /* if (ListView_GetSelectedCount(hWndList) <= 0) { // nothing selected - so select 1st item // Select the first item in the List View LVSelectItem(hWndList, 0); } else { LVSelectItem(hWndList, ListView_GetNextItem(hWndList, -1, LVNI_SELECTED)); } */ LVSelectItem(hWndList, nSelectedItem); out: SendMessage(hWndList, WM_SETREDRAW, TRUE, 0L); return(hr); } //$$ /******************************************************************************/ // // HrGetLDAPSearchRestriction - // // // For a simple search we have the following data to work with // Country - PR_COUNTRY // DisplayName - PR_DISPLAY_NAME // // For a detailed search // We have the following data to work with // Country - PR_COUNTRY // FirstName - PR_GIVEN_NAME // LastName - PR_SURNAME // EMail - PR_EMAIL_ADDRESS // Organization - PR_COMPANY_NAME // // /******************************************************************************/ HRESULT HrGetLDAPSearchRestriction(LDAP_SEARCH_PARAMS LDAPsp, LPSRestriction lpSRes) { SRestriction SRes = {0}; LPSRestriction lpPropRes = NULL; ULONG ulcPropCount = 0; HRESULT hr = E_FAIL; ULONG i = 0; SCODE sc = ERROR_SUCCESS; lpSRes->rt = RES_AND; ulcPropCount = 0; if (lstrlen(LDAPsp.szData[ldspDisplayName])) ulcPropCount++; //PR_EMAIL_ADDRESS and PR_DISPLAY_NAME if (lstrlen(LDAPsp.szData[ldspEmail])) ulcPropCount++; if (!ulcPropCount) { DebugPrintError(( TEXT("No Search Props!\n"))); goto out; } lpSRes->res.resAnd.cRes = ulcPropCount; lpSRes->res.resAnd.lpRes = NULL; sc = MAPIAllocateBuffer(ulcPropCount * sizeof(SRestriction), (LPVOID *) &(lpSRes->res.resAnd.lpRes)); if (sc != S_OK) { DebugPrintError(( TEXT("MAPIAllocateBuffer failed\n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } if(!(lpSRes->res.resAnd.lpRes)) { DebugPrintError(( TEXT("Error Allocating Memory\n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } lpPropRes = lpSRes->res.resAnd.lpRes; ulcPropCount = 0; for(i=0;iulPropTag = ulPropTag; lpPropArray->Value.LPSZ = NULL; cchSize=lstrlen(LDAPsp.szData[i])+1; MAPIAllocateMore(sizeof(TCHAR)*cchSize, lpPropRes, (LPVOID *) (&(lpPropArray->Value.LPSZ))); if(!lpPropArray->Value.LPSZ) { DebugPrintError(( TEXT("Error allocating memory\n"))); hr = MAPI_E_NOT_ENOUGH_MEMORY; goto out; } StrCpyN(lpPropArray->Value.LPSZ,LDAPsp.szData[i], cchSize); ulcPropCount++; } } hr = S_OK; out: return hr; } //$$///////////////////////////////////////////////////////////////////////// // // ShowMessageBoxParam - Generic MessageBox displayer .. saves space all over // // hWndParent - Handle of Message Box Parent // MsgID - resource id of message string // ulFlags - MessageBox flags // ... - format parameters // /////////////////////////////////////////////////////////////////////////// int __cdecl ShowMessageBoxParam(HWND hWndParent, int MsgId, int ulFlags, ...) { TCHAR szBuf[MAX_BUF_STR] = TEXT(""); TCHAR szCaption[MAX_PATH] = TEXT(""); LPTSTR lpszBuffer = NULL; int iRet = 0; va_list vl; va_start(vl, ulFlags); LoadString(hinstMapiX, MsgId, szBuf, ARRAYSIZE(szBuf)); // if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER, szBuf, 0,0, //ignored (LPTSTR)&lpszBuffer, MAX_BUF_STR, //MAX_UI_STR // (LPTSTR *)&(lpParam))) { (va_list *)&vl)) { TCHAR szCaption[MAX_PATH]; *szCaption = '\0'; if(hWndParent) GetWindowText(hWndParent, szCaption, ARRAYSIZE(szCaption)); if(!lstrlen(szCaption)) // if no caption get the parents caption - this is necessary for property sheets { if(hWndParent) GetWindowText(GetParent(hWndParent), szCaption, ARRAYSIZE(szCaption)); if(!lstrlen(szCaption)) //if still not caption, get the generic title LoadString(hinstMapiX, IDS_ADDRBK_CAPTION, szCaption, ARRAYSIZE(szCaption)); } iRet = MessageBox(hWndParent, lpszBuffer, szCaption, ulFlags); LocalFreeAndNull(&lpszBuffer); } va_end(vl); return(iRet); } //$$///////////////////////////////////////////////////////////////////////// // // ShowMessageBox - Generic MessageBox displayer .. saves space all over // // hWndParent - Handle of Message Box Parent // MsgID - resource id of message string // ulFlags - MessageBox flags // /////////////////////////////////////////////////////////////////////////// int ShowMessageBox(HWND hWndParent, int MsgId, int ulFlags) { TCHAR szBuf[MAX_BUF_STR]; TCHAR szCaption[MAX_PATH]; szCaption[0]='\0'; szBuf[0]='\0'; LoadString(hinstMapiX, MsgId, szBuf, ARRAYSIZE(szBuf)); if(hWndParent) { GetWindowText(hWndParent, szCaption, ARRAYSIZE(szCaption)); if(!lstrlen(szCaption)) { // if we cant get a caption, get the windows parents caption HWND hWnd = GetParent(hWndParent); GetWindowText(hWnd, szCaption, ARRAYSIZE(szCaption)); } } if(!lstrlen(szCaption)) { //if we cant get the parents caption, get a generic title LoadString(hinstMapiX, IDS_ADDRBK_CAPTION, szCaption, ARRAYSIZE(szCaption)); } return MessageBox(hWndParent, szBuf, szCaption, ulFlags); } //$$///////////////////////////////////////////////////////////////////////////// // // my_atoi - personal version of atoi function // // lpsz - string to parse into numbers - non numeral characters are ignored // ///////////////////////////////////////////////////////////////////////////////// int my_atoi(LPTSTR lpsz) { int i=0; int nValue = 0; if(lpsz) { if (lstrlen(lpsz)) { nValue = 0; while((lpsz[i]!='\0')&&(i<=lstrlen(lpsz))) { int tmp = lpsz[i]-'0'; if(tmp <= 9) nValue = nValue*10 + tmp; i++; } } } return nValue; } #ifdef OLD_STUFF //$$///////////////////////////////////////////////////////////////////////////// // // FillComboLDAPCountryNames - Fills a dropdown conbo with LDAP country names // // hWndCombo - Handle of Combo // ///////////////////////////////////////////////////////////////////////////////// void FillComboLDAPCountryNames(HWND hWndCombo) { TCHAR szBuf[MAX_UI_STR]; int nCountrys = 0; int i=0; LoadString(hinstMapiX, idsCountryCount,szBuf,ARRAYSIZE(szBuf)); nCountrys = my_atoi(szBuf); if(nCountrys == 0) nCountrys = MAX_COUNTRY_NUM; for(i=0;i0) { // We got a valid country but it obviously doesnt have a code if(szCountry) StrCpyN(szCountry, szTemp, cchCountry); if(szCountryCode) { int i =0; int cMax=0; TCHAR szBufCountry[MAX_UI_STR]; szBufCountry[0]='\0'; StrCpyN(szBufCountry,szTemp,ARRAYSIZE(szBufCountry)); LoadString(hinstMapiX, idsCountryCount,szTemp,ARRAYSIZE(szTemp)); cMax = my_atoi(szTemp); for(i=0;iulObjectType == MAPI_DISTLIST) { if(lpsbEID) { lpsbEID->cb = lpItem->cbEntryID; lpsbEID->lpb = (LPBYTE)lpItem->lpEntryID; } return TRUE; } return FALSE; } //$$ //////////////////////////////////////////////////////////////////////////////// // // GetCurrentOptionsState - looks at the current state based on the ListView and // the Combo and decides which options should be enabled or disabled // // hWndCombo - handle of Show Names combo // hWndLV - handle of ListView to look at // lpbState - points to a predefined array of BOOL bState[tbMAX] // //////////////////////////////////////////////////////////////////////////////// void GetCurrentOptionsState(HWND hWndLVContainer, HWND hWndLV, LPBOOL lpbState) { int i = 0; ULONG cbEID = 0; LPENTRYID lpEID = NULL; BYTE bType = 0; int nItemCount = ListView_GetItemCount(hWndLV); int nSelectedCount = ListView_GetSelectedCount(hWndLV); for(i=0;i 0) lpbState[tbPrint] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbDelete] = TRUE; else lpbState[tbPrint] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbDelete] = FALSE; if(nSelectedCount <= 0) lpbState[tbCopy] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbDelete] = FALSE; else if(nSelectedCount > 1) //lpbState[tbaction] = lpbState[tbProperties] = FALSE; } else if(bType == WAB_LDAP_CONTAINER) { lpbState[tbDelete] = lpbState[tbNew] = lpbState[tbNewEntry] = lpbState[tbNewGroup] = lpbState[tbNewFolder] = FALSE; if(nItemCount > 0) lpbState[tbPrint] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbAddToWAB] = TRUE; else lpbState[tbPrint] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbAddToWAB] = FALSE; if(nSelectedCount <= 0) lpbState[tbPaste] = lpbState[tbCopy] = /*lpbState[tbAction] =*/ lpbState[tbProperties] = lpbState[tbDelete] = FALSE; else if(nSelectedCount > 1) //lpbState[tbAction] = lpbState[tbProperties] = FALSE; } else { // cant handle this case so turn everything off .... for(i=0;ilpWABCurrentUserFolder->sbEID.cb; lpContEID = (LPENTRYID)(lpIAB->lpWABCurrentUserFolder->sbEID.lpb); } hr = HrCreateNewEntry( lpAdrBook, hWndParent, MAPI_MAILUSER, //MAILUSER or DISTLIST cbContEID, lpContEID, MAPI_ABCONT,//add to root container only CREATE_CHECK_DUP_STRICT, TRUE, ulcProps, lpPropArray, lpcbOutputEID, lppOutputEID); } if(HR_FAILED(hr)) { DebugPrintError(( TEXT("HrCreateNewEntry failed:%x\n"))); goto out; } out: if (lpPropArray) MAPIFreeBuffer(lpPropArray); return hr; } //$$//////////////////////////////////////////////////////////////////////////////// // // HrAddToWAB - Adds an LDAP or one-off entry to the Address Book // All such items will be added to the root container only // // lpIAB - ADRBOOK object // hWndLV - Listview window handle // lpftLast - WAB file time at last update // // //////////////////////////////////////////////////////////////////////////////////// HRESULT HrAddToWAB( LPADRBOOK lpIAB, HWND hWndLV, LPFILETIME lpftLast) { HRESULT hr = hrSuccess; HRESULT hrDeferred = hrSuccess; int nSelectedCount = 0; LPRECIPIENT_INFO lpItem = NULL; ULONG cbEID = 0; LPENTRYID lpEID = NULL; ULONG i = 0; HCURSOR hOldCursor = SetCursor(LoadCursor(NULL,IDC_WAIT)); // // Looks at the selected item in the List View, // gets its entry id, gets its props, creates a new item with those props // if (!lpIAB || !hWndLV) { hr = MAPI_E_INVALID_PARAMETER; goto out; } nSelectedCount = ListView_GetSelectedCount(hWndLV); if(nSelectedCount <= 0) { ShowMessageBox(GetParent(hWndLV), idsNoItemsSelectedForAdding, MB_ICONEXCLAMATION | MB_OK); hr = E_FAIL; goto out; } else { // Walk through all the items processing the one by one int iItemIndex = 0; int iLastItemIndex = -1; iItemIndex = ListView_GetNextItem(hWndLV, iLastItemIndex, LVNI_SELECTED); while(iItemIndex != -1) { iLastItemIndex = iItemIndex; lpItem = GetItemFromLV(hWndLV, iItemIndex); if(lpItem) { nSelectedCount--; //now tracks how many are left hr = HrEntryAddToWAB( lpIAB, GetParent(hWndLV), lpItem->cbEntryID, lpItem->lpEntryID, &cbEID, &lpEID); if(HR_FAILED(hr)) { DebugPrintError(( TEXT("HrEntryAddToWAB failed:%x\n"))); if(hr != MAPI_E_USER_CANCEL) hrDeferred = hr; if (lpEID) MAPIFreeBuffer(lpEID); lpEID = NULL; if(hr == MAPI_E_USER_CANCEL && nSelectedCount) { // user canceled this one and some other remain .. // Ask if he wants to cancel the whole import operation if(IDYES == ShowMessageBox(GetParent(hWndLV), idsContinueAddingToWAB, MB_YESNO | MB_ICONEXCLAMATION)) { goto out; } } // just keep going on if there are any remaining entries goto end_loop; } // Update the wab file write time so the timer doesn't // catch this change and refresh. //if (lpftLast) { // CheckChangedWAB(((LPIAB)lpIAB)->lpPropertyStore, lpftLast); //} if (lpEID) MAPIFreeBuffer(lpEID); lpEID = NULL; } end_loop: // Get the next selected item ... iItemIndex = ListView_GetNextItem(hWndLV, iLastItemIndex, LVNI_SELECTED); } } out: if (lpEID) MAPIFreeBuffer(lpEID); if(hr != MAPI_E_USER_CANCEL) { if (!hrDeferred) //hr could be MAPI_W_ERRORS_RETURNED in which case it wasnt all roses so dont give this message ... { if(nSelectedCount) ShowMessageBox(GetParent(hWndLV), idsSuccessfullyAddedUsers, MB_ICONINFORMATION | MB_OK); } else if(hrDeferred == MAPI_E_NOT_FOUND) ShowMessageBox(GetParent(hWndLV), idsCouldNotAddSomeEntries, MB_ICONINFORMATION | MB_OK); } SetCursor(hOldCursor); return hr; } //$$ /************************************************************************************ - ReadRegistryPositionInfo - * Purpose: * Getss the previously stored modeless window size and column width info * for persistence between sessions. * * Arguments: * LPABOOK_POSCOLSIZE lpABPosColSize * LPTSTR szPosKey - key to store it under * * Returns: * BOOL * *************************************************************************************/ BOOL ReadRegistryPositionInfo(LPIAB lpIAB, LPABOOK_POSCOLSIZE lpABPosColSize, LPTSTR szPosKey) { BOOL bRet = FALSE; HKEY hKey = NULL; HKEY hKeyRoot = (lpIAB && lpIAB->hKeyCurrentUser) ? lpIAB->hKeyCurrentUser : HKEY_CURRENT_USER; DWORD dwLenName = sizeof(ABOOK_POSCOLSIZE); DWORD dwDisposition = 0; DWORD dwType = 0; if(!lpABPosColSize) goto out; tryReadingAgain: // Open key if (ERROR_SUCCESS != RegCreateKeyEx(hKeyRoot, lpszRegSortKeyName, 0, //reserved NULL, REG_OPTION_NON_VOLATILE, KEY_READ, NULL, &hKey, &dwDisposition)) { goto out; } if(dwDisposition == REG_CREATED_NEW_KEY) goto out; // Now Read the key if (ERROR_SUCCESS != RegQueryValueEx(hKey, szPosKey, NULL, &dwType, (LPBYTE) lpABPosColSize, &dwLenName)) { DebugTrace( TEXT("RegQueryValueEx failed\n")); if(hKeyRoot != HKEY_CURRENT_USER) { // with identities .. this will fail the first time ..so recover old HKCU settings for upgrades hKeyRoot = HKEY_CURRENT_USER; if(hKey) RegCloseKey(hKey); goto tryReadingAgain; } goto out; } bRet = TRUE; out: if (hKey) RegCloseKey(hKey); return(bRet); } //$$ /************************************************************************************* - WriteRegistryPostionInfo - * Purpose: * Write the given window position to the registry * * Arguments: * LPABOOK_POSCOLSIZE lpABPosColSize * LPTSTR szPosKey - key to write it in * * Returns: * BOOL * *************************************************************************************/ BOOL WriteRegistryPositionInfo(LPIAB lpIAB, LPABOOK_POSCOLSIZE lpABPosColSize, LPTSTR szPosKey) { BOOL bRet = FALSE; HKEY hKey = NULL; HKEY hKeyRoot = (lpIAB && lpIAB->hKeyCurrentUser) ? lpIAB->hKeyCurrentUser : HKEY_CURRENT_USER; DWORD dwLenName = sizeof(ABOOK_POSCOLSIZE); DWORD dwDisposition =0; if(!lpABPosColSize) goto out; // Open key if (ERROR_SUCCESS != RegCreateKeyEx(hKeyRoot, lpszRegSortKeyName, 0, //reserved NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition)) { DebugTrace( TEXT("RegCreateKeyEx failed\n")); goto out; } // Now Write this key if (ERROR_SUCCESS != RegSetValueEx( hKey, szPosKey, 0, REG_BINARY, (LPBYTE) lpABPosColSize, dwLenName)) { DebugTrace( TEXT("RegSetValue failed\n")); goto out; } bRet = TRUE; out: if (hKey) RegCloseKey(hKey); return(bRet); } //$$//////////////////////////////////////////////////////////////////////////////// // // ProcessLVCustomDraw - Processes the NMCustomDraw message for the various list views // // Used for setting the DistLists to a bolder font // // Parameters - // // lParam - lParam of original message // hDlg - handle of dialog if the relevant window is a dialog, null otherwise // bIsDialog - flag that tells us if this is a dialog or not // //////////////////////////////////////////////////////////////////////////////////// LRESULT ProcessLVCustomDraw(HWND hDlg, LPARAM lParam, BOOL bIsDialog) { NMCUSTOMDRAW *pnmcd = (NMCUSTOMDRAW *) lParam; if(pnmcd->dwDrawStage==CDDS_PREPAINT) { if(bIsDialog) { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NOTIFYITEMDRAW|CDRF_DODEFAULT); return TRUE; } else return CDRF_NOTIFYITEMDRAW|CDRF_DODEFAULT; } else if(pnmcd->dwDrawStage==CDDS_ITEMPREPAINT) { LPRECIPIENT_INFO lpItem = (LPRECIPIENT_INFO) pnmcd->lItemlParam; if(lpItem) { if(lpItem->ulObjectType == MAPI_DISTLIST) { SelectObject(((NMLVCUSTOMDRAW *)lParam)->nmcd.hdc, GetFont(fntsSysIconBold)); if(bIsDialog) { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_NEWFONT); return TRUE; } else return CDRF_NEWFONT; } } } if(bIsDialog) { SetWindowLongPtr(hDlg, DWLP_MSGRESULT, CDRF_DODEFAULT); return TRUE; } else return CDRF_DODEFAULT; } /***** //$$///////////////////////////////////////////////////////////// // // DoLVQuickFilter - Simple quick find routine for matching edit box contents to // List view entries // // lpIAB - lpAdrBook object // hWndEdit - handle of Edit Box // hWndLV - handle of List View // lppContentsList - ContentsList // // ulFlags - AB_FUZZY_FIND_NAME | AB_FUZZY_FIND_EMAIL or Both // int minLen - we may not want to trigger a search with 1 char or 2 char or less etc // /////////////////////////////////////////////////////////////// void DoLVQuickFilter( LPADRBOOK lpAdrBook, HWND hWndEdit, HWND hWndLV, LPSORT_INFO lpSortInfo, ULONG ulFlags, int nMinLen, LPRECIPIENT_INFO * lppContentsList) { TCHAR szBuf[MAX_PATH]; HRESULT hr = hrSuccess; LPSBinary rgsbEntryIDs = NULL; ULONG cValues = 0; ULONG i =0; HCURSOR hOldCur = SetCursor(LoadCursor(NULL, IDC_WAIT)); LPIAB lpIAB = (LPIAB)lpIAB; GetWindowText(hWndEdit,szBuf,ARRAYSIZE(szBuf)); TrimSpaces(szBuf); if(lstrlen(szBuf)) { if(lstrlen(szBuf) < nMinLen) goto out; // BUGBUG : Need to pass in the correct container here... hr = HrFindFuzzyRecordMatches( lpIAB->lpPropertyStore->hPropertyStore, NULL, szBuf, ulFlags, //flags &cValues, &rgsbEntryIDs); if(HR_FAILED(hr)) goto out; SendMessage(hWndLV, WM_SETREDRAW, (WPARAM) FALSE, 0); ClearListView(hWndLV, lppContentsList); if(cValues <= 0) { goto out; } for(i=0;ilpPrev = NULL; lpItem->lpNext = *lppContentsList; if (*lppContentsList) (*lppContentsList)->lpPrev = lpItem; (*lppContentsList) = lpItem; } HrFillListView(hWndLV, *lppContentsList); SortListViewColumn( hWndLV, 0, lpSortInfo, TRUE); LVSelectItem(hWndLV, 0); SendMessage(hWndLV, WM_SETREDRAW, (WPARAM) TRUE, 0); } else { hr = HrGetWABContents( hWndLV, lpAdrBook, NULL, *lpSortInfo, lppContentsList); } out: FreeEntryIDs(((LPIAB)lpIAB)->lpPropertyStore->hPropertyStore, cValues, rgsbEntryIDs); SetCursor(hOldCur); return; } /*******/ //$$///////////////////////////////////////////////////////////// // // SetWindowPropertiesTitle - puts the objects name in front of the // TEXT(" Properties") and puts it in the title // // e.g. Viewing properties on Vikram Madan would show a window // with TEXT("Vikram Madan Properties") in the title as per // Windows guidelines. // if bProperties is false, shows TEXT("Vikram Madan Reports") /////////////////////////////////////////////////////////////// void SetWindowPropertiesTitle(HWND hWnd, LPTSTR lpszName) { LPTSTR lpszBuffer = NULL; TCHAR szBuf[MAX_UI_STR]; TCHAR szTmp[MAX_PATH], *lpszTmp; LoadString( hinstMapiX, idsWindowTitleProperties, szBuf, ARRAYSIZE(szBuf)); // Win9x bug FormatMessage cannot have more than 1023 chars CopyTruncate(szTmp, lpszName, MAX_PATH - 1); lpszTmp = szTmp; if(FormatMessage( FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER, szBuf, 0,0, //ignored (LPTSTR) &lpszBuffer, MAX_UI_STR, (va_list *)&lpszTmp)) { SetWindowText(hWnd, lpszBuffer); LocalFreeAndNull(&lpszBuffer); } return; } /**** Dont mess with the order of these arrays (especially the address components street,city,zip etc ****/ static const SizedSPropTagArray(25, ToolTipsProps)= { 25, { PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_HOME_ADDRESS_STREET, PR_HOME_ADDRESS_CITY, PR_HOME_ADDRESS_STATE_OR_PROVINCE, PR_HOME_ADDRESS_POSTAL_CODE, PR_HOME_ADDRESS_COUNTRY, PR_HOME_TELEPHONE_NUMBER, PR_HOME_FAX_NUMBER, PR_CELLULAR_TELEPHONE_NUMBER, PR_PERSONAL_HOME_PAGE, PR_TITLE, PR_DEPARTMENT_NAME, PR_OFFICE_LOCATION, PR_COMPANY_NAME, PR_BUSINESS_ADDRESS_STREET, PR_BUSINESS_ADDRESS_CITY, PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE, PR_BUSINESS_ADDRESS_POSTAL_CODE, PR_BUSINESS_ADDRESS_COUNTRY, PR_BUSINESS_TELEPHONE_NUMBER, PR_BUSINESS_FAX_NUMBER, PR_PAGER_TELEPHONE_NUMBER, PR_BUSINESS_HOME_PAGE, PR_COMMENT, } }; enum _prop { txtDisplayName=0, txtEmailAddress, txtHomeAddress, txtHomeCity, txtHomeState, txtHomeZip, txtHomeCountry, txtHomePhone, txtHomeFax, txtHomeCellular, txtHomeWeb, txtBusinessTitle, txtBusinessDept, txtBusinessOffice, txtBusinessCompany, txtBusinessAddress, txtBusinessCity, txtBusinessState, txtBusinessZip, txtBusinessCountry, txtBusinessPhone, txtBusinessFax, txtBusinessPager, txtBusinessWeb, txtNotes }; static const int idsString[] = { 0, idsContactTextEmail, idsContactTextHomeAddress, 0, 0, 0, 0, idsContactTextHomePhone, idsContactTextHomeFax, idsContactTextHomeCellular, idsContactTextPersonalWebPage, idsContactTextTitle, idsContactTextDepartment, idsContactTextOffice, idsContactTextCompany, idsContactTextBusinessAddress, 0, 0, 0, 0, idsContactTextBusinessPhone, idsContactTextBusinessFax, idsContactTextBusinessPager, idsContactTextBusinessWebPage, idsContactTextNotes, }; // // The routine that generates data for tooltips, clipboard, and printing // creates a localized version of the address for the contact. This // localized formatmessage string may contain ugly blank spaces due to // missing data hence we need to cleanup the address string // This works for US build - hopefully the localizers wont break it // void CleanAddressString(TCHAR * szAddress) { LPTSTR lpTemp = szAddress; LPTSTR lpTemp2 = NULL; // we search for these 2 substrings LPTSTR szText1 = TEXT(" \r\n"); LPTSTR szText2 = TEXT(" "); ULONG nSpaceCount = 0; // // BUGBUG: This routine is not DBCS smart! // It should use IsSpace and CharNext to parse these strings. // if(SubstringSearch(szAddress, szText2)) { //First remove continuous blanks beyond 4 while(*lpTemp) { if(*lpTemp == ' ') { nSpaceCount++; if(nSpaceCount == 5) { lpTemp2 = lpTemp+1; StrCpyN(lpTemp, lpTemp2, lstrlen(lpTemp2)+1); nSpaceCount = 0; lpTemp = lpTemp - 4; continue; } } else nSpaceCount = 0; lpTemp++; } } while(SubstringSearch(szAddress, szText1)) { lpTemp = szAddress; lpTemp2 = szText1; while (*lpTemp && *lpTemp2) { if (*lpTemp != *lpTemp2) { lpTemp -= (lpTemp2 - szText1); lpTemp2 = szText1; } else { lpTemp2++; } lpTemp++; } if(*lpTemp2 == '\0') { //match found LPTSTR lpTemp3 = lpTemp; lpTemp -= (lpTemp2-szText1); StrCpyN(lpTemp, lpTemp3, lstrlen(lpTemp3)+1); } } // also need to strip out the \r\n at the end of the address string nSpaceCount = lstrlen(szAddress); if(nSpaceCount >= 2) szAddress[nSpaceCount-2] = '\0'; return; } //$$///////////////////////////////////////////////////////////////////////////// // // void HrGetLVItemDataString - Gets the item's data for the currently selected // item in the list view and puts it in a string // // lpIAB - Pointer to AddrBook object // hWndLV - Handle of list view // nItem - item in list view whose properties we are retrieving // lpszData - returned string containing item properties - a buffer is allocated // to hold the data and the user needs to LocalFree the buffer // //////////////////////////////////////////////////////////////////////////////// HRESULT HrGetLVItemDataString(LPADRBOOK lpAdrBook, HWND hWndLV, int iItemIndex, LPTSTR * lppszData) { HRESULT hr = E_FAIL; LPRECIPIENT_INFO lpItem = NULL; LPSPropValue lpPropArray = NULL; ULONG ulcProps = 0; ULONG i =0,j=0; ULONG ulBufSize = 0; LPTSTR lpszData = NULL; LPTSTR szParanStart = TEXT(" ("); LPTSTR szParanEnd = TEXT(")"); LPTSTR szLineBreakDL = TEXT("\r\n "); LPTSTR lpszHomeAddress = NULL, lpszBusinessAddress = NULL; LPTSTR lpszEmailAddresses = NULL; LPTSTR * lpsz = NULL; BOOL bBusinessTitle = FALSE, bPersonalTitle = FALSE; ULONG * lpulPropTagArray = NULL; ULONG cchSize; // Some items will have both the PR_CONTACT_EMAIL_ADDRESSES and PR_EMAIL_ADDRESS // while others will have only PR_EMAIL_ADDRESS // In case of the former, we want to avoid duplication by ignoring email-address // when contact-email-addresses exist. For this we use a flag. BOOL bFoundContactAddresses = FALSE; ULONG ulObjectType = 0; SizedSPropTagArray(3, DLToolTipsProps)= { 3, { PR_DISPLAY_NAME, PR_WAB_DL_ENTRIES, PR_WAB_DL_ONEOFFS, } }; *lppszData = NULL; lpItem = GetItemFromLV(hWndLV, iItemIndex); if(lpItem) { hr = HrGetPropArray(lpAdrBook, NULL, lpItem->cbEntryID, lpItem->lpEntryID, MAPI_UNICODE, &ulcProps, &lpPropArray); if(HR_FAILED(hr)) goto out; // is this a MailUser or a Distribution List ulObjectType = lpItem->ulObjectType; if(ulObjectType == MAPI_DISTLIST) { LPTSTR * lppszNameCache = NULL, * lppDLName = NULL, * lppDLOneOffName = NULL; LPTSTR * lppszEmailCache = NULL, * lppDLEmail = NULL, * lppDLOneOffEmail = NULL; ULONG ulNumNames = 0, ulNames = 0, ulOneOffNames = 0; // First we count the data to get a buffer size for our buffer for(j=0;jszDisplayName; // Set the localized versions of the addresses if any for(i=txtHomeAddress;i<=txtHomeCountry;i++) { if(lpsz[i]) { TCHAR szBuf[MAX_UI_STR]; { //Bug 1115995 - TEXT("(null)")s produced by Format message for null pointers // in the va_list .. replace these with szEmpty for(j=txtHomeAddress;j<=txtHomeCountry;j++) if(!lpsz[j]) lpsz[j]=szEmpty; } LoadString(hinstMapiX, idsContactAddress, szBuf, ARRAYSIZE(szBuf)); if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, szBuf, 0, // stringid 0, // dwLanguageId (LPTSTR)&lpszHomeAddress, // output buffer 0, //MAX_UI_STR (va_list *)&lpsz[txtHomeAddress])) { for(j=txtHomeAddress;j<=txtHomeCountry;j++) lpsz[j]=NULL; CleanAddressString(lpszHomeAddress); lpsz[txtHomeAddress] = lpszHomeAddress; break; } } } for(i=txtHomeAddress;i<=txtHomeWeb;i++) { if(lpsz[i]) { TCHAR szBuf[MAX_UI_STR]; bPersonalTitle = TRUE; LoadString(hinstMapiX, idsContactTextPersonal, szBuf, ARRAYSIZE(szBuf)); ulBufSize += sizeof(TCHAR)*(lstrlen(szBuf)); break; } } for(i=txtBusinessAddress;i<=txtBusinessCountry;i++) { if(lpsz[i]) { TCHAR szBuf[MAX_UI_STR]; { //Bug 1115995 - TEXT("(null)")s produced by Format message for null pointers // in the va_list .. replace these with szEmpty for(j=txtBusinessAddress;j<=txtBusinessCountry;j++) if(!lpsz[j]) lpsz[j]=szEmpty; } LoadString(hinstMapiX, idsContactAddress, szBuf, ARRAYSIZE(szBuf)); if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY, szBuf, 0, // stringid 0, // dwLanguageId (LPTSTR)&lpszBusinessAddress, // output buffer 0, //MAX_UI_STR (va_list *)&lpsz[txtBusinessAddress])) { for(j=txtBusinessAddress;j<=txtBusinessCountry;j++) lpsz[j]=NULL; CleanAddressString(lpszBusinessAddress); lpsz[txtBusinessAddress] = lpszBusinessAddress; break; } } } for(i=txtBusinessAddress;i<=txtBusinessWeb;i++) { if(lpsz[i]) { TCHAR szBuf[MAX_UI_STR]; bBusinessTitle = TRUE; LoadString(hinstMapiX, idsContactTextBusiness, szBuf, ARRAYSIZE(szBuf)); ulBufSize += sizeof(TCHAR)*(lstrlen(szBuf)); break; } } for(i=0;i= (2 * sizeof(WORD))) { // set up buffer for calls to VerQueryValue() CHAR *rgszVer[] = { "FileVersion", "LegalCopyright" }; int rgId[] = { IDC_ABOUT_LABEL_VERSION, IDC_ABOUT_COPYRIGHT }; DWORD cch; wnsprintfA(szBuf, ARRAYSIZE(szBuf), "\\StringFileInfo\\%04X%04X\\", lpwTrans[0], lpwTrans[1]); lpszT = szBuf + lstrlenA(szBuf); cch = ARRAYSIZE(szBuf) - lstrlenA(szBuf); // Walk through the dialog items that we want to replace: for (i = 0; i <= 1; i++) { StrCpyNA(lpszT, rgszVer[i], cch); if (VerQueryValueA(lpInfo, szBuf, (LPVOID *)&lpVersion, &uLen) && uLen) { LPTSTR lp = ConvertAtoW((LPSTR) lpVersion); SetDlgItemText(hDlg, rgId[i], lp); LocalFreeAndNull(&lp); } } } } LocalFree(lpInfo); } } else DebugPrintTrace(( TEXT("GetFileVersionSize failed: %d\n"),GetLastError())); { LPPTGDATA lpPTGData=GetThreadStoragePointer(); if(pt_lpIAB && !pt_bIsWABOpenExSession) { // hack lpDataFile = GetWABFileName(((LPIAB)pt_lpIAB)->lpPropertyStore->hPropertyStore, FALSE); } if(lpDataFile && lstrlen(lpDataFile)) SetDlgItemText(hDlg, IDC_ABOUT_EDIT_FILENAME, lpDataFile); else { ShowWindow(GetDlgItem(hDlg, IDC_ABOUT_EDIT_FILENAME), SW_HIDE); ShowWindow(GetDlgItem(hDlg, IDC_ABOUT_STATIC_FILENAME), SW_HIDE); } } } break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: case IDOK: EndDialog(hDlg, 0); break; } break; default: return FALSE; break; } return TRUE; } //$$//////////////////////////////////////////////////////////////////// // // nTruncatePos // // With DBCS strings we want to truncate the string at the beginning of // a TCHAR and not in the middle of the double TCHAR. // Hence we take a string, take in the maximum length we want, scan the // string and return the length of the string at which we can safely // truncate // // PARAMETERS: // lpsz - input string // nMaxLen - maximum allowed length of the string // //////////////////////////////////////////////////////////////////////// ULONG TruncatePos(LPTSTR lpsz, ULONG nMaxLen) { ULONG nLen = 0; ULONG nDesiredLen = 0; if(!lpsz || !lstrlen(lpsz) || !nMaxLen) goto out; nLen = lstrlen(lpsz); if (nLen >= nMaxLen) { ULONG nCharsSteppedOverCount = 0; ULONG nLastCharCount = 0; ULONG nTotalLen = nLen; //lstrlen(lpsz); nDesiredLen = nMaxLen; while(*lpsz) { nLastCharCount = nCharsSteppedOverCount; lpsz = CharNext(lpsz); nCharsSteppedOverCount = nTotalLen - lstrlen(lpsz); // + 1; if(nCharsSteppedOverCount > nDesiredLen) break; } if (nCharsSteppedOverCount < nDesiredLen) nLen = nCharsSteppedOverCount; else nLen = nLastCharCount; } out: return nLen; } //$$//////////////////////////////////////////////////////////////////// // // FreeRecipList - frees allocated memory in a RecipientInfo List // // // PARAMETERS: // lppList - list to free // //////////////////////////////////////////////////////////////////////// void FreeRecipList(LPRECIPIENT_INFO * lppList) { if(lppList) { LPRECIPIENT_INFO lpItem = NULL; lpItem = *lppList; while(lpItem) { *lppList = lpItem->lpNext; FreeRecipItem(&lpItem); lpItem = *lppList; } *lppList = NULL; } return; } //$$//////////////////////////////////////////////////////////////////// // // HrCreateNewObject - Creates a new object in the wab // // // PARAMETERS: // lpIAB - lpAdrbook // &lpMailUser - MailUser to return // //////////////////////////////////////////////////////////////////////// HRESULT HrCreateNewObject(LPADRBOOK lpAdrBook, LPSBinary lpsbContainer, ULONG ulObjectType, ULONG ulCreateFlags, LPMAPIPROP * lppPropObj) { HRESULT hResult = hrSuccess; LPENTRYID lpWABEID = NULL; ULONG cbWABEID = 0; ULONG ulObjType = 0; ULONG cProps = 0; LPABCONT lpContainer = NULL; LPSPropValue lpCreateEIDs = NULL; LPMAPIPROP lpPropObj = NULL; LPPTGDATA lpPTGData=GetThreadStoragePointer(); LPIAB lpIAB = (LPIAB) lpAdrBook; if(!lpsbContainer || !lpsbContainer->cb || !lpsbContainer->lpb) { SetVirtualPABEID(lpIAB, &cbWABEID, &lpWABEID); if (hResult = lpAdrBook->lpVtbl->GetPAB(lpAdrBook, &cbWABEID, &lpWABEID)) goto exit; } else { cbWABEID = lpsbContainer->cb; lpWABEID = (LPENTRYID) lpsbContainer->lpb; } if (hResult = lpAdrBook->lpVtbl->OpenEntry(lpAdrBook, cbWABEID, // size of EntryID to open lpWABEID, // EntryID to open NULL, // interface 0, // flags &ulObjType, (LPUNKNOWN *)&lpContainer)) { goto exit; } // Get us the creation entryids if (hResult = lpContainer->lpVtbl->GetProps(lpContainer, (LPSPropTagArray)&ptaCreate, MAPI_UNICODE, &cProps, &lpCreateEIDs)) { DebugTrace( TEXT("Can't get container properties for PAB\n")); // Bad stuff here! goto exit; } if (hResult = lpContainer->lpVtbl->CreateEntry(lpContainer, (ulObjectType == MAPI_MAILUSER ? lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].Value.bin.cb : lpCreateEIDs[icrPR_DEF_CREATE_DL].Value.bin.cb), (ulObjectType == MAPI_MAILUSER ? (LPENTRYID)lpCreateEIDs[icrPR_DEF_CREATE_MAILUSER].Value.bin.lpb : (LPENTRYID)lpCreateEIDs[icrPR_DEF_CREATE_DL].Value.bin.lpb), ulCreateFlags, &lpPropObj)) { DebugTraceResult( TEXT("CreateMailUser:CreateEntry"), hResult); goto exit; } *lppPropObj = lpPropObj; exit: if(HR_FAILED(hResult) && lpPropObj) lpPropObj->lpVtbl->Release(lpPropObj); if(lpWABEID && (!lpsbContainer || lpsbContainer->lpb != (LPBYTE) lpWABEID)) FreeBufferAndNull(&lpWABEID); UlRelease(lpContainer); FreeBufferAndNull(&lpCreateEIDs); return hResult; } const LPTSTR szDefMailKey = TEXT("Software\\Clients\\Mail"); const LPTSTR szOEDllPathKey = TEXT("DllPath"); const LPTSTR szOEName = TEXT("Outlook Express"); //$$/////////////////////////////////////////////////////////////////////// // // CheckForOutlookExpress // // szDllPath - is a big enough buffer that will contain the path for // the OE dll .. // ////////////////////////////////////////////////////////////////////////// BOOL CheckForOutlookExpress(LPTSTR szDllPath, DWORD cchDllPath) { HKEY hKeyMail = NULL; HKEY hKeyOE = NULL; DWORD dwErr = 0; DWORD dwSize = 0; TCHAR szBuf[MAX_PATH]; TCHAR szPathExpand[MAX_PATH]; DWORD dwType = 0; BOOL bRet = FALSE; szDllPath[0] = TEXT('\0'); szPathExpand[0] = TEXT('\0'); // Open the key for default internet mail client // HKLM\Software\Clients\Mail dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szDefMailKey, 0, KEY_READ, &hKeyMail); if(dwErr != ERROR_SUCCESS) { DebugTrace( TEXT("RegopenKey %s Failed -> %u\n"), szDefMailKey, dwErr); goto out; } dwSize = ARRAYSIZE(szBuf); // Expect ERROR_MORE_DATA dwErr = RegQueryValueEx( hKeyMail, NULL, NULL, &dwType, (LPBYTE)szBuf, &dwSize); if(dwErr != ERROR_SUCCESS) { goto out; } if(!lstrcmpi(szBuf, szOEName)) { // Yes its outlook express .. bRet = TRUE; } //Get the DLL Path anyway whether this is the default key or not // Get the DLL Path dwErr = RegOpenKeyEx(hKeyMail, szOEName, 0, KEY_READ, &hKeyOE); if(dwErr != ERROR_SUCCESS) { DebugTrace( TEXT("RegopenKey %s Failed -> %u\n"), szDefMailKey, dwErr); goto out; } dwSize = ARRAYSIZE(szBuf); szBuf[0]=TEXT('\0'); dwErr = RegQueryValueEx(hKeyOE, szOEDllPathKey, NULL, &dwType, (LPBYTE)szBuf, &dwSize); if (REG_EXPAND_SZ == dwType) { ExpandEnvironmentStrings(szBuf, szPathExpand, ARRAYSIZE(szPathExpand)); StrCpyN(szBuf, szPathExpand,ARRAYSIZE(szBuf)); } if(dwErr != ERROR_SUCCESS) { goto out; } if(lstrlen(szBuf)) StrCpyN(szDllPath, szBuf, cchDllPath); out: if(hKeyOE) RegCloseKey(hKeyOE); if(hKeyMail) RegCloseKey(hKeyMail); return bRet; } static const SizedSPropTagArray(1, ptaMailToExItemType)= { 1, { PR_OBJECT_TYPE, } }; // We will create a linked list of all selected entries that have an // email address and then use that to create the recip list for sendmail typedef struct _RecipList { LPTSTR lpszName; LPTSTR lpszEmail; LPSBinary lpSB; struct _RecipList * lpNext; } RECIPLIST, * LPRECIPLIST; //$$///////////////////////////////////////////////////////////////////// // // FreeLPRecipList // // Frees a linked list containing the above structures // ///////////////////////////////////////////////////////////////////////// void FreeLPRecipList(LPRECIPLIST lpList) { if(lpList) { LPRECIPLIST lpTemp = lpList; while(lpTemp) { lpList = lpTemp->lpNext; if(lpTemp->lpszName) LocalFree(lpTemp->lpszName); if(lpTemp->lpszEmail) LocalFree(lpTemp->lpszEmail); if(lpTemp->lpSB) MAPIFreeBuffer(lpTemp->lpSB); LocalFree(lpTemp); lpTemp = lpList; } } } //$$///////////////////////////////////////////////////////////////////// // // GetItemNameEmail // // Gets the name and email address of the specified item // and appends it to the provided linked list .. // ///////////////////////////////////////////////////////////////////////// HRESULT HrGetItemNameEmail( LPADRBOOK lpAdrBook, BOOL bIsOE, ULONG cbEntryID, LPENTRYID lpEntryID, int nExtEmail, LPRECIPLIST * lppList) { HRESULT hr = E_FAIL; ULONG cValues; LPRECIPLIST lpTemp = NULL; LPSPropValue lpspv = NULL; LPRECIPLIST lpList = *lppList; LPTSTR lpEmail = NULL, lpAddrType = NULL, lpName = NULL; SizedSPropTagArray(5, ptaMailToEx)= { 5, { PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_CONTACT_EMAIL_ADDRESSES, PR_CONTACT_ADDRTYPES } }; // Open the entry and read the email address. // NOTE: We can't just take the address out of the listbox // because it may be truncated! if (HR_FAILED(hr = HrGetPropArray( lpAdrBook, (LPSPropTagArray)&ptaMailToEx, cbEntryID, lpEntryID, MAPI_UNICODE, &cValues, &lpspv))) { goto out; } lpName = (lpspv[0].ulPropTag == PR_DISPLAY_NAME) ? lpspv[0].Value.LPSZ : szEmpty; if( nExtEmail && lpspv[3].ulPropTag == PR_CONTACT_EMAIL_ADDRESSES && lpspv[4].ulPropTag == PR_CONTACT_ADDRTYPES && lpspv[3].Value.MVSZ.cValues >= (ULONG)nExtEmail) { lpEmail = lpspv[3].Value.MVSZ.LPPSZ[nExtEmail-1]; lpAddrType = lpspv[4].Value.MVSZ.LPPSZ[nExtEmail-1]; } if(!lpEmail) lpEmail = (lpspv[1].ulPropTag == PR_EMAIL_ADDRESS) ? lpspv[1].Value.LPSZ : szEmpty; if(!lpAddrType) lpAddrType = (lpspv[2].ulPropTag == PR_ADDRTYPE) ? lpspv[2].Value.LPSZ : szEmpty; if(lstrlen(lpEmail) && lstrlen(lpName)) //only if this item has a email address do we include it { lpTemp = LocalAlloc(LMEM_ZEROINIT, sizeof(RECIPLIST)); if(lpTemp) { DWORD cchEmail; DWORD cchName=lstrlen(lpName) + 1; lpTemp->lpszName = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchName); if (lpTemp->lpszName) { StrCpyN(lpTemp->lpszName, lpName, cchName); } cchEmail=(lstrlen(lpEmail) + lstrlen(lpAddrType) + 2); lpTemp->lpszEmail = LocalAlloc(LMEM_ZEROINIT, sizeof(TCHAR)*cchEmail); if (lpTemp->lpszEmail) { if(bIsOE) { lpTemp->lpszEmail[0]=TEXT('\0'); } else { StrCpyN(lpTemp->lpszEmail, lpAddrType, cchEmail); StrCatBuff(lpTemp->lpszEmail, szColon, cchEmail); } StrCatBuff(lpTemp->lpszEmail, lpEmail, cchEmail); } MAPIAllocateBuffer(sizeof(SBinary), (LPVOID) &(lpTemp->lpSB)); // Create a one off entry id for this buffer CreateWABEntryID(WAB_ONEOFF, lpTemp->lpszName, lpAddrType, lpEmail, 0, 0, (LPVOID) lpTemp->lpSB, (LPULONG) (&(lpTemp->lpSB->cb)), (LPENTRYID *) &(lpTemp->lpSB->lpb)); lpTemp->lpNext = lpList; lpList = lpTemp; } } FreeBufferAndNull(&lpspv); *lppList = lpList; hr = S_OK; out: return hr; } //$$////////////////////////////////////////////////////////////////// // // Function that opens an item and adds it to the recip list // If the opened item is a group, calls itself recursively for all // subgroups .. // // lpnRecipCount - returns the number of items in lppList // lppList - dynamically allocated - must be freed by caller // bIsOE - tells us to follow a slightly different code-path to handle OE // inconsistencies - ** warning ** - this will break when they // fix their inconsistencies // nExtEmail - this is non-zero when there is a single selection and the // user chose a non-default email address which should be used for // sending mail // ////////////////////////////////////////////////////////////////////// HRESULT GetRecipListFromSelection(LPADRBOOK lpAdrBook, BOOL bIsOE, ULONG cbEntryID, LPENTRYID lpEntryID, int nExtEmail, ULONG * lpnRecipCount, ULONG * lpnNoEmailCount, LPRECIPLIST * lppList) { ULONG ulObjectType = 0; HRESULT hr = E_FAIL; { ULONG cValues = 0; LPSPropValue lpspv = NULL; // First check if this item is a mailuser or a group if (HR_FAILED(hr = HrGetPropArray( lpAdrBook, (LPSPropTagArray)&ptaMailToExItemType, cbEntryID, lpEntryID, MAPI_UNICODE, &cValues, &lpspv))) { return hr; } ulObjectType = lpspv[0].Value.l; FreeBufferAndNull(&lpspv); } if(ulObjectType == MAPI_MAILUSER) { LPRECIPLIST lpTemp = *lppList; if (!HR_FAILED(hr = HrGetItemNameEmail(lpAdrBook, bIsOE, cbEntryID,lpEntryID, nExtEmail, lppList))) { if(lpTemp != *lppList) // means an item was added to the list .. (*lpnRecipCount)++; else (*lpnNoEmailCount)++; } } else if(ulObjectType == MAPI_DISTLIST) { ULONG cValues = 0; LPSPropValue lpspv = NULL; SizedSPropTagArray(2, tagaDLEntriesOneOffs) = { 2, { PR_WAB_DL_ENTRIES, PR_WAB_DL_ONEOFFS, } }; if (HR_FAILED(hr = HrGetPropArray( lpAdrBook, (LPSPropTagArray)&tagaDLEntriesOneOffs, cbEntryID, lpEntryID, MAPI_UNICODE, &cValues, &lpspv))) { return hr; } { ULONG i,j; for(i=0;i<2;i++) { if(lpspv[i].ulPropTag == PR_WAB_DL_ENTRIES || lpspv[i].ulPropTag == PR_WAB_DL_ONEOFFS) { // Look at each entry in the PR_WAB_DL_ENTRIES and PR_WAB_DL_ONEOFFS for (j = 0; j < lpspv[i].Value.MVbin.cValues; j++) { ULONG cbEID = lpspv[i].Value.MVbin.lpbin[j].cb; LPENTRYID lpEID = (LPENTRYID)lpspv[i].Value.MVbin.lpbin[j].lpb; GetRecipListFromSelection(lpAdrBook, bIsOE, cbEID, lpEID, 0, lpnRecipCount, lpnNoEmailCount, lppList); } } } } FreeBufferAndNull(&lpspv); } return hr; } //$$////////////////////////////////////////////////////////////////////// // // HrSendMail - does the actual mail sending // Our first priority is to Outlook Express which currently has a // different code path than the regular MAPI client .. so we look // under HKLM\Software\Clients\Mail .. if the client is OE then // we just loadlibrary and getprocaddress for sendmail // If its not OE, then we call the mapi32.dll and load it .. // If both fail we will not be able to send mail ... // // This function will free the lpList no matter what happens // so caller should not expect to reuse it (This is so we can // give the pointer to a seperate thread and not worry about it) // ////////////////////////////////////////////////////////////////////////// HRESULT HrSendMail(HWND hWndParent, ULONG nRecipCount, LPRECIPLIST lpList, LPIAB lpIAB, BOOL bUseOEForSendMail) { HRESULT hr = E_FAIL; HINSTANCE hLibMapi = NULL; BOOL bIsOE = FALSE; // right now there is a different code path // for OE vs other MAPI clients TCHAR szBuf[MAX_PATH]; LPMAPISENDMAIL lpfnMAPISendMail = NULL; LHANDLE hMapiSession = 0; LPMAPILOGON lpfnMAPILogon = NULL; LPMAPILOGOFF lpfnMAPILogoff = NULL; LPBYTE lpbName, lpbAddrType, lpbEmail; ULONG ulMapiDataType; ULONG cbEntryID = 0; LPENTRYID lpEntryID = NULL; MapiMessage Msg = {0}; MapiRecipDesc * lprecips = NULL; if(!nRecipCount) { hr = MAPI_W_ERRORS_RETURNED; goto out; } // Check if OutlookExpress is the default current client .. bIsOE = CheckForOutlookExpress(szBuf, ARRAYSIZE(szBuf)); // Turn off all notifications for simple MAPI send mail, if the default // email client is Outlook. This is necessary because Outlook changes the // WAB MAPI allocation functions during simple MAPI and we don't want any // internal WAB functions using these allocators. if (!bIsOE && !bUseOEForSendMail) vTurnOffAllNotifications(); // if OE is the default client or OE launched this WAB, use OE for SendMail if(lstrlen(szBuf) && (bIsOE||bUseOEForSendMail)) { hLibMapi = LoadLibrary(szBuf); } else { // Check if simple mapi is installed if(GetProfileInt( TEXT("mail"), TEXT("mapi"), 0) == 1) hLibMapi = LoadLibrary( TEXT("mapi32.dll")); if(!hLibMapi) // try loading the OE MAPI dll directly { // Load the path to the msimnui.dll CheckForOutlookExpress(szBuf, ARRAYSIZE(szBuf)); if(lstrlen(szBuf)) // Load the dll directly - dont bother going through msoemapi.dll hLibMapi = LoadLibrary(szBuf); } } if(!hLibMapi) { DebugPrintError(( TEXT("Could not load/find simple mapi\n"))); hr = MAPI_E_NOT_FOUND; goto out; } else if(hLibMapi) { lpfnMAPILogon = (LPMAPILOGON) GetProcAddress (hLibMapi, "MAPILogon"); lpfnMAPILogoff= (LPMAPILOGOFF)GetProcAddress (hLibMapi, "MAPILogoff"); lpfnMAPISendMail = (LPMAPISENDMAIL) GetProcAddress (hLibMapi, "MAPISendMail"); if(!lpfnMAPISendMail || !lpfnMAPILogon || !lpfnMAPILogoff) { DebugPrintError(( TEXT("MAPI proc not found\n"))); hr = MAPI_E_NOT_FOUND; goto out; } hr = lpfnMAPILogon( (ULONG_PTR)hWndParent, NULL, NULL, // No password needed. 0L, // Use shared session. 0L, // Reserved; must be 0. &hMapiSession); // Session handle. if(hr != SUCCESS_SUCCESS) { DebugTrace( TEXT("MAPILogon failed\n")); // its possible the logon failed since there was no shared logon session // Try again to create a new session with UI hr = lpfnMAPILogon( (ULONG_PTR)hWndParent, NULL, NULL, // No password needed. MAPI_LOGON_UI | MAPI_NEW_SESSION, // Use shared session. 0L, // Reserved; must be 0. &hMapiSession); // Session handle. if(hr != SUCCESS_SUCCESS) { DebugTrace( TEXT("MAPILogon failed\n")); goto out; } } } // Load the MAPI functions here ... // lprecips = LocalAlloc(LMEM_ZEROINIT, sizeof(MapiRecipDesc) * nRecipCount); { LPRECIPLIST lpTemp = lpList; ULONG count = 0; while(lpTemp) { lprecips[count].ulRecipClass = MAPI_TO; lprecips[count].lpszName = ConvertWtoA(lpTemp->lpszName); lprecips[count].lpszAddress = ConvertWtoA(lpTemp->lpszEmail); // [PaulHi] 4/20/99 Raid 73455 // Convert Unicode EID OneOff strings to ANSI if ( IsWABEntryID(lpTemp->lpSB->cb, (LPVOID)lpTemp->lpSB->lpb, &lpbName, &lpbAddrType, &lpbEmail, (LPVOID *)&ulMapiDataType, NULL) == WAB_ONEOFF ) { #ifndef _WIN64 // As I founf from RAID this part only for Outlook if (ulMapiDataType & MAPI_UNICODE) { hr = CreateWABEntryIDEx( FALSE, // Don't want Unicode EID strings WAB_ONEOFF, // EID type (LPWSTR)lpbName, (LPWSTR)lpbAddrType, (LPWSTR)lpbEmail, 0, 0, NULL, &cbEntryID, &lpEntryID); if (FAILED(hr)) goto out; lprecips[count].ulEIDSize = cbEntryID; lprecips[count].lpEntryID = lpEntryID; } else #endif // _WIN64 { lprecips[count].ulEIDSize = lpTemp->lpSB->cb; lprecips[count].lpEntryID = (LPVOID)lpTemp->lpSB->lpb; } } lpTemp = lpTemp->lpNext; count++; } } Msg.nRecipCount = nRecipCount; Msg.lpRecips = lprecips; hr = lpfnMAPISendMail (hMapiSession, (ULONG_PTR)hWndParent, &Msg, // the message being sent MAPI_DIALOG, // allow the user to edit the message 0L); // reserved; must be 0 if(hr != SUCCESS_SUCCESS) goto out; hr = S_OK; out: // This must be freed within the Outlook simple MAPI session, since it was // allocated within this session (i.e., with Outlook allocators). if (lpEntryID) MAPIFreeBuffer(lpEntryID); // The simple MAPI session should end after this if(hMapiSession && lpfnMAPILogoff) lpfnMAPILogoff(hMapiSession,0L,0L,0L); if(hLibMapi) FreeLibrary(hLibMapi); // Turn all notifications back on and refresh the WAB UI (just in case) if (!bIsOE && !bUseOEForSendMail) { vTurnOnAllNotifications(); if (lpIAB->hWndBrowse) PostMessage(lpIAB->hWndBrowse, WM_COMMAND, (WPARAM) IDM_VIEW_REFRESH, 0); } if(lprecips) { ULONG i = 0; for(i=0;ihWnd, lpMP->nRecipCount, lpMP->lpList, lpMP->lpIAB, lpMP->bUseOEForSendMail); LocalFree(lpMP); return 0; } //$$////////////////////////////////////////////////////////////////////// // // HrStartMailThread // // Starts a seperate thread to send mapi based mail from // ////////////////////////////////////////////////////////////////////////// HRESULT HrStartMailThread(HWND hWndParent, ULONG nRecipCount, LPRECIPLIST lpList, LPIAB lpIAB, BOOL bUseOEForSendMail) { LPMAIL_PARAMS lpMP = NULL; HRESULT hr = E_FAIL; lpMP = LocalAlloc(LMEM_ZEROINIT, sizeof(MAIL_PARAMS)); if(!lpMP) goto out; { HANDLE hThread = NULL; DWORD dwThreadID = 0; lpMP->hWnd = hWndParent; lpMP->nRecipCount = nRecipCount; lpMP->lpList = lpList; lpMP->bUseOEForSendMail = bUseOEForSendMail; lpMP->lpIAB = lpIAB; hThread = CreateThread( NULL, // no security attributes 0, // use default stack size MailThreadProc, // thread function (LPVOID) lpMP, // argument to thread function 0, // use default creation flags &dwThreadID); // returns the thread identifier if(hThread == NULL) goto out; hr = S_OK; CloseHandle(hThread); } out: if(HR_FAILED(hr)) { ShowMessageBox(hWndParent, idsSendMailError, MB_OK | MB_ICONEXCLAMATION); // we can assume that HrSendMail never got called so we should free lpList & lpMP if(lpMP) LocalFree(lpMP); if(lpList) FreeLPRecipList(lpList); } return hr; } //$$////////////////////////////////////////////////////////////////////// // // HrSendMailToSelectedContacts // // Uses simple MAPI to send mail to the selected contacts // // hWndLV - handle of List view. We look up the all the selected items in // this list view, get their lParam structure, then get its // EntryID and get the email address .. in the case of a group // we get all the email addresses of all the members // All these are put into a recip list and given to // MAPISendMail ... // // lpIAB - handle to current AdrBook object - used for calling details // nExtEmail - if this is a non-zero positive number, then it is the index of an // e-mail address in the PR_CONTACT_EMAIL_ADDRESSES property and means that // the user specified a non-default e-mail address to send mail to in which case // that particular email address should be used for sending mail. nExtEmail will be // non-zero only if one item is selected and a specific email is chosen for that item. // // Returns:S_OK // E_FAIL // ////////////////////////////////////////////////////////////////////////// HRESULT HrSendMailToSelectedContacts(HWND hWndLV, LPADRBOOK lpAdrBook, int nExtEmail) { HRESULT hr = E_FAIL; int nSelected = ListView_GetSelectedCount(hWndLV); int iItemIndex = 0; HWND hWndParent = GetParent(hWndLV); TCHAR szBuf[MAX_PATH]; LPIAB lpIAB = (LPIAB) lpAdrBook; LPRECIPLIST lpList = NULL; ULONG nRecipCount = 0, nNoEmailCount = 0; HCURSOR hOldCur = SetCursor(LoadCursor(NULL, IDC_WAIT)); // Check if OutlookExpress is the current client ..need to know this to workaround a bug // in what they expect right now as recipients BOOL bIsOE = CheckForOutlookExpress(szBuf, ARRAYSIZE(szBuf)); // Create a recipients list to put in the new message ... if(nSelected > 0) { // Get index of selected item iItemIndex = ListView_GetNextItem(hWndLV,-1,LVNI_SELECTED); while (iItemIndex != -1) { LPRECIPIENT_INFO lpItem = GetItemFromLV(hWndLV, iItemIndex);; // Get item lParam LPRECIPIENT_INFO structure if (lpItem) { GetRecipListFromSelection(lpAdrBook, bIsOE, lpItem->cbEntryID, lpItem->lpEntryID, nExtEmail, &nRecipCount, &nNoEmailCount, &lpList); } iItemIndex = ListView_GetNextItem(hWndLV,iItemIndex,LVNI_SELECTED); } if(nRecipCount > 0 && nNoEmailCount > 0) { if(IDNO == ShowMessageBox(hWndParent, idsSomeHaveNoEmail, MB_ICONEXCLAMATION | MB_YESNO)) { hr = MAPI_E_USER_CANCEL; goto out; } } } else { // nothing selected ShowMessageBox(hWndParent, IDS_ADDRBK_MESSAGE_NO_ITEM, MB_ICONEXCLAMATION); goto out; } hr = HrStartMailThread( hWndParent, nRecipCount, lpList, // HrSendMail frees lpList so dont reuse lpIAB, lpIAB->bUseOEForSendMail); out: SetCursor(hOldCur); return hr; } /* const LPTSTR szClients = TEXT( TEXT("Software\\Clients\\%s")); // // FUNCTION: ShellUtil_RunIndirectRegCommand() // // PURPOSE: find the default value under HKLM\Software\Clients\pszClient // tack on shell\open\command // then runreg that // void ShellUtil_RunClientRegCommand(HWND hwnd, LPCTSTR pszClient) { TCHAR szDefApp[MAX_PATH]; TCHAR szKey[MAX_PATH]; LONG cbSize = ARRAYSIZE(szDefApp); wnsprintf(szKey, ARRAYSIZE(szKey), szClients, pszClient); if (RegQueryValue(HKEY_LOCAL_MACHINE, szKey, szDefApp, &cbSize) == ERROR_SUCCESS) { TCHAR szFullKey[MAX_PATH]; // tack on shell\open\command wnsprintf(szFullKey, ARRAYSIZE(szFullKey), TEXT("%s\\%s\\shell\\open\\command"), szKey, szDefApp); cbSize = ARRAYSIZE(szDefApp); if (RegQueryValue(HKEY_LOCAL_MACHINE, szFullKey, szDefApp, &cbSize) == ERROR_SUCCESS) { LPSTR pszArgs = NULL; SHELLEXECUTEINFO ExecInfo; LPTSTR lp = szDefApp; // if we have long file names in this string, we need to skip past the qoutes if(lp) { if(*lp == '"') { lp = CharNext(lp); while(lp && *lp && *lp!='"') lp = CharNext(lp); } // Now find the next blank space because this is where the parameters start .. while(lp && *lp && *lp!=' ') // No DBCS spaces here lp = CharNext(lp); if(*lp == ' ') { pszArgs = CharNext(lp); *lp = '\0'; TrimSpaces(pszArgs); } //Now remove the quotes from lp lp = szDefApp; while(lp && *lp) { if(*lp == '"') *lp = ' '; lp = CharNext(lp); } TrimSpaces(szDefApp); } ExecInfo.hwnd = hwnd; ExecInfo.lpVerb = NULL; ExecInfo.lpFile = szDefApp; ExecInfo.lpParameters = pszArgs; ExecInfo.lpDirectory = NULL; ExecInfo.nShow = SW_SHOWNORMAL; ExecInfo.fMask = 0; ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); ShellExecuteEx(&ExecInfo); } } } */ /* //$$////////////////////////////////////////////////////////////////////// // // HrSendMailToSingleContact // // Uses simple MAPI to send mail to the specified contact // // Returns:S_OK // E_FAIL // ////////////////////////////////////////////////////////////////////////// HRESULT HrSendMailToSingleContact(HWND hWnd, LPIAB lpIAB, ULONG cbEntryID, LPENTRYID lpEntryID) { HRESULT hr = E_FAIL; TCHAR szBuf[MAX_PATH]; LPRECIPLIST lpList = NULL; ULONG nRecipCount = 0; HCURSOR hOldCur = SetCursor(LoadCursor(NULL, IDC_WAIT)); // Check if OutlookExpress is the current client ..need to know this to workaround a bug // in what they expect right now as recipients BOOL bIsOE = CheckForOutlookExpress(szBuf, ARRAYSIZE(szBuf)); // Create a recipients list to put in the new message ... GetRecipListFromSelection((LPADRBOOK) lpIAB, bIsOE, cbEntryID, lpEntryID, 0, &nRecipCount, &lpList); //hr = HrSendMail(hWnd, nRecipCount, lpList); // HrSendMail frees the lpList so dont reuse .. hr = HrStartMailThread(hWnd, nRecipCount, lpList); // HrSendMail frees lpList so dont reuse SetCursor(hOldCur); return hr; } */ //$$/////////////////////////////////////////////////////////////////// // // Removes all characters from input string that are not allowed by the // file system // /////////////////////////////////////////////////////////////////////// void TrimIllegalFileChars(LPTSTR sz) { LPTSTR lpCurrent = sz; if(!lpCurrent) return; // Escape illegal chars in the file name while (*lpCurrent) { switch (*lpCurrent) { case '\\': case '/': case '<': case '>': case ':': case '"': case '|': case '?': case '*': //case '.': *lpCurrent = '_'; // replace with underscore break; default: break; } lpCurrent = CharNext(lpCurrent); } return; } /*************************************************************************** Name : IsSpace Purpose : Does the single or DBCS character represent a space? Parameters: lpChar -> SBCS or DBCS character Returns : TRUE if this character is a space Comment : ***************************************************************************/ BOOL __fastcall IsSpace(LPTSTR lpChar) { Assert(lpChar); if (*lpChar) { /* * [PaulHi] 3/31/99 Raid 73845. DBCS is not valid for UNICODE app. if (IsDBCSLeadByte((BYTE)*lpChar)) { WORD CharType[2] = {0}; GetStringTypeW(CT_CTYPE1,lpChar,2,// Double-Byte CharType); return(CharType[0] & C1_SPACE); } */ return(*lpChar == ' '); } return(FALSE); // end of string } /*************************************************************************** Name : SetRegistryUseOutlook Purpose : Sets the registry flag that makes us use Outlook Parameters: bUseOutlook or not Returns : TRUE if it was correctly changed Comment : ***************************************************************************/ BOOL SetRegistryUseOutlook(BOOL bUseOutlook) { HKEY hKey = NULL; DWORD dwUseOutlook = (DWORD) bUseOutlook; BOOL bRet = FALSE; // We'll probably never have to create the key since Outlook will do that at setup // if(ERROR_SUCCESS == RegCreateKeyEx( HKEY_CURRENT_USER, lpNewWABRegKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) { if(ERROR_SUCCESS == RegSetValueEx( hKey, lpRegUseOutlookVal, 0, REG_DWORD, (LPBYTE) &dwUseOutlook, sizeof(DWORD) )) { bRet = TRUE; } } if(hKey) RegCloseKey(hKey); return bRet; } const LPTSTR lpRegOffice = TEXT("Software\\Microsoft\\Office\\8.0"); const LPTSTR lpRegOffice9 = TEXT("Software\\Microsoft\\Office\\9.0"); const LPTSTR lpRegOutlWAB = TEXT("Software\\Microsoft\\WAB\\OutlWABDLLPath"); const LPTSTR lpRegOfficeBin = TEXT("BinDirPath"); const LPTSTR lpOUTLWAB_DLL_NAME = TEXT("Outlwab.dll"); BOOL bFindOutlWABDll(LPTSTR sz, DWORD cchSz, LPTSTR szDLLPath, DWORD cchDLLPath, BOOL bAppendName) { BOOL bRet = FALSE; if(bAppendName) { if(*(sz+lstrlen(sz)-1) != '\\') StrCatBuff(sz, szBackSlash, cchSz); StrCatBuff(sz, lpOUTLWAB_DLL_NAME, cchSz); } if(GetFileAttributes(sz) != 0xFFFFFFFF) { if(szDLLPath) StrCpyN(szDLLPath, sz, cchDLLPath); bRet = TRUE; } return bRet; } //$$///////////////////////////////////////////////////////////////////////////// // // bCheckForOutlookWABDll // // Search for the Outlook WAB DLL .. if found, we // can use that as na indicator that outlook is installed // // szDLLPath should be a big enough buffer // ////////////////////////////////////////////////////////////////////////////////// BOOL bCheckForOutlookWABDll(LPTSTR szDLLPath, DWORD cchDLLPath) { // Check in the Office Bin directory TCHAR sz[MAX_PATH]; BOOL bRet = FALSE; DWORD dwType = REG_SZ; DWORD dwSize = ARRAYSIZE(sz); HKEY hKey = NULL; *sz = '\0'; if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRegOutlWAB, 0, KEY_READ, &hKey)) { if(ERROR_SUCCESS == RegQueryValueEx(hKey, szEmpty, NULL, &dwType, (LPBYTE) sz, &dwSize)) { if(lstrlen(sz)) bRet = bFindOutlWABDll(sz, ARRAYSIZE(sz), szDLLPath, cchDLLPath, FALSE); } RegCloseKey(hKey); } if (!bRet) { *sz = '\0'; if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRegOffice9, 0, KEY_READ, &hKey)) { if(ERROR_SUCCESS == RegQueryValueEx(hKey, lpRegOfficeBin, NULL, &dwType, (LPBYTE) sz, &dwSize)) { if(lstrlen(sz)) bRet = bFindOutlWABDll(sz, ARRAYSIZE(sz), szDLLPath, cchDLLPath, TRUE); } } RegCloseKey(hKey); } if(!bRet) { *sz = '\0'; if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRegOffice, 0, KEY_READ, &hKey)) { if(ERROR_SUCCESS == RegQueryValueEx(hKey, lpRegOfficeBin, NULL, &dwType, (LPBYTE) sz, &dwSize)) { if(lstrlen(sz)) bRet = bFindOutlWABDll(sz, ARRAYSIZE(sz), szDLLPath, cchDLLPath, TRUE); } } RegCloseKey(hKey); } // Check in the Windows System Directory if(!bRet) { *sz = '\0'; GetSystemDirectory(sz, ARRAYSIZE(sz)); if(lstrlen(sz)) bRet = bFindOutlWABDll(sz, ARRAYSIZE(sz), szDLLPath, cchDLLPath, TRUE); } return bRet; } /*************************************************************************** Name : bUseOutlookStore Purpose : Determines if we are supposed to be using Outlook Parameters: none Returns : TRUE if we are supposed to use outlook AND we can find the outlook installation Comment : ***************************************************************************/ BOOL bUseOutlookStore() { HKEY hKey = NULL; DWORD dwUseOutlook = 0; BOOL bRet = FALSE; DWORD dwType = REG_DWORD; DWORD dwSize = sizeof(DWORD); if(ERROR_SUCCESS == RegOpenKeyEx( HKEY_CURRENT_USER, lpNewWABRegKey, 0, KEY_READ, &hKey)) { if(ERROR_SUCCESS == RegQueryValueEx(hKey, lpRegUseOutlookVal, NULL, &dwType, (LPBYTE) &dwUseOutlook, &dwSize)) { bRet = (BOOL) dwUseOutlook; } } if(hKey) RegCloseKey(hKey); if(bRet) { // just double check that we can actually find the OutlookWABSPI dll bRet = bCheckForOutlookWABDll(NULL, 0); } return bRet; } //$$////////////////////////////////////////////////////////// // // Copies Src to Dest. If Src is longer than Dest, // truncates the src and trails it with 3 dots // ////////////////////////////////////////////////////////////// int CopyTruncate(LPTSTR szDest, LPTSTR szSrc, int nMaxLen) { int nLen = lstrlen(szSrc)+1; if (nLen >= nMaxLen) { ULONG iLenDots = lstrlen(szTrailingDots) + 1; ULONG iLen = TruncatePos(szSrc, nMaxLen - iLenDots); CopyMemory(szDest,szSrc, sizeof(TCHAR)*(nMaxLen - iLenDots)); szDest[iLen]='\0'; StrCatBuff(szDest,szTrailingDots, nMaxLen); //DebugTrace("%s = %s\n", szDest, szSrc); } else { StrCpyN(szDest,szSrc,nMaxLen); } return nLen; } /////////////////////////////////////////////////////////////////// // // HrShowDSProps - shows Directory Service properties UI // // hWndParent - hWnd of Parent // lpszName - pointer to a buffer ... also contains name of LDAP // server to view prperties on - this name can be modified so // lpszName should point to a big enough buffer // bAddNew - TRUE if this is a new entry, false if this is props /////////////////////////////////////////////////////////////////// HRESULT HrShowDSProps(HWND hWndParent, LPTSTR ptszAcct, LPTSTR *pptszName, BOOL bAddNew) { HRESULT hr = hrSuccess; IImnAccountManager2 * lpAccountManager = NULL; IImnAccount * lpAccount = NULL; LPSTR lpAcct = ConvertWtoA(ptszAcct); // init account manager // Make sure there is an account manager if (hr = InitAccountManager(NULL, &lpAccountManager, NULL)) { ShowMessageBox(hWndParent, idsLDAPUnconfigured, MB_ICONEXCLAMATION | MB_OK); goto out; } // find this account if (hr = lpAccountManager->lpVtbl->FindAccount(lpAccountManager, AP_ACCOUNT_NAME, lpAcct, &lpAccount)) { DebugTrace( TEXT("FindAccount(%s) -> %x\n"), lpAcct, GetScode(hr)); goto out; } // show properties if (hr = lpAccount->lpVtbl->ShowProperties(lpAccount, hWndParent, 0)) { DebugTrace( TEXT("ShowProperties(%s) -> %x\n"), lpAcct, GetScode(hr)); goto out; } { char szBuf[MAX_UI_STR]; // Get the friendly name (== account name if this changed) if (! (HR_FAILED(hr = lpAccount->lpVtbl->GetPropSz(lpAccount, AP_ACCOUNT_NAME, szBuf, ARRAYSIZE(szBuf))))) { LPTSTR lp = ConvertAtoW(szBuf); if(lp) { *pptszName = lp; } } } out: if (lpAccount) { lpAccount->lpVtbl->Release(lpAccount); } LocalFreeAndNull(&lpAcct); // Don't release the account manager. It will be done when the IAdrBook is released. // if (lpAccountManager) { // lpAccountManager->lpVtbl->Release(lpAccountManager); // } return hr; } //$$/////////////////////////////////////////////////////////////////////////////// // // HrShowDirectoryServiceModificationDlg - Shows the main dialog with the list // of directory services and with a prop sheet for changing check order // // hWndParent - Parent for this dialog ///////////////////////////////////////////////////////////////////////////////// HRESULT HrShowDirectoryServiceModificationDlg(HWND hWndParent, LPIAB lpIAB) { ACCTLISTINFO ali; HRESULT hr = hrSuccess; IImnAccountManager2 * lpAccountManager; // Make sure there is an account manager if (hr = InitAccountManager(lpIAB, &lpAccountManager, NULL)) { ShowMessageBox(hWndParent, idsLDAPUnconfigured, MB_ICONEXCLAMATION | MB_OK); goto out; } ali.cbSize = sizeof(ACCTLISTINFO); ali.AcctTypeInit = (ACCTTYPE)-1; ali.dwAcctFlags = ACCT_FLAG_DIR_SERV; ali.dwFlags = 0; hr = lpAccountManager->lpVtbl->AccountListDialog(lpAccountManager, hWndParent, &ali); out: return hr; } /* - HrShellExecInternetCall - * * Checks if the selected, single item has PR_SERVERS set on it and has a default * callto item - if yes, shell-exects this item .. */ HRESULT HrShellExecInternetCall(LPADRBOOK lpAdrBook, HWND hWndLV) { HRESULT hr = E_FAIL; LPRECIPIENT_INFO lpItem = NULL; LPSPropValue lpPropArray = NULL; ULONG ulcProps = 0; int nCount = ListView_GetSelectedCount(hWndLV); if(nCount != 1) { ShowMessageBox(GetParent(hWndLV), (nCount > 1) ? IDS_ADDRBK_MESSAGE_ACTION : IDS_ADDRBK_MESSAGE_NO_ITEM, MB_ICONEXCLAMATION); goto out; } lpItem = GetItemFromLV(hWndLV, ListView_GetNextItem(hWndLV, -1, LVNI_SELECTED)); if(lpItem) { if(!HR_FAILED(hr = HrGetPropArray( lpAdrBook, NULL, lpItem->cbEntryID, lpItem->lpEntryID, MAPI_UNICODE, &ulcProps, &lpPropArray))) { ULONG i = 0, nConf = 0xffffffff, nDef = 0xffffffff; LPTSTR lpsz = NULL; for(i=0;i= lstrlen(szCallto)) { int nLen = lstrlen(szCallto); CopyMemory(sz, lpPropArray[nConf].Value.MVSZ.LPPSZ[i], sizeof(TCHAR)*nLen); sz[nLen] = '\0'; if(!lstrcmpi(sz, szCallto)) { lpsz = lpPropArray[nConf].Value.MVSZ.LPPSZ[i]; break; } } } } if(lpsz) if(!ShellExecute(GetParent(hWndLV), TEXT("open"), lpsz, NULL, NULL, SW_SHOWNORMAL)) ShowMessageBox(GetParent(hWndLV), idsCouldNotSelectUser, MB_ICONEXCLAMATION); } if(nConf == 0xffffffff || !lpsz) ShowMessageBox(GetParent(hWndLV), idsInternetCallNoCallTo, MB_ICONEXCLAMATION); } } out: if(lpPropArray) MAPIFreeBuffer(lpPropArray); return hr; } /* - GetItemFromLV - * utility function for returning the recipient item from the LV */ LPRECIPIENT_INFO GetItemFromLV(HWND hWndLV, int iItem) { LPRECIPIENT_INFO lpItem = NULL; LV_ITEM LVItem; LVItem.mask = LVIF_PARAM; LVItem.iItem = iItem; LVItem.iSubItem = 0; LVItem.lParam = 0; // Get item lParam LPRECIPIENT_INFO structure if (ListView_GetItem(hWndLV,&LVItem)) lpItem = ((LPRECIPIENT_INFO) LVItem.lParam); return lpItem; } /* - Helper function - */ void SetSBinary(LPSBinary lpsb, ULONG cb, LPBYTE lpb) { if(!lpsb || !cb || !lpb) return; if(lpsb->lpb = LocalAlloc(LMEM_ZEROINIT, cb)) { lpsb->cb = cb; CopyMemory(lpsb->lpb, lpb, cb); } } /* - GetWABIconImage - * */ int GetWABIconImage(LPRECIPIENT_INFO lpItem) { if(lpItem->cbEntryID == 0) return imageUnknown; if(lpItem->ulObjectType == MAPI_DISTLIST) { return imageDistList; } else { BYTE bType; if(lpItem->bIsMe) return imageMailUserMe; else if(lpItem->bHasCert) return imageMailUserWithCert; bType = IsWABEntryID(lpItem->cbEntryID, lpItem->lpEntryID, NULL,NULL,NULL, NULL, NULL); if(bType == WAB_LDAP_MAILUSER) return imageMailUserLDAP; else if(bType == WAB_ONEOFF) return imageMailUserOneOff; } return imageMailUser; } enum { IE401_DONTKNOW=0, IE401_TRUE, IE401_FALSE }; static int g_nIE401 = IE401_DONTKNOW; /* - bIsIE401 - * Checks if this installation has IE4.01 or greater so we can decide what flags to pass to the prop sheets * */ BOOL bIsIE401OrGreater() { BOOL bRet = FALSE; if(g_nIE401 == IE401_TRUE) return TRUE; if(g_nIE401 == IE401_FALSE) return FALSE; g_nIE401 = IE401_FALSE; // else we need to check InitCommonControlLib(); //load the DLL if(ghCommCtrlDLLInst) { LPDLLGETVERSIONPROCOE lpfnDllGetVersionProc = NULL; lpfnDllGetVersionProc = (LPDLLGETVERSIONPROCOE) GetProcAddress(ghCommCtrlDLLInst, "DllGetVersion"); if(lpfnDllGetVersionProc) { // Check the version number DLLVERSIONINFO dvi = {0}; dvi.cbSize = sizeof(dvi); lpfnDllGetVersionProc(&dvi); // we are looking for IE4 version 4.72 or more if( (dvi.dwMajorVersion > 4) || (dvi.dwMajorVersion == 4 && dvi.dwMinorVersion >= 72) ) { g_nIE401 = IE401_TRUE; bRet = TRUE; } } } DeinitCommCtrlClientLib(); return bRet; } #ifdef COLSEL_MENU /** ColSel_PropTagToString: This function will convert a propertytag to a string */ BOOL ColSel_PropTagToString( ULONG ulPropTag, LPTSTR lpszString, ULONG cchString) { UINT i, j; UINT iIndex; HMENU hMainMenu; HMENU hMenu; MENUITEMINFO mii; BOOL fRet = FALSE; hMainMenu = LoadMenu(hinstMapiX, MAKEINTRESOURCE(IDR_MENU_LVCONTEXTMENU_COLSEL)); if( !hMainMenu ) { DebugTrace( TEXT("unable to load main colsel menu\n")); goto exit; } hMenu = GetSubMenu( hMainMenu, 0); if( !hMenu ) { DebugTrace( TEXT("unable to load submenu from colsel main menu\n")); goto exit; } if( !lpszString ) { DebugTrace( TEXT("illegal argument -- lpszString must be valid mem\n")); goto exit; } mii.fMask = MIIM_TYPE; mii.cbSize = sizeof( MENUITEMINFO ); mii.dwTypeData = lpszString; mii.cch = cchString; for( i = 0; i < MAXNUM_MENUPROPS; i++) { if( MenuToPropTagMap[i] == ulPropTag ) { if( !GetMenuItemInfo( hMenu, i, TRUE, &mii) ) { DebugTrace( TEXT("unable to get menu item info: %x\n"), GetLastError() ); goto exit; } fRet = TRUE; } } exit: if ( hMainMenu != NULL ) DestroyMenu( hMainMenu ); if( !fRet ) DebugTrace( TEXT("unable to find property tag\n")); return fRet; } #endif // COLSEL_MENU /* - IsWindowOnScreen - * Checks if a window is onscreen so that if it is not entirely onscreen we can push it back * into a viewable area .. this way if the user changes screen resolution or switches multi-monitors * around, we don't lose the app */ BOOL IsWindowOnScreen(LPRECT lprc) { HDC hDC = GetDC(NULL); BOOL fRet = RectVisible(hDC, lprc); ReleaseDC(NULL, hDC); return fRet; } /* - IsHTTPMailEnabled - * Checks if HTTP is enabled so that we can hide UI if its not. */ static TCHAR c_szRegRootAthenaV2[] = TEXT("Software\\Microsoft\\Outlook Express"); static TCHAR c_szEnableHTTPMail[] = TEXT("HTTP Mail Enabled"); BOOL IsHTTPMailEnabled(LPIAB lpIAB) { #ifdef NOHTTPMAIL return FALSE; #else DWORD cb, bEnabled = FALSE; HKEY hkey = NULL; // [PaulHi] 1/5/98 Raid #64160 // Hotmail synchronization is disabled if the WAB is not in "identity aware" // mode. So, we need to check for this too. bEnabled = lpIAB->bProfilesIdent; // @todo [PaulHi] 12/1/98 // We really shouldn't be doing a registry query every time the user // opens up the Tools menu, i.e., in update menu. // Check this registry sometime during start up and save per instance. // open the OE5.0 key if ( bEnabled && (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegRootAthenaV2, 0, KEY_QUERY_VALUE, &hkey)) ) { cb = sizeof(bEnabled); RegQueryValueEx(hkey, c_szEnableHTTPMail, 0, NULL, (LPBYTE)&bEnabled, &cb); RegCloseKey(hkey); } // // [PaulHi] 12/1/98 Raid #57739 // HACK WARNING // Since the Hotmail server is currently hard coded to the U.S. 1252 // codepage, any other system codepage will result in corrupted data // after a round sync trip to the Hotmail server and back, for any fields // with DB characters (i.e., international). The temporary solution is // to simply disable Hotmail synchronization if a codepage other than // 1252 is detected on the client machine. // #define USLatin1CodePage 1252 if (bEnabled) { DWORD dwCodepage = GetACP(); if (dwCodepage != USLatin1CodePage) bEnabled = FALSE; } return bEnabled; #endif } /* - - WriteRegistryDeletedHotsyncItem * * Writes the Hotmail Contact/ID/Modtime info to the registry so we can track deletions for * Hotmail syncing * */ extern LPTSTR g_lpszSyncKey; void WriteRegistryDeletedHotsyncItem(LPTSTR lpServerID, LPTSTR lpContactID, LPTSTR lpModTime) { HKEY hKey = NULL,hSubKey = NULL; DWORD dwDisposition = 0; if( !lpServerID || !lstrlen(lpServerID) || !lpContactID || !lstrlen(lpContactID) || !lpModTime || !lstrlen(lpModTime) ) return; // Open key if (ERROR_SUCCESS == RegCreateKeyEx(HKEY_CURRENT_USER, g_lpszSyncKey, 0, //reserved NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition)) { if (ERROR_SUCCESS == RegCreateKeyEx(hKey,lpContactID, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hSubKey, &dwDisposition)) { // Create a value here .. the value name is the Contact ID and the Value Data is the ModTime // Now Write this key RegSetValueEx( hSubKey, lpServerID, 0, REG_SZ, (LPBYTE) lpModTime, (lstrlen(lpModTime)+1) * sizeof(TCHAR) ); } } if(hSubKey) RegCloseKey(hSubKey); if(hKey) RegCloseKey(hKey); } /* - HrSaveHotmailSyncInfoOnDeletion - * If the user has ever done any hotmail syncing, we need to track deletions in the WAB * so that after you delete an entry in the WAB, the corresponding hotmail entry will * be deleted on Sync. * * We store the hotmail sync info in the registry, hopefully there won't be too much of it * Whenever the hotmail sync happens, the info gets cleaned out. */ HRESULT HrSaveHotmailSyncInfoOnDeletion(LPADRBOOK lpAdrBook, LPSBinary lpEID) { // Basically we will open the object being deleted, look for it's Hotmail // properties and if these properties exist, we will put them into the registry // HRESULT hr = S_OK; ULONG ulcValues = 0,i=0; LPSPropValue lpProps = NULL; SizedSPropTagArray(3, ptaHotProps) = { 3, { PR_WAB_HOTMAIL_CONTACTIDS, PR_WAB_HOTMAIL_MODTIMES, PR_WAB_HOTMAIL_SERVERIDS, } }; hr = HrGetPropArray(lpAdrBook, (LPSPropTagArray) &ptaHotProps, lpEID->cb,(LPENTRYID) lpEID->lpb, MAPI_UNICODE, &ulcValues,&lpProps); if(HR_FAILED(hr) || !ulcValues || !lpProps) goto out; // The three props are supposed to be in sync, so if one exists, the other 2 will also exist // and if this is not true, then don't write the data to the registry if( lpProps[0].ulPropTag != PR_WAB_HOTMAIL_CONTACTIDS || !lpProps[0].Value.MVSZ.cValues || lpProps[1].ulPropTag != PR_WAB_HOTMAIL_MODTIMES || !lpProps[1].Value.MVSZ.cValues || lpProps[2].ulPropTag != PR_WAB_HOTMAIL_SERVERIDS || !lpProps[2].Value.MVSZ.cValues || lpProps[0].Value.MVSZ.cValues != lpProps[1].Value.MVSZ.cValues || lpProps[0].Value.MVSZ.cValues != lpProps[2].Value.MVSZ.cValues || lpProps[1].Value.MVSZ.cValues != lpProps[2].Value.MVSZ.cValues) goto out; for(i=0;i