/* * VCard.C - Implement VCard * * Wrap VCard in a mailuser object * * Copyright 1992 - 1996 Microsoft Corporation. All Rights Reserved. * */ #include "_apipch.h" #ifdef VCARD // This is the current vCard version implemented in this file // #define CURRENT_VCARD_VERSION "2.1" //#define CURRENT_VCARD_VERSION "2.1+" <- The code is really this version, see the URL section in WriteVCard. typedef enum _VC_STATE_ENUM { VCS_INITIAL, VCS_ITEMS, VCS_FINISHED, VCS_ERROR, } VC_STATE_ENUM, *LPVC_STATE_ENUM; typedef struct _VC_STATE { VC_STATE_ENUM vce; // state ULONG ulEmailAddrs; // count of email addresses BOOL fBusinessURL; // TRUE if we have already got a business URL BOOL fPersonalURL; // TRUE if we have already got a personal URL } VC_STATE, *LPVC_STATE; typedef enum _VCARD_KEY { VCARD_KEY_NONE = -1, // Always first VCARD_KEY_BEGIN = 0, VCARD_KEY_END, VCARD_KEY_ADR, VCARD_KEY_ORG, VCARD_KEY_N, VCARD_KEY_NICKNAME, VCARD_KEY_AGENT, VCARD_KEY_LOGO, VCARD_KEY_PHOTO, VCARD_KEY_LABEL, VCARD_KEY_FADR, VCARD_KEY_FN, VCARD_KEY_TITLE, VCARD_KEY_SOUND, VCARD_KEY_LANG, VCARD_KEY_TEL, VCARD_KEY_EMAIL, VCARD_KEY_TZ, VCARD_KEY_GEO, VCARD_KEY_NOTE, VCARD_KEY_URL, VCARD_KEY_BDAY, VCARD_KEY_ROLE, VCARD_KEY_REV, VCARD_KEY_UID, VCARD_KEY_KEY, VCARD_KEY_MAILER, VCARD_KEY_X, VCARD_KEY_VCARD, VCARD_KEY_VERSION, VCARD_KEY_X_WAB_GENDER, VCARD_KEY_MAX, } VCARD_KEY, *LPVCARD_KEY; // MUST be maintained in same order as _VCARD_KEY enum const LPSTR vckTable[VCARD_KEY_MAX] = { "BEGIN", // VCARD_KEY_BEGIN "END", // VCARD_KEY_END "ADR", // VCARD_KEY_ADR "ORG", // VCARD_KEY_ORG "N", // VCARD_KEY_N "NICKNAME", // VCARD_KEY_NICKNAME "AGENT", // VCARD_KEY_AGENT "LOGO", // VCARD_KEY_LOGO "PHOTO", // VCARD_KEY_PHOTO "LABEL", // VCARD_KEY_LABEL "FADR", // VCARD_KEY_FADR "FN", // VCARD_KEY_FN "TITLE", // VCARD_KEY_TITLE "SOUND", // VCARD_KEY_SOUND "LANG", // VCARD_KEY_LANG "TEL", // VCARD_KEY_TEL "EMAIL", // VCARD_KEY_EMAIL "TZ", // VCARD_KEY_TZ "GEO", // VCARD_KEY_GEO "NOTE", // VCARD_KEY_NOTE "URL", // VCARD_KEY_URL "BDAY", // VCARD_KEY_BDAY "ROLE", // VCARD_KEY_ROLE "REV", // VCARD_KEY_REV "UID", // VCARD_KEY_UID "KEY", // VCARD_KEY_KEY "MAILER", // VCARD_KEY_MAILER "X-", // VCARD_KEY_X "VCARD", // VCARD_KEY_VCARD "VERSION", // VCARD_KEY_VERSION "X-WAB-GENDER", // VCARD_KEY_X_WAB_GENDER }; typedef enum _VCARD_TYPE { VCARD_TYPE_NONE = -1, // always first VCARD_TYPE_DOM = 0, VCARD_TYPE_INTL, VCARD_TYPE_POSTAL, VCARD_TYPE_PARCEL, VCARD_TYPE_HOME, VCARD_TYPE_WORK, VCARD_TYPE_PREF, VCARD_TYPE_VOICE, VCARD_TYPE_FAX, VCARD_TYPE_MSG, VCARD_TYPE_CELL, VCARD_TYPE_PAGER, VCARD_TYPE_BBS, VCARD_TYPE_MODEM, VCARD_TYPE_CAR, VCARD_TYPE_ISDN, VCARD_TYPE_VIDEO, VCARD_TYPE_AOL, VCARD_TYPE_APPLELINK, VCARD_TYPE_ATTMAIL, VCARD_TYPE_CIS, VCARD_TYPE_EWORLD, VCARD_TYPE_INTERNET, VCARD_TYPE_IBMMAIL, VCARD_TYPE_MSN, VCARD_TYPE_MCIMAIL, VCARD_TYPE_POWERSHARE, VCARD_TYPE_PRODIGY, VCARD_TYPE_TLX, VCARD_TYPE_X400, VCARD_TYPE_GIF, VCARD_TYPE_CGM, VCARD_TYPE_WMF, VCARD_TYPE_BMP, VCARD_TYPE_MET, VCARD_TYPE_PMB, VCARD_TYPE_DIB, VCARD_TYPE_PICT, VCARD_TYPE_TIFF, VCARD_TYPE_ACROBAT, VCARD_TYPE_PS, VCARD_TYPE_JPEG, VCARD_TYPE_QTIME, VCARD_TYPE_MPEG, VCARD_TYPE_MPEG2, VCARD_TYPE_AVI, VCARD_TYPE_WAVE, VCARD_TYPE_AIFF, VCARD_TYPE_PCM, VCARD_TYPE_X509, VCARD_TYPE_PGP, VCARD_TYPE_MAX } VCARD_TYPE, *LPVCARD_TYPE; // MUST be maintained in same order as _VCARD_TYPE enum const LPSTR vctTable[VCARD_TYPE_MAX] = { "DOM", // VCARD_TYPE_DOM "INTL", // VCARD_TYPE_INTL "POSTAL", // VCARD_TYPE_POSTAL "PARCEL", // VCARD_TYPE_PARCEL "HOME", // VCARD_TYPE_HOME "WORK", // VCARD_TYPE_WORK "PREF", // VCARD_TYPE_PREF "VOICE", // VCARD_TYPE_VOICE "FAX", // VCARD_TYPE_FAX "MSG", // VCARD_TYPE_MSG "CELL", // VCARD_TYPE_CELL "PAGER", // VCARD_TYPE_PAGER "BBS", // VCARD_TYPE_BBS "MODEM", // VCARD_TYPE_MODEM "CAR", // VCARD_TYPE_CAR "ISDN", // VCARD_TYPE_ISDN "VIDEO", // VCARD_TYPE_VIDEO "AOL", // VCARD_TYPE_AOL "APPLELINK", // VCARD_TYPE_APPLELINK "ATTMAIL", // VCARD_TYPE_ATTMAIL "CIS", // VCARD_TYPE_CIS "EWORLD", // VCARD_TYPE_EWORLD "INTERNET", // VCARD_TYPE_INTERNET "IBMMAIL", // VCARD_TYPE_IBMMAIL "MSN", // VCARD_TYPE_MSN "MCIMAIL", // VCARD_TYPE_MCIMAIL "POWERSHARE", // VCARD_TYPE_POWERSHARE "PRODIGY", // VCARD_TYPE_PRODIGY "TLX", // VCARD_TYPE_TLX "X400", // VCARD_TYPE_X400 "GIF", // VCARD_TYPE_GIF "CGM", // VCARD_TYPE_CGM "WMF", // VCARD_TYPE_WMF "BMP", // VCARD_TYPE_BMP "MET", // VCARD_TYPE_MET "PMB", // VCARD_TYPE_PMB "DIB", // VCARD_TYPE_DIB "PICT", // VCARD_TYPE_PICT "TIFF", // VCARD_TYPE_TIFF "ACROBAT", // VCARD_TYPE_ACROBAT "PS", // VCARD_TYPE_PS "JPEG", // VCARD_TYPE_JPEG "QTIME", // VCARD_TYPE_QTIME "MPEG", // VCARD_TYPE_MPEG "MPEG2", // VCARD_TYPE_MPEG2 "AVI", // VCARD_TYPE_AVI "WAVE", // VCARD_TYPE_WAVE "AIFF", // VCARD_TYPE_AIFF "PCM", // VCARD_TYPE_PCM "X509", // VCARD_TYPE_X509 "PGP", // VCARD_TYPE_PGP }; typedef enum _VCARD_PARAM{ VCARD_PARAM_NONE = -1, // always first VCARD_PARAM_TYPE = 0, VCARD_PARAM_ENCODING, VCARD_PARAM_LANGUAGE, VCARD_PARAM_VALUE, VCARD_PARAM_CHARSET, VCARD_PARAM_MAX, } VCARD_PARAM, *LPVCARD_PARAM; // MUST be maintained in same order as _VCARD_PARAM enum const LPSTR vcpTable[VCARD_PARAM_MAX] = { "TYPE", // VCARD_PARAM_TYPE "ENCODING", // VCARD_PARAM_ENCODING "LANGUAGE", // VCARD_PARAM_LANGUAGE "VALUE", // VCARD_PARAM_VALUE "CHARSET", // VCARD_PARAM_CHARSET }; typedef enum _VCARD_ENCODING{ VCARD_ENCODING_NONE = -1, // always first VCARD_ENCODING_QUOTED_PRINTABLE = 0, VCARD_ENCODING_BASE64, VCARD_ENCODING_7BIT, VCARD_ENCODING_8BIT, VCARD_ENCODING_X, VCARD_ENCODING_MAX, } VCARD_ENCODING, *LPVCARD_ENCODING; // MUST be maintained in same order as _VCARD_ENCODING enum const LPSTR vceTable[VCARD_ENCODING_MAX] = { "QUOTED-PRINTABLE", // VCARD_ENCODING_QUOTED_PRINTABLE "BASE64", // VCARD_ENCODING_BASE64 "7BIT", // VCARD_ENCODING_7BIT "8BIT", // VCARD_ENCODING_8BIT "X-", // VCARD_ENCODING_X }; const LPTSTR szColon = TEXT(":"); const LPSTR szColonA = ":"; const LPTSTR szSemicolon = TEXT(";"); const LPSTR szEquals = "="; const LPSTR szCRLFA = "\r\n"; const LPTSTR szCRLF = TEXT("\r\n"); const LPTSTR szCommaSpace = TEXT(", "); const LPTSTR szSpace = TEXT(" "); const LPTSTR szX400 = TEXT("X400"); const LPSTR szSMTPA = "SMTP"; typedef struct _VCARD_PARAM_FLAGS { int fTYPE_DOM:1; int fTYPE_INTL:1; int fTYPE_POSTAL:1; int fTYPE_PARCEL:1; int fTYPE_HOME:1; int fTYPE_WORK:1; int fTYPE_PREF:1; int fTYPE_VOICE:1; int fTYPE_FAX:1; int fTYPE_MSG:1; int fTYPE_CELL:1; int fTYPE_PAGER:1; int fTYPE_BBS:1; int fTYPE_MODEM:1; int fTYPE_CAR:1; int fTYPE_ISDN:1; int fTYPE_VIDEO:1; int fTYPE_AOL:1; int fTYPE_APPLELINK:1; int fTYPE_ATTMAIL:1; int fTYPE_CIS:1; int fTYPE_EWORLD:1; int fTYPE_INTERNET:1; int fTYPE_IBMMAIL:1; int fTYPE_MSN:1; int fTYPE_MCIMAIL:1; int fTYPE_POWERSHARE:1; int fTYPE_PRODIGY:1; int fTYPE_TLX:1; int fTYPE_X400:1; int fTYPE_GIF:1; int fTYPE_CGM:1; int fTYPE_WMF:1; int fTYPE_BMP:1; int fTYPE_MET:1; int fTYPE_PMB:1; int fTYPE_DIB:1; int fTYPE_PICT:1; int fTYPE_TIFF:1; int fTYPE_ACROBAT:1; int fTYPE_PS:1; int fTYPE_JPEG:1; int fTYPE_QTIME:1; int fTYPE_MPEG:1; int fTYPE_MPEG2:1; int fTYPE_AVI:1; int fTYPE_WAVE:1; int fTYPE_AIFF:1; int fTYPE_PCM:1; int fTYPE_X509:1; int fTYPE_PGP:1; int fPARAM_TYPE:1; int fPARAM_ENCODING:1; int fPARAM_LANGUAGE:1; int fPARAM_VALUE:1; int fPARAM_CHARSET:1; int fENCODING_QUOTED_PRINTABLE:1; int fENCODING_BASE64:1; int fENCODING_7BIT:1; int fENCODING_X:1; LPSTR szPARAM_LANGUAGE; LPSTR szPARAM_CHARSET; } VCARD_PARAM_FLAGS, *LPVCARD_PARAM_FLAGS; // // For WAB clients that use named props and want to get/put their unique data // in the vCards as extended vCard properties, we will store these extended // props in a linked list and use it when importing/exporting a vCard // typedef struct _ExtVCardProp { ULONG ulExtPropTag; LPSTR lpszExtPropName; struct _ExtVCardProp * lpNext; } EXTVCARDPROP, * LPEXTVCARDPROP; CONST CHAR six2base64[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M', 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', 'a','b','c','d','e','f','g','h','i','j','k','l','m', 'n','o','p','q','r','s','t','u','v','w','x','y','z', '0','1','2','3','4','5','6','7','8','9','+','/' }; CONST INT base642six[256]={ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63, 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9, 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27, 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64, 64,64,64,64,64,64,64,64,64,64,64,64,64 }; HRESULT ReadLn(HANDLE hVCard, VCARD_READ ReadFn, LPSTR * lppLine, LPULONG lpcbItem, LPSTR * lppBuffer, LPULONG lpcbBuffer); HRESULT InterpretVCardItem (LPSTR lpName, LPSTR lpOption, LPSTR lpData, LPMAILUSER lpMailUser, LPEXTVCARDPROP lpList, LPVC_STATE lpvcs); void ParseVCardItem(LPSTR lpBuffer, LPSTR * lppName, LPSTR * lppOption, LPSTR * lppData); HRESULT ParseVCardType(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf); HRESULT ParseVCardParams(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf); VCARD_KEY RecognizeVCardKeyWord(LPSTR lpName); HRESULT ParseVCardEncoding(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf); HRESULT ReadVCardItem(HANDLE hVCard, VCARD_READ ReadFn, LPSTR * lppBuffer, LPULONG lpcbBuffer); HRESULT FileWriteFn(HANDLE handle, LPVOID lpBuffer, ULONG uBytes, LPULONG lpcbWritten); HRESULT ParseCert( LPSTR lpData, ULONG cbData, LPMAILUSER lpMailUser ); HRESULT DecodeBase64(LPSTR bufcoded,LPSTR pbuffdecoded, PDWORD pcbDecoded); HRESULT WriteVCardValue(HANDLE hVCard, VCARD_WRITE WriteFn, LPBYTE lpData, ULONG cbData); /*************************************************************************** Name : FreeExtVCardPropList Purpose : Frees the localalloced list of extended props that we get/set on a vCard Parameters: lpList -> list to free up Returns : void Comment : ***************************************************************************/ void FreeExtVCardPropList(LPEXTVCARDPROP lpList) { LPEXTVCARDPROP lpTmp = lpList; while(lpTmp) { lpList = lpList->lpNext; LocalFreeAndNull(&lpTmp->lpszExtPropName); LocalFree(lpTmp); lpTmp = lpList; } } /*************************************************************************** Name : HrGetExtVCardPropList Purpose : Reads the registry for registered named props for VCard import/export and gets the named prop names and guids and extended prop strings and turns them into proper tags and stores these tags and strings in a linked list Parameters: lppList -> list to return Returns : HRESULT Comment : ***************************************************************************/ static const TCHAR szNamedVCardPropsRegKey[] = TEXT("Software\\Microsoft\\WAB\\WAB4\\NamedVCardProps"); HRESULT HrGetExtVCardPropList(LPMAILUSER lpMailUser, LPEXTVCARDPROP * lppList) { HRESULT hr = E_FAIL; HKEY hKey = NULL; LPEXTVCARDPROP lpList = NULL; DWORD dwIndex = 0, dwSize = 0; TCHAR szGuidName[MAX_PATH]; if(!lppList) goto out; *lppList = NULL; // // We will look in the registry under HKLM\Software\Microsoft\WAB\WAB4\NamedVCardProps // If this key exists, we enumerate all sub keys under it // The format for this key is // // HKLM\Software\Microsoft\WAB\WAB4\NamedVCardProps // Guid1 // PropString1:PropName1 // PropString1:PropName2 // Guid2 // PropString1:PropName1 // etc // if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, szNamedVCardPropsRegKey, 0, KEY_READ, &hKey)) { goto out; } *szGuidName = '\0'; dwSize = CharSizeOf(szGuidName); // Found the key, now enumerate all sub keys ... while(ERROR_SUCCESS == RegEnumKeyEx(hKey, dwIndex, szGuidName, &dwSize, NULL, NULL, NULL, NULL)) { GUID guidTmp = {0}; unsigned short szW[MAX_PATH]; StrCpyN(szW, szGuidName, ARRAYSIZE(szW)); if( !(HR_FAILED(hr = CLSIDFromString(szW, &guidTmp))) ) { HKEY hGuidKey = NULL; // Open the GUID key if(ERROR_SUCCESS == RegOpenKeyEx(hKey, szGuidName, 0, KEY_READ, &hGuidKey)) { TCHAR szValName[MAX_PATH]; DWORD dwValIndex = 0, dwValSize = CharSizeOf(szValName); DWORD dwType = 0, dwTagName = 0, dwTagSize = sizeof(DWORD); TCHAR szTagName[MAX_PATH]; *szValName = '\0'; while(ERROR_SUCCESS == RegEnumValue(hGuidKey, dwValIndex, szValName, &dwValSize, 0, &dwType, NULL, NULL)) { MAPINAMEID mnid = {0}; LPMAPINAMEID lpmnid = NULL; LPSPropTagArray lpspta = NULL; *szTagName = '\0'; // Check if this is a NAME or an ID if(dwType == REG_DWORD) { dwTagSize = sizeof(DWORD); //Read in the Value if(ERROR_SUCCESS != RegQueryValueEx(hGuidKey, szValName, 0, &dwType, (LPBYTE) &dwTagName, &dwTagSize)) { continue; } } else if(dwType == REG_SZ) { dwTagSize = CharSizeOf(szTagName); //Read in the Value if(ERROR_SUCCESS != RegQueryValueEx(hGuidKey, szValName, 0, &dwType, (LPBYTE) szTagName, &dwTagSize)) { continue; } } // // At this point I have the GUID, the name of the named prop, and the // ExtendedPropString for this prop .. // // First get the actual named proptag from this GUID // mnid.lpguid = &guidTmp; if(lstrlen(szTagName)) { mnid.ulKind = MNID_STRING; mnid.Kind.lpwstrName = (LPWSTR) szTagName; } else { mnid.ulKind = MNID_ID; mnid.Kind.lID = dwTagName; } lpmnid = &mnid; if(!HR_FAILED(lpMailUser->lpVtbl->GetIDsFromNames( lpMailUser, 1, &lpmnid, MAPI_CREATE, // Dont create if it dont exist &lpspta))) { // Got the tag if(lpspta->aulPropTag[0] && lpspta->cValues) { LPEXTVCARDPROP lpTmp = LocalAlloc(LMEM_ZEROINIT, sizeof(EXTVCARDPROP)); if(lpTmp) { lpTmp->lpszExtPropName = ConvertWtoA(szValName); if(lpTmp->lpszExtPropName) { lpTmp->ulExtPropTag = CHANGE_PROP_TYPE(lpspta->aulPropTag[0],PT_STRING8); lpTmp->lpNext = lpList; lpList = lpTmp; } else LocalFree(lpTmp); } } if(lpspta) MAPIFreeBuffer(lpspta); } dwValIndex++; *szValName = '\0'; dwValSize = CharSizeOf(szValName); } } if(hGuidKey) RegCloseKey(hGuidKey); } dwIndex++; *szGuidName = '\0'; dwSize = CharSizeOf(szGuidName); } *lppList = lpList; hr = S_OK; out: if(hKey) RegCloseKey(hKey); if(HR_FAILED(hr) && lpList) FreeExtVCardPropList(lpList); return hr; } const static int c_cchMaxWin9XBuffer = 1000; /*************************************************************************** Name : ReadVCard Purpose : Reads a vCard from a file to a MAILUSER object. Parameters: hVCard = open handle to VCard object ReadFn = Read function to read hVCard, looks like ReadFile(). lpMailUser -> open mailuser object Returns : HRESULT Comment : ***************************************************************************/ HRESULT ReadVCard(HANDLE hVCard, VCARD_READ ReadFn, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; LPSTR lpBuffer = NULL; LPSTR lpName, lpOption, lpData; ULONG cbBuffer; VC_STATE vcs; LPEXTVCARDPROP lpList = NULL; vcs.vce = VCS_INITIAL; vcs.ulEmailAddrs = 0; vcs.fBusinessURL = FALSE; vcs.fPersonalURL = FALSE; // // See if there are any named props we need to handle on import // HrGetExtVCardPropList(lpMailUser, &lpList); while ( !HR_FAILED(hResult) && !(HR_FAILED(hResult = ReadVCardItem(hVCard, ReadFn, &lpBuffer, &cbBuffer))) && lpBuffer && (vcs.vce != VCS_FINISHED)) { ParseVCardItem(lpBuffer, &lpName, &lpOption, &lpData); // [PaulHi] 5/13/99 Win9X cannot handle strings larger than 1023 characters // in length (FormatMessage() is one example). And can cause buffer overruns // and crashes. If we get VCard data greater than this then we must not add // it to the lpMailUser object, or risk crashing Win9X when trying to display. // Instead just truncate so user can salvage as much as possible. // // YSt 6/25/99 if vCard has certificate then buffer may be more than 1000 bytes, we need to exlude this case // from this checkin. // I suppose that certificate has VCARD_KEY_KEY tag if (!g_bRunningOnNT && (lpName && lstrcmpiA(lpName, vckTable[VCARD_KEY_KEY])) && lpData && (lstrlenA(lpData) > c_cchMaxWin9XBuffer) ) lpData[c_cchMaxWin9XBuffer] = '\0'; if (lpName && lpData) { if (hResult = InterpretVCardItem(lpName, lpOption, lpData, lpMailUser, lpList, &vcs)) { DebugTrace( TEXT("ReadVCard:InterpretVCardItem -> %x"), GetScode(hResult)); } } LocalFreeAndNull(&lpBuffer); } if (! HR_FAILED(hResult)) { hResult = hrSuccess; } if(lpList) FreeExtVCardPropList(lpList); return(hResult); } /*************************************************************************** Name : BufferReadFn Purpose : Read from the supplied buffer Parameters: handle = pointer to SBinary in which the lpb contains the source buffer and the cb param contains how much of the buffer has been parsed lpBuffer -> buffer to read to uBytes = size of lpBuffer lpcbRead -> returned bytes read Returns : HRESULT ***************************************************************************/ HRESULT BufferReadFn(HANDLE handle, LPVOID lpBuffer, ULONG uBytes, LPULONG lpcbRead) { LPSBinary lpsb = (LPSBinary) handle; LPSTR lpBuf = (LPSTR) lpsb->lpb; LPSTR lpSrc = lpBuf + lpsb->cb; *lpcbRead = 0; if(!lstrlenA(lpSrc)) return(ResultFromScode(WAB_W_END_OF_DATA)); if(uBytes > (ULONG) lstrlenA(lpSrc)) uBytes = lstrlenA(lpSrc); CopyMemory(lpBuffer, lpSrc, uBytes); lpsb->cb += uBytes; *lpcbRead = uBytes; return(hrSuccess); } /*************************************************************************** Name : FileReadFn Purpose : Read from the file handle Parameters: handle = open file handle lpBuffer -> buffer to read to uBytes = size of lpBuffer lpcbRead -> returned bytes read Returns : HRESULT Comment : ReadFile callback for ReadVCard ***************************************************************************/ HRESULT FileReadFn(HANDLE handle, LPVOID lpBuffer, ULONG uBytes, LPULONG lpcbRead) { *lpcbRead = 0; if (! ReadFile(handle, lpBuffer, uBytes, lpcbRead, NULL)) { DebugTrace( TEXT("FileReadFn:ReadFile -> %u\n"), GetLastError()); return(ResultFromScode(MAPI_E_DISK_ERROR)); } if (*lpcbRead == 0) { return(ResultFromScode(WAB_W_END_OF_DATA)); } return(hrSuccess); } /*************************************************************************** Name : TrimLeadingWhitespace Purpose : Move the pointer past any whitespace. Parameters: lpBuffer -> string (null terminated) Returns : pointer to next non-whitespace or NULL if end of line Comment : ***************************************************************************/ LPBYTE TrimLeadingWhitespace(LPBYTE lpBuffer) { while (*lpBuffer) { switch (*lpBuffer) { case ' ': case '\t': lpBuffer++; break; default: return(lpBuffer); } } return(NULL); } /*************************************************************************** Name : TrimTrailingWhiteSpace Purpose : Trims off the trailing white space Parameters: lpString = string to trim Returns : none Comment : Starts at the end of the string, moving the EOS marker back until a non-whitespace character is found. Space and Tab are the only whitespace characters recognized. ***************************************************************************/ void TrimTrailingWhiteSpace(LPSTR lpString) { register LPSTR lpEnd; lpEnd = lpString + (lstrlenA(lpString) - 1); while ((lpEnd >= lpString) && ((*lpEnd == ' ') || (*lpEnd == '\t'))) { *(lpEnd--) = '\0'; } } /*************************************************************************** Name : ParseWord Purpose : Move the pointer to the next word and null the end of the current word. (null terminated) Parameters: lpBuffer -> current word ch = delimiter character Returns : pointer to next word or NULL if end of line Comment : ***************************************************************************/ LPSTR ParseWord(LPSTR lpString, TCHAR ch) { while (*lpString) { if (*lpString == ch) { *lpString++ = '\0'; lpString = (LPSTR)TrimLeadingWhitespace((LPBYTE)lpString); if (lpString && *lpString) { return(lpString); } else { return(NULL); } } lpString++; } // Didn't find another word. return(NULL); } /*************************************************************************** Name : RecognizeVCardKeyWord Purpose : Recognize the vCard keyword (null terminated) Parameters: lpName -> start of key name Returns : VCARD_KEY value Comment : ***************************************************************************/ VCARD_KEY RecognizeVCardKeyWord(LPSTR lpName) { register ULONG i; // Look for recognized words for (i = 0; i < VCARD_KEY_MAX; i++) { if (! lstrcmpiA(vckTable[i], lpName)) { // Found it return(i); } } return(VCARD_KEY_NONE); // didn't recognize } /*************************************************************************** Name : RecognizeVCardType Purpose : Recognize the vCard type option (null terminated) Parameters: lpName -> start of type name Returns : VCARD_OPTION value Comment : ***************************************************************************/ VCARD_TYPE RecognizeVCardType(LPSTR lpName) { register ULONG i; // Look for recognized words for (i = 0; i < VCARD_TYPE_MAX; i++) { if (! lstrcmpiA(vctTable[i], lpName)) { // Found it return(i); } } return(VCARD_TYPE_NONE); // didn't recognize } /*************************************************************************** Name : RecognizeVCardParam Purpose : Recognize the vCard param (null terminated) Parameters: lpName -> start of param name Returns : VCARD_PARAM value Comment : ***************************************************************************/ VCARD_PARAM RecognizeVCardParam(LPSTR lpName) { register ULONG i; // Look for recognized words for (i = 0; i < VCARD_PARAM_MAX; i++) { if (! lstrcmpiA(vcpTable[i], lpName)) { // Found it return(i); } } return(VCARD_PARAM_NONE); } /*************************************************************************** Name : RecognizeVCardEncoding Purpose : Recognize the vCard encoding (null terminated) Parameters: lpName -> start of encoding name Returns : VCARD_ENCODING value Comment : ***************************************************************************/ VCARD_ENCODING RecognizeVCardEncoding(LPSTR lpName) { register ULONG i; // Look for recognized words for (i = 0; i < VCARD_ENCODING_MAX; i++) { if (! lstrcmpiA(vceTable[i], lpName)) { // Found it return(i); } } return(VCARD_ENCODING_NONE); // didn't recognize } /*************************************************************************** Name : ParseVCardItem Purpose : Parse the vCard item into components Parameters: lpBuffer = current input line (null terminated) lppName -> returned property name pointer lppOption -> returned options string pointer lppData -> returned data string pointer Returns : none Comment : Expects the keyword to be in the current line (lpBuffer), but may read more lines as necesary to get a complete item. ***************************************************************************/ void ParseVCardItem(LPSTR lpBuffer, LPSTR * lppName, LPSTR * lppOption, LPSTR * lppData) { TCHAR ch; BOOL fColon = FALSE; BOOL fSemicolon = FALSE; *lppName = *lppOption = *lppData = NULL; // Find the Name if (lpBuffer = (LPSTR)TrimLeadingWhitespace((LPBYTE)lpBuffer)) { *lppName = lpBuffer; while (ch = *lpBuffer) { switch (ch) { case ':': // found end of name/options fColon = TRUE; // data follows whitespace *lppData = (LPSTR)TrimLeadingWhitespace((LPBYTE)lpBuffer + 1); *lpBuffer = '\0'; // null out colon goto exit; case ';': // found an option seperator if (! fSemicolon) { fSemicolon = TRUE; // option follows semicolon and whitespace *lppOption = (LPSTR)TrimLeadingWhitespace((LPBYTE)lpBuffer + 1); *lpBuffer = '\0'; // null out first semicolon } break; case '.': // end of a group prefix if (! fColon && ! fSemicolon) { // Yes, this is a group prefix. Get past it. *lppName = (LPSTR)TrimLeadingWhitespace((LPBYTE)lpBuffer + 1); } break; default: // normal character break; } lpBuffer++; } } exit: return; } /*************************************************************************** Name : ParseName Purpose : parse the name into properites Parameters: lpvcpf = parameter flags lpData = data string lpMailUser -> output mailuser object Returns : hResult Comment : vCard names are of the form: TEXT("surname; given name; middle name; prefix; suffix") ***************************************************************************/ HRESULT ParseName(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; SPropValue spv[5] = {0}; register LPSTR lpCurrent; ULONG i = 0; // Look for Surname if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_SURNAME, PT_STRING8); spv[i].Value.lpszA = lpCurrent; i++; } } if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_GIVEN_NAME, PT_STRING8); spv[i].Value.lpszA = lpCurrent; i++; } } if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_MIDDLE_NAME, PT_STRING8); spv[i].Value.lpszA = lpCurrent; i++; } } if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_DISPLAY_NAME_PREFIX, PT_STRING8); spv[i].Value.lpszA = lpCurrent; i++; } } if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_GENERATION, PT_STRING8); spv[i].Value.lpszA = lpCurrent; i++; } } if (i) { if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, i, spv, NULL))) { DebugTrace( TEXT("ParseName:SetProps -> %x\n"), GetScode(hResult)); } } return(hResult); } /*************************************************************************** Name : ParseAdr Purpose : parse the address into properites Parameters: lpvcpf -> parameter flags lpData = data string lpMailUser -> output mailuser object Returns : hResult Comment : vCard addreses are of the form: TEXT("PO box; extended addr; street addr; city; region; postal code; country") Option: DOM; INTL; POSTAL; PARCEL; HOME; WORK; PREF; CHARSET; LANGUAGE We will combine extended addr and street addr into PR_STREET_ADDRESS. ***************************************************************************/ HRESULT ParseAdr(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; SPropValue spv[7] = {0}; // must keep up with props set from ADR! register LPSTR lpCurrent; ULONG i = 0; LPSTR lpStreetAddr = NULL; LPSTR lpExtendedAddr = NULL; ULONG cbAddr = 0; LPSTR lpAddr = NULL; SCODE sc; BOOL fHome = lpvcpf->fTYPE_HOME; BOOL fBusiness = lpvcpf->fTYPE_WORK; // // default is other type of address // Look for PO box if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { if(fBusiness) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_POST_OFFICE_BOX, PT_STRING8); else if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_POST_OFFICE_BOX, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_POST_OFFICE_BOX, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } // extended addr if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { lpExtendedAddr = lpCurrent; } } // Street address if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { lpStreetAddr = lpCurrent; } } if (fBusiness) { // BUSINESS if (lpExtendedAddr) { // have a business extended addr field spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OFFICE_LOCATION, PT_STRING8); spv[i].Value.lpszA = lpExtendedAddr;; i++; } if (lpStreetAddr) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_ADDRESS_STREET, PT_STRING8); spv[i].Value.lpszA = lpStreetAddr;; i++; } } else { // HOME // Don't have extended for home if (! lpExtendedAddr && lpStreetAddr) { if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_STREET, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_STREET, PT_STRING8); spv[i].Value.lpszA= lpStreetAddr; i++; } else if (lpExtendedAddr && ! lpStreetAddr) { if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_STREET, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_STREET, PT_STRING8); spv[i].Value.lpszA= lpExtendedAddr; i++; } else { // Have to concatenate Extended and Street address if (lpExtendedAddr) { cbAddr = (lstrlenA(lpExtendedAddr)+1); } if (lpStreetAddr) { cbAddr += (lstrlenA(lpStreetAddr)+1); } if (cbAddr) { // room for CR and NULL if (sc = MAPIAllocateBuffer(cbAddr, &lpAddr)) { hResult = ResultFromScode(sc); goto exit; } if (lpExtendedAddr) { StrCpyNA(lpAddr, lpExtendedAddr, (cbAddr / sizeof(lpAddr[0]))); if (lpStreetAddr) { StrCatBuffA(lpAddr, "\n", (cbAddr / sizeof(lpAddr[0]))); } StrCatBuffA(lpAddr, lpStreetAddr, (cbAddr / sizeof(lpAddr[0]))); } else if (lpStreetAddr) { StrCpyNA(lpAddr, lpStreetAddr, (cbAddr / sizeof(lpAddr[0]))); } if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_STREET, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_STREET, PT_STRING8); spv[i].Value.lpszA= lpAddr; i++; } } } // locality (city) if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { if(fBusiness) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_ADDRESS_CITY, PT_STRING8); else if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_CITY, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_CITY, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } // region (state/province) if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { if(fBusiness) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE, PT_STRING8); else if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_STATE_OR_PROVINCE, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_STATE_OR_PROVINCE, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } // postal code if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { if(fBusiness) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_ADDRESS_POSTAL_CODE, PT_STRING8); else if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_POSTAL_CODE, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_POSTAL_CODE, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } // Country if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { if(fBusiness) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_ADDRESS_COUNTRY, PT_STRING8); else if(fHome) spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_ADDRESS_COUNTRY, PT_STRING8); else spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_ADDRESS_COUNTRY, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } if (i) { if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, i, spv, NULL))) { DebugTrace( TEXT("ParseAdr:SetProps -> %x\n"), GetScode(hResult)); } } exit: FreeBufferAndNull(&lpAddr); return(hResult); } enum { iphPR_BUSINESS_FAX_NUMBER, iphPR_HOME_FAX_NUMBER, iphPR_CELLULAR_TELEPHONE_NUMBER, iphPR_CAR_TELEPHONE_NUMBER, iphPR_ISDN_NUMBER, iphPR_PAGER_TELEPHONE_NUMBER, iphPR_BUSINESS_TELEPHONE_NUMBER, iphPR_BUSINESS2_TELEPHONE_NUMBER, iphPR_HOME_TELEPHONE_NUMBER, iphPR_HOME2_TELEPHONE_NUMBER, iphPR_PRIMARY_TELEPHONE_NUMBER, iphPR_OTHER_TELEPHONE_NUMBER, iphMax }; SizedSPropTagArray(iphMax, tagaPhone) = { iphMax, { PR_BUSINESS_FAX_NUMBER, PR_HOME_FAX_NUMBER, PR_CELLULAR_TELEPHONE_NUMBER, PR_CAR_TELEPHONE_NUMBER, PR_ISDN_NUMBER, PR_PAGER_TELEPHONE_NUMBER, PR_BUSINESS_TELEPHONE_NUMBER, PR_BUSINESS2_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_HOME2_TELEPHONE_NUMBER, PR_PRIMARY_TELEPHONE_NUMBER, PR_OTHER_TELEPHONE_NUMBER } }; /*************************************************************************** Name : ParseTel Purpose : parse the telephone number into properites Parameters: lpvcpf -> parameter flags lpData = data string lpMailUser -> output mailuser object Returns : hResult Comment : ***************************************************************************/ HRESULT ParseTel(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; SPropValue spv[iphMax] = {0}; ULONG i = 0; BOOL fBusiness = lpvcpf->fTYPE_WORK;// || ! lpvcpf->fTYPE_HOME; // default is business BOOL fHome = lpvcpf->fTYPE_HOME; BOOL fFax = lpvcpf->fTYPE_FAX; BOOL fCell = lpvcpf->fTYPE_CELL; BOOL fCar = lpvcpf->fTYPE_CAR; BOOL fModem = lpvcpf->fTYPE_MODEM; BOOL fISDN = lpvcpf->fTYPE_ISDN; BOOL fPager = lpvcpf->fTYPE_PAGER; BOOL fBBS = lpvcpf->fTYPE_BBS; BOOL fVideo = lpvcpf->fTYPE_VIDEO; BOOL fMsg = lpvcpf->fTYPE_MSG; BOOL fVoice = lpvcpf->fTYPE_VOICE | (! (fMsg | fFax | fModem | fISDN | fPager | fBBS)); BOOL fPref = lpvcpf->fTYPE_PREF; LPSPropValue lpaProps = NULL; ULONG ulcProps; // if this is not a prefered address, and its not home or business // turn it into a business number - we make this exception for the // primary_telephone_number which we output with the PREF prefix if(!fPref && !fBusiness && !fHome && !fVoice) fBusiness = TRUE; // FAX # if (lpData && *lpData) { // What is already there? if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser, (LPSPropTagArray)&tagaPhone, 0, //MAPI_UNICODE, // ulFlags, &ulcProps, &lpaProps))) { DebugTraceResult( TEXT("ParseTel:GetProps(DL)\n"), hResult); // No properties, not fatal. } if (fFax) { if (fBusiness) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_BUSINESS_FAX_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_FAX_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } if (fHome) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_HOME_FAX_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_FAX_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } } // CELL # if (fCell) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_CELLULAR_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_CELLULAR_TELEPHONE_NUMBER, PT_STRING8); // not business/home specific spv[i].Value.lpszA= lpData; i++; } } // CAR # if (fCar) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_CAR_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_CAR_TELEPHONE_NUMBER, PT_STRING8); // not business/home specific spv[i].Value.lpszA= lpData; i++; } } // ISDN # if (fISDN) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_ISDN_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_ISDN_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } // PAGER # if (fPager) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_PAGER_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_PAGER_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } // VOICE # if (fVoice) { if (fBusiness) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_BUSINESS_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } else if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_BUSINESS2_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_BUSINESS2_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } else if (fHome) { if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_HOME_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } else if (lpvcpf->fTYPE_PREF || PROP_ERROR(lpaProps[iphPR_HOME2_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_HOME2_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } else { if (lpvcpf->fTYPE_VOICE && PROP_ERROR(lpaProps[iphPR_OTHER_TELEPHONE_NUMBER]) && !(fFax | fCell | fCar | fModem | fISDN | fPager | fBBS | fVideo | fMsg) ) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } } if(fPref && !fFax && !fCell && !fCar && !fModem && !fISDN && !fPager && !fBBS && !fMsg) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_PRIMARY_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } // Store the first one of BBS, MODEM, or VIDEO that we get // if (fMsg || fBBS || fModem || fVideo) { if (PROP_ERROR(lpaProps[iphPR_OTHER_TELEPHONE_NUMBER])) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_OTHER_TELEPHONE_NUMBER, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } } FreeBufferAndNull(&lpaProps); if (i) { if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, i, spv, NULL))) { DebugTrace( TEXT("ParseTel:SetProps -> %x\n"), GetScode(hResult)); } } } return(hResult); } enum { iemPR_CONTACT_EMAIL_ADDRESSES, iemPR_CONTACT_ADDRTYPES, iemPR_CONTACT_DEFAULT_ADDRESS_INDEX, iemPR_EMAIL_ADDRESS, iemPR_ADDRTYPE, iemMax }; SizedSPropTagArray(iemMax, tagaEmail) = { iemMax, { PR_CONTACT_EMAIL_ADDRESSES, PR_CONTACT_ADDRTYPES, PR_CONTACT_DEFAULT_ADDRESS_INDEX, PR_EMAIL_ADDRESS, PR_ADDRTYPE, } }; const char szAtSign[] = "@"; #define cbAtSign sizeof(szAtSign) const char szMSNpostfix[] = "@msn.com"; #define cbMSNpostfix sizeof(szMSNpostfix) const char szAOLpostfix[] = "@aol.com"; #define cbAOLpostfix sizeof(szAOLpostfix) const char szCOMPUSERVEpostfix[] = "@compuserve.com"; #define cbCOMPUSERVEpostfix sizeof(szCOMPUSERVEpostfix) /*************************************************************************** Name : ParseEmail Purpose : parse an email address into properites Parameters: lpvcpf -> parameter flags lpData = data string lpMailUser -> output mailuser object lpvcs -> vCard import state Returns : hResult Comment : ***************************************************************************/ HRESULT ParseEmail(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser, LPVC_STATE lpvcs) { HRESULT hResult = hrSuccess; ULONG i = 0; BOOL fBusiness = ! lpvcpf->fTYPE_HOME; // default is business LPSPropValue lpaProps = NULL; ULONG ulcProps; SCODE sc; LPSTR lpAddrType = szSMTPA; LPSTR lpEmailAddress = lpData; LPSTR lpTemp = NULL; LPTSTR lpAddrTypeW = NULL; LPTSTR lpEmailAddressW = NULL; if (lpData && *lpData) { if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser, (LPSPropTagArray)&tagaEmail, MAPI_UNICODE, // ulFlags, &ulcProps, &lpaProps))) { DebugTraceResult( TEXT("ParseEmail:GetProps(DL)\n"), hResult); // No property, not fatal. // allocate a buffer if (sc = MAPIAllocateBuffer(iemMax * sizeof(SPropValue), &lpaProps)) { DebugTrace( TEXT("ParseEmail:MAPIAllocateBuffer -> %x\n"), sc); sc = ResultFromScode(sc); goto exit; } // fill in with prop errors lpaProps[iemPR_EMAIL_ADDRESS].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_EMAIL_ADDRESS)); lpaProps[iemPR_ADDRTYPE].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_ADDRTYPE)); lpaProps[iemPR_CONTACT_EMAIL_ADDRESSES].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_CONTACT_EMAIL_ADDRESSES)); lpaProps[iemPR_CONTACT_ADDRTYPES].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_CONTACT_ADDRTYPES)); lpaProps[iemPR_CONTACT_DEFAULT_ADDRESS_INDEX].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(PR_CONTACT_DEFAULT_ADDRESS_INDEX)); } if (lpvcpf->fTYPE_INTERNET) { // default } else if (lpvcpf->fTYPE_MSN) { // convert to SMTP // Allocate a new, longer string DWORD cchSize = (lstrlenA(lpData) + 1 + cbMSNpostfix); if (sc = MAPIAllocateBuffer((cchSize * sizeof(lpTemp[0])), &lpTemp)) { DebugTrace( TEXT("ParseEmail:MAPIAllocateBuffer -> %x\n"), sc); hResult = ResultFromScode(sc); goto exit; } // append the msn site StrCpyNA(lpTemp, lpData, cchSize); StrCatBuffA(lpTemp, szMSNpostfix, cchSize); lpEmailAddress = lpTemp; } else if (lpvcpf->fTYPE_CIS) { // convert to SMTP // Allocate a new, longer string DWORD cchSize2 = (lstrlenA(lpData) + 1 + cbCOMPUSERVEpostfix); if (sc = MAPIAllocateBuffer((cchSize2 * sizeof(lpTemp[0])), &lpTemp)) { DebugTrace( TEXT("ParseEmail:MAPIAllocateBuffer -> %x\n"), sc); hResult = ResultFromScode(sc); goto exit; } // append the MSN site StrCpyNA(lpTemp, lpData, cchSize2); StrCatBuffA(lpTemp, szCOMPUSERVEpostfix, cchSize2); // I need to convert the ',' to a '.' lpEmailAddress = lpTemp; while (*lpTemp) { if (*lpTemp == ',') { *lpTemp = '.'; break; // should only be one comma } lpTemp = CharNextA(lpTemp); } lpTemp = lpEmailAddress; } else if (lpvcpf->fTYPE_AOL) { // convert to SMTP // Allocate a new, longer string DWORD cchSize3 = (lstrlenA(lpData) + 1 + cbAOLpostfix); if (sc = MAPIAllocateBuffer((cchSize3 * sizeof(lpTemp[0])), &lpTemp)) { DebugTrace( TEXT("ParseEmail:MAPIAllocateBuffer -> %x\n"), sc); hResult = ResultFromScode(sc); goto exit; } // append the AOL site StrCpyNA(lpTemp, lpData, cchSize3); StrCatBuffA(lpTemp, szAOLpostfix, cchSize3); lpEmailAddress = lpTemp; } // Don't know of any mappings to SMTP for these: else if (lpvcpf->fTYPE_X400) { // Mark as X400 lpAddrType = vctTable[VCARD_TYPE_X400]; } else if (lpvcpf->fTYPE_ATTMAIL) { // Mark as ATTMAIL lpAddrType = vctTable[VCARD_TYPE_ATTMAIL]; } else if (lpvcpf->fTYPE_EWORLD) { // Mark as EWORLD lpAddrType = vctTable[VCARD_TYPE_EWORLD]; } else if (lpvcpf->fTYPE_IBMMAIL) { // Mark as IBMMAIL lpAddrType = vctTable[VCARD_TYPE_IBMMAIL]; } else if (lpvcpf->fTYPE_MCIMAIL) { // Mark as MCIMAIL lpAddrType = vctTable[VCARD_TYPE_MCIMAIL]; } else if (lpvcpf->fTYPE_POWERSHARE) { // Mark as POWERSHARE lpAddrType = vctTable[VCARD_TYPE_POWERSHARE]; } else if (lpvcpf->fTYPE_PRODIGY) { // Mark as PRODIGY lpAddrType = vctTable[VCARD_TYPE_PRODIGY]; // // Telex Number should go in PR_TELEX_NUMBER // } else if (lpvcpf->fTYPE_TLX) { // // Mark as TLX // lpAddrType = vctTable[VCARD_TYPE_TLX]; } lpEmailAddressW = ConvertAtoW(lpEmailAddress); lpAddrTypeW = ConvertAtoW(lpAddrType); if (hResult = AddPropToMVPString(lpaProps, ulcProps, iemPR_CONTACT_EMAIL_ADDRESSES, lpEmailAddressW)) { goto exit; } if (hResult = AddPropToMVPString(lpaProps, ulcProps, iemPR_CONTACT_ADDRTYPES, lpAddrTypeW)) { goto exit; } // Is this the default email address? if (lpvcpf->fTYPE_PREF || lpvcs->ulEmailAddrs == 0) { lpaProps[iemPR_CONTACT_DEFAULT_ADDRESS_INDEX].ulPropTag = PR_CONTACT_DEFAULT_ADDRESS_INDEX; lpaProps[iemPR_CONTACT_DEFAULT_ADDRESS_INDEX].Value.l = lpvcs->ulEmailAddrs; lpaProps[iemPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS; lpaProps[iemPR_EMAIL_ADDRESS].Value.LPSZ = lpEmailAddressW; lpaProps[iemPR_ADDRTYPE].ulPropTag = PR_ADDRTYPE; lpaProps[iemPR_ADDRTYPE].Value.LPSZ = lpAddrTypeW; } else { ulcProps = 2; // contact addresses and contact addrtypes } lpvcs->ulEmailAddrs++; if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, ulcProps, lpaProps, NULL))) { DebugTrace( TEXT("ParseEmail:SetProps -> %x\n"), GetScode(hResult)); } } exit: FreeBufferAndNull(&lpaProps); FreeBufferAndNull(&lpTemp); LocalFreeAndNull(&lpAddrTypeW); LocalFreeAndNull(&lpEmailAddressW); return(hResult); } /*************************************************************************** Name : ParseBday Purpose : parse the birthday from string into FileTime Parameters: lpvcpf -> parameter flags lpData = data string lpMailUser -> output mailuser object Returns : hResult Comment : ***************************************************************************/ HRESULT ParseBday(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpDataA, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; SPropValue spv[1] = {0}; SYSTEMTIME st = {0}; TCHAR sz[32]; LPTSTR lpTmp = NULL; LPTSTR lpData = ConvertAtoW(lpDataA); // Birthday can be in 2 formats: // basic ISO 8601: YYYYMMDD // or // extended ISO 8601: YYYY-MM-DDTHH-MM-SSetc // // we'll assume that if the strlen == 8, then it is basic // if (lpData && *lpData && (lstrlen(lpData) >= 8)) { StrCpyN(sz, lpData,ARRAYSIZE(sz)); sz[31] = '\0'; if(lstrlen(lpData) == 8) //basic ISO 8601 { lpTmp = &(sz[6]); st.wDay = (WORD) my_atoi(lpTmp); *lpTmp = '\0'; lpTmp = &(sz[4]); st.wMonth = (WORD) my_atoi(lpTmp); *lpTmp = '\0'; st.wYear = (WORD) my_atoi(sz); } else //extended ISO 8601 { sz[10]='\0'; lpTmp = &(sz[8]); st.wDay = (WORD) my_atoi(lpTmp); sz[7]='\0'; lpTmp = &(sz[5]); st.wMonth = (WORD) my_atoi(lpTmp); sz[4]='\0'; st.wYear = (WORD) my_atoi(sz); } SystemTimeToFileTime(&st, &(spv[0].Value.ft)); spv[0].ulPropTag = PR_BIRTHDAY; if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, 1, spv, NULL))) { DebugTrace( TEXT("ParseBday(0x%08x):SetProps -> %x\n"), PR_BIRTHDAY, GetScode(hResult)); } } LocalFreeAndNull(&lpData); return(hResult); } /*************************************************************************** Name : ParseSimple Purpose : parse the simple text prop into the property Parameters: lpvcpf -> parameter flags lpData = data string lpMailUser -> output mailuser object ulPropTag = property to save Returns : hResult Comment : ***************************************************************************/ HRESULT ParseSimple(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser, ULONG ulPropTag) { HRESULT hResult = hrSuccess; SPropValue spv[1] = {0}; if (lpData && *lpData) { spv[0].ulPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_STRING8); spv[0].Value.lpszA= lpData; if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, 1, spv, NULL))) { DebugTrace( TEXT("ParseSimple(0x%08x):SetProps -> %x\n"), ulPropTag, GetScode(hResult)); } } return(hResult); } /*************************************************************************** Name : InterpretVCardEncoding Purpose : Recognize the VCard encoding and set the flags Parameters: lpType = encoding string lpvcpf -> flags structure to fill in Returns : HRESULT Comment : ***************************************************************************/ HRESULT InterpretVCardEncoding(LPSTR lpEncoding, LPVCARD_PARAM_FLAGS lpvcpf) { HRESULT hResult = hrSuccess; if (*lpEncoding) { // what is it? switch (RecognizeVCardEncoding(lpEncoding)) { case VCARD_ENCODING_NONE: break; case VCARD_ENCODING_QUOTED_PRINTABLE: lpvcpf->fENCODING_QUOTED_PRINTABLE = TRUE; break; case VCARD_ENCODING_BASE64: lpvcpf->fENCODING_BASE64 = TRUE; break; case VCARD_ENCODING_7BIT: lpvcpf->fENCODING_7BIT = TRUE; break; default: // Assert(FALSE); break; } } return(hResult); } /*************************************************************************** Name : InterpretVCardType Purpose : Recognize the VCard type and set the flags Parameters: lpType = type string lpvcpf -> flags structure to fill in Returns : HRESULT Comment : ***************************************************************************/ HRESULT InterpretVCardType(LPSTR lpType, LPVCARD_PARAM_FLAGS lpvcpf) { HRESULT hResult = hrSuccess; if (*lpType) { // what is it? switch (RecognizeVCardType(lpType)) { case VCARD_TYPE_DOM: lpvcpf->fTYPE_DOM = TRUE; break; case VCARD_TYPE_INTL: lpvcpf->fTYPE_INTL = TRUE; break; case VCARD_TYPE_POSTAL: lpvcpf->fTYPE_POSTAL = TRUE; break; case VCARD_TYPE_PARCEL: lpvcpf->fTYPE_PARCEL = TRUE; break; case VCARD_TYPE_HOME: lpvcpf->fTYPE_HOME = TRUE; break; case VCARD_TYPE_WORK: lpvcpf->fTYPE_WORK = TRUE; break; case VCARD_TYPE_PREF: lpvcpf->fTYPE_PREF = TRUE; break; case VCARD_TYPE_VOICE: lpvcpf->fTYPE_VOICE = TRUE; break; case VCARD_TYPE_FAX: lpvcpf->fTYPE_FAX = TRUE; break; case VCARD_TYPE_MSG: lpvcpf->fTYPE_MSG = TRUE; break; case VCARD_TYPE_CELL: lpvcpf->fTYPE_CELL = TRUE; break; case VCARD_TYPE_PAGER: lpvcpf->fTYPE_PAGER = TRUE; break; case VCARD_TYPE_BBS: lpvcpf->fTYPE_BBS = TRUE; break; case VCARD_TYPE_MODEM: lpvcpf->fTYPE_MODEM = TRUE; break; case VCARD_TYPE_CAR: lpvcpf->fTYPE_CAR = TRUE; break; case VCARD_TYPE_ISDN: lpvcpf->fTYPE_ISDN = TRUE; break; case VCARD_TYPE_VIDEO: lpvcpf->fTYPE_VIDEO = TRUE; break; case VCARD_TYPE_AOL: lpvcpf->fTYPE_AOL = TRUE; break; case VCARD_TYPE_APPLELINK: lpvcpf->fTYPE_APPLELINK = TRUE; break; case VCARD_TYPE_ATTMAIL: lpvcpf->fTYPE_ATTMAIL = TRUE; break; case VCARD_TYPE_CIS: lpvcpf->fTYPE_CIS = TRUE; break; case VCARD_TYPE_EWORLD: lpvcpf->fTYPE_EWORLD = TRUE; break; case VCARD_TYPE_INTERNET: lpvcpf->fTYPE_INTERNET = TRUE; break; case VCARD_TYPE_IBMMAIL: lpvcpf->fTYPE_IBMMAIL = TRUE; break; case VCARD_TYPE_MSN: lpvcpf->fTYPE_MSN = TRUE; break; case VCARD_TYPE_MCIMAIL: lpvcpf->fTYPE_MCIMAIL = TRUE; break; case VCARD_TYPE_POWERSHARE: lpvcpf->fTYPE_POWERSHARE = TRUE; break; case VCARD_TYPE_PRODIGY: lpvcpf->fTYPE_PRODIGY = TRUE; break; case VCARD_TYPE_TLX: lpvcpf->fTYPE_TLX = TRUE; break; case VCARD_TYPE_X400: lpvcpf->fTYPE_X400 = TRUE; break; case VCARD_TYPE_GIF: lpvcpf->fTYPE_GIF = TRUE; break; case VCARD_TYPE_CGM: lpvcpf->fTYPE_CGM = TRUE; break; case VCARD_TYPE_WMF: lpvcpf->fTYPE_WMF = TRUE; break; case VCARD_TYPE_BMP: lpvcpf->fTYPE_BMP = TRUE; break; case VCARD_TYPE_MET: lpvcpf->fTYPE_MET = TRUE; break; case VCARD_TYPE_PMB: lpvcpf->fTYPE_PMB = TRUE; break; case VCARD_TYPE_DIB: lpvcpf->fTYPE_DIB = TRUE; break; case VCARD_TYPE_PICT: lpvcpf->fTYPE_PICT = TRUE; break; case VCARD_TYPE_TIFF: lpvcpf->fTYPE_TIFF = TRUE; break; case VCARD_TYPE_ACROBAT: lpvcpf->fTYPE_ACROBAT = TRUE; break; case VCARD_TYPE_PS: lpvcpf->fTYPE_PS = TRUE; break; case VCARD_TYPE_JPEG: lpvcpf->fTYPE_JPEG = TRUE; break; case VCARD_TYPE_QTIME: lpvcpf->fTYPE_QTIME = TRUE; break; case VCARD_TYPE_MPEG: lpvcpf->fTYPE_MPEG = TRUE; break; case VCARD_TYPE_MPEG2: lpvcpf->fTYPE_MPEG2 = TRUE; break; case VCARD_TYPE_AVI: lpvcpf->fTYPE_AVI = TRUE; break; case VCARD_TYPE_WAVE: lpvcpf->fTYPE_WAVE = TRUE; break; case VCARD_TYPE_AIFF: lpvcpf->fTYPE_AIFF = TRUE; break; case VCARD_TYPE_PCM: lpvcpf->fTYPE_PCM = TRUE; break; case VCARD_TYPE_X509: lpvcpf->fTYPE_X509 = TRUE; break; case VCARD_TYPE_PGP: lpvcpf->fTYPE_PGP = TRUE; break; case VCARD_TYPE_NONE: // Wasn't a TYPE, try an encoding hResult = InterpretVCardEncoding(lpType, lpvcpf); break; default: // Assert(FALSE); break; } } return(hResult); } /*************************************************************************** Name : ParseVCardParams Purpose : Parse out the vCard's parameters Parameters: lpBuffer = option string lpvcpf -> flags structure to fill in Returns : HRESULT Comment : Assumes lpvcpf is initialized to all false. ***************************************************************************/ HRESULT ParseVCardParams(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf) { TCHAR ch; LPSTR lpOption, lpArgs; BOOL fReady; HRESULT hResult = hrSuccess; // Is there anything here? if (lpBuffer) { while (*lpBuffer) { fReady = FALSE; lpOption = lpBuffer; lpArgs = NULL; while ((ch = *lpBuffer) && ! fReady) { switch (ch) { case ';': // end of this param *lpBuffer = '\0'; fReady = TRUE; break; case '=': // found a param with args if (! lpArgs) { lpArgs = lpBuffer + 1; *lpBuffer = '\0'; } break; default: // normal character break; } lpBuffer++; } if (*lpOption) { // what is it? switch (RecognizeVCardParam(lpOption)) { case VCARD_PARAM_TYPE: if (lpArgs) { ParseVCardType(lpArgs, lpvcpf); } break; case VCARD_PARAM_ENCODING: if (lpArgs) { ParseVCardEncoding(lpArgs, lpvcpf); } break; case VCARD_PARAM_LANGUAGE: lpvcpf->szPARAM_LANGUAGE = lpArgs; break; case VCARD_PARAM_CHARSET: lpvcpf->szPARAM_CHARSET = lpArgs; break; case VCARD_PARAM_VALUE: // BUGBUG: Should reject those that we can't process (like URL) break; case VCARD_PARAM_NONE: if (hResult = InterpretVCardType(lpOption, lpvcpf)) { goto exit; } break; default: break; } } } } exit: return(hResult); } /*************************************************************************** Name : ParseOrg Purpose : parse the organization into properites Parameters: lpvcpf -> lpData = data string lpMailUser -> output mailuser object Returns : hResult Comment : vCard organizations are of the form: TEXT("organization; org unit; org unit; ...") ***************************************************************************/ HRESULT ParseOrg(LPVCARD_PARAM_FLAGS lpvcpf, LPSTR lpData, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; SPropValue spv[2] = {0}; register LPSTR lpCurrent; ULONG i = 0; // Look for Organization (company) if (lpData && *lpData) { lpCurrent = lpData; lpData = ParseWord(lpData, ';'); if (*lpCurrent) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_COMPANY_NAME, PT_STRING8); spv[i].Value.lpszA= lpCurrent; i++; } } // Everything else goes into PR_DEPARTMENT_NAME if (lpData && *lpData) { spv[i].ulPropTag = CHANGE_PROP_TYPE(PR_DEPARTMENT_NAME, PT_STRING8); spv[i].Value.lpszA= lpData; i++; } if (i) { if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, i, spv, NULL))) { DebugTrace( TEXT("ParseName:SetProps -> %x\n"), GetScode(hResult)); } } return(hResult); } /*************************************************************************** Name : ParseVCardType Purpose : Parse out the vCard's type parameter Parameters: lpBuffer = type string lpvcpf -> flags structure to fill in Returns : HRESULT Comment : ***************************************************************************/ HRESULT ParseVCardType(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf) { TCHAR ch; BOOL fReady; LPSTR lpType; HRESULT hResult = hrSuccess; // Is there anything here? if (lpBuffer) { while (*lpBuffer) { fReady = FALSE; lpType = lpBuffer; while ((ch = *lpBuffer) && ! fReady) { switch (ch) { case ',': // end of this type *lpBuffer = '\0'; fReady = TRUE; break; default: // normal character break; } lpBuffer++; } hResult = InterpretVCardType(lpType, lpvcpf); } } return(hResult); } /*************************************************************************** Name : ParseVCardEncoding Purpose : Parse out the vCard encoding parameter Parameters: lpBuffer = type string lpvcpf -> flags structure to fill in Returns : HRESULT Comment : ***************************************************************************/ HRESULT ParseVCardEncoding(LPSTR lpBuffer, LPVCARD_PARAM_FLAGS lpvcpf) { TCHAR ch; BOOL fReady; LPSTR lpEncoding; HRESULT hResult = hrSuccess; // Is there anything here? if (lpBuffer) { while (*lpBuffer) { fReady = FALSE; lpEncoding = lpBuffer; while ((ch = *lpBuffer) && ! fReady) { switch (ch) { case ',': // end of this type *lpBuffer = '\0'; fReady = TRUE; break; default: // normal character break; } lpBuffer++; } hResult = InterpretVCardEncoding(lpEncoding, lpvcpf); } } return(hResult); } /*************************************************************************** Name : Base64DMap Purpose : Decoding mapping for Base64 Parameters: chIn = character from Base64 encoding Returns : 6-bit value represented by chIn. Comment : ***************************************************************************/ /* this function is not necessary any more because of the DecodeBase64 function UCHAR Base64DMap(UCHAR chIn) { UCHAR chOut; // 'A' -> 0, 'B' -> 1, ... 'Z' -> 25 if (chIn >= 'A' && chIn <= 'Z') { chOut = chIn - 'A'; } else if (chIn >= 'a' && chIn <= 'z') { // 'a' -> 26 chOut = (chIn - 'a') + 26; } else if (chIn >= '0' && chIn <= '9') { // '0' -> 52 chOut = (chIn - '0') + 52; } else if (chIn == '+') { chOut = 62; } else if (chIn == '/') { chOut = 63; } else { // uh oh Assert(FALSE); chOut = 0; } return(chOut); } */ /*************************************************************************** Name : DecodeVCardData Purpose : Decode QUOTED_PRINTABLE or BASE64 data Parameters: lpData = data string cbData = the length of the decoded data string // changed t-jstaj lpvcs -> state variable Returns : hResult Comment : Decode in place is possible since both encodings are guaranteed to take at least as much space as the original data. ***************************************************************************/ HRESULT DecodeVCardData(LPSTR lpData, PULONG cbData, LPVCARD_PARAM_FLAGS lpvcpf) { HRESULT hResult = hrSuccess; LPSTR lpTempIn = lpData; LPSTR lpTempOut = lpData; char chIn, chOut; char chA, chB, chC, chD; if (lpvcpf->fENCODING_QUOTED_PRINTABLE) { // Look for '=', this is the escape character for QP while (chIn = *lpTempIn) { if (chIn == '=') { chIn = *(++lpTempIn); // is it a soft line break or a hex character? if (chIn == '\n' || chIn == '\r') { // Soft line break while (chIn && (chIn == '\n' || chIn == '\r')) { chIn = *(++lpTempIn); } continue; // We're now pointing to next good data or NULL } else { // hex encoded char // high nibble if (chIn >= '0' && chIn <= '9') { chOut = (chIn - '0') << 4; } else if (chIn >= 'A' && chIn <= 'F') { chOut = ((chIn - 'A') + 10) << 4; } else if (chIn >= 'a' && chIn <= 'f') { chOut = ((chIn - 'a') + 10) << 4; } else { // bogus QUOTED_PRINTABLE data // Cut it short right here. break; } chIn = *(++lpTempIn); // low nibble if (chIn >= '0' && chIn <= '9') { chOut |= (chIn - '0'); } else if (chIn >= 'A' && chIn <= 'F') { chOut |= ((chIn - 'A') + 10); } else if (chIn >= 'a' && chIn <= 'f') { chOut |= ((chIn - 'a') + 10); } else { // bogus QUOTED_PRINTABLE data // Cut it short right here. break; } } } else { chOut = chIn; } *(lpTempOut++) = chOut; lpTempIn++; } *lpTempOut = '\0'; // terminate it } else if (lpvcpf->fENCODING_BASE64) { // eliminate white spaces LPSTR lpTempCopyPt; for( lpTempCopyPt = lpTempIn = lpData; lpTempIn && *lpTempIn; lpTempCopyPt++, lpTempIn++ ) { while( /*IsSpace(lpTempIn)*/ *lpTempIn == ' ' || *lpTempIn == '\t') lpTempIn++; if( lpTempCopyPt != lpTempIn ) *(lpTempCopyPt) = *(lpTempIn); } *(lpTempCopyPt) = '\0'; lpTempIn = lpData; lpTempOut = lpData; if( HR_FAILED(hResult = DecodeBase64(lpTempIn, lpTempOut, cbData) ) ) { DebugTrace( TEXT("couldn't decode buffer\n")); } /** This is the original code for vcard decoding base64, however, it wasn't working so new decoding is all done within DecodeBase64 function. lpTempIn = lpData; lpTempOut = lpData; while (*lpTempIn) { chA = Base64DMap(*(PUCHAR)(lpTempIn)++); if (! (chB = Base64DMap(*(PUCHAR)(lpTempIn)++) )) { chC = chD = 0; } else if (chC = Base64DMap(*(PUCHAR)(lpTempIn)++)) { chD = 0; } else { chD = Base64DMap(*(PUCHAR)(lpTempIn)++); } // chA = high 6 bits // chD = low 6 bits *(lpTempOut++) = (chA << 0x02) | ((chB & 0x60) >> 6); *(lpTempOut++) = ((chB & 0x0F) << 4) | ((chC & 0x3B) >> 2); *(lpTempOut++) = ((chC & 0x03) << 6) | (chD & 0x3F); } *lpTempOut = '\0'; // terminate it */ } return(hResult); } /*************************************************************************** Name : InterpretVCardItem Purpose : Recognize the vCard item Parameters: lpName = property name lpOption = option string lpData = data string lpMailUser -> output mailuser object lpvcs -> state variable Returns : hResult Comment : Expects the keyword to be in the current line (lpBuffer), but may read more lines as necesary to get a complete item. ***************************************************************************/ HRESULT InterpretVCardItem(LPSTR lpName, LPSTR lpOption, LPSTR lpData, LPMAILUSER lpMailUser, LPEXTVCARDPROP lpList, LPVC_STATE lpvcs) { HRESULT hResult = hrSuccess; VCARD_PARAM_FLAGS vcpf = {0}; ULONG cbData = 0; ParseVCardParams(lpOption, &vcpf); #if 0 #ifdef DEBUG if(lstrcmpiA(lpName, "KEY")) { LPTSTR lpW1 = ConvertAtoW(lpName); LPTSTR lpW2 = ConvertAtoW(lpData); DebugTrace( TEXT("%s:%s\n"), lpW1, lpW2); LocalFreeAndNull(&lpW1); LocalFreeAndNull(&lpW2); } else DebugTrace(TEXT("KEY:\n")); #endif #endif if (hResult = DecodeVCardData(lpData, &cbData, &vcpf)) { goto exit; } switch (RecognizeVCardKeyWord(lpName)) { case VCARD_KEY_VCARD: hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); break; case VCARD_KEY_BEGIN: if (lpvcs->vce != VCS_INITIAL) { // uh oh, already saw BEGIN hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); } else { switch (RecognizeVCardKeyWord(lpData)) { case VCARD_KEY_VCARD: lpvcs->vce = VCS_ITEMS; break; default: lpvcs->vce = VCS_ERROR; hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); break; } } break; case VCARD_KEY_END: if (lpvcs->vce != VCS_ITEMS) { // uh oh, haven't seen begin yet hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); } else { switch (RecognizeVCardKeyWord(lpData)) { case VCARD_KEY_VCARD: lpvcs->vce = VCS_FINISHED; break; default: lpvcs->vce = VCS_ERROR; hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); break; } } break; case VCARD_KEY_N: // structured name // Data: surname; given name; middle name; prefix; suffix hResult = ParseName(&vcpf, lpData, lpMailUser); break; case VCARD_KEY_ORG: // organization info // Data: company name; org unit; org unit; ... hResult = ParseOrg(&vcpf, lpData, lpMailUser); break; case VCARD_KEY_ADR: // Data: TEXT("PO box; extended addr; street addr; city; region; postal code; country") // Option: DOM; INTL; POSTAL; PARCEL; HOME; WORK; PREF; CHARSET; LANGUAGE hResult = ParseAdr(&vcpf, lpData, lpMailUser); break; case VCARD_KEY_TEL: // Data: canonical form phone number // Options: HOME, WORK, MSG, PREF, FAX, CELL, PAGER, VIDEO, BBS, MODEM, ISDN hResult = ParseTel(&vcpf, lpData, lpMailUser); break; case VCARD_KEY_TITLE: // Data: job title // Options: CHARSET, LANGUAGE hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_TITLE); break; case VCARD_KEY_NICKNAME: // Data: job title // Options: CHARSET, LANGUAGE hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_NICKNAME); break; case VCARD_KEY_URL: // Data: URL // Options: none (though we'd like to see HOME, WORK) if (vcpf.fTYPE_HOME) { hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_PERSONAL_HOME_PAGE); lpvcs->fPersonalURL = TRUE; } else if (vcpf.fTYPE_WORK) { hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_BUSINESS_HOME_PAGE); lpvcs->fBusinessURL = TRUE; } else if (! lpvcs->fPersonalURL) { // assume it is HOME page hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_PERSONAL_HOME_PAGE); lpvcs->fPersonalURL = TRUE; } else if (! lpvcs->fBusinessURL) { // assume it is BUSINESS web page hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_BUSINESS_HOME_PAGE); lpvcs->fBusinessURL = TRUE; } // else, toss it break; case VCARD_KEY_NOTE: // Data: note text // Options: CHARSET, LANGUAGE hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_COMMENT); break; case VCARD_KEY_FN: hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_DISPLAY_NAME); break; case VCARD_KEY_EMAIL: // since we are forcibly putting the telex value into the EMAIL type, // we also need to be able to get it out of there if(vcpf.fTYPE_TLX) hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_TELEX_NUMBER); else hResult = ParseEmail(&vcpf, lpData, lpMailUser, lpvcs); break; case VCARD_KEY_ROLE: hResult = ParseSimple(&vcpf, lpData, lpMailUser, PR_PROFESSION); break; case VCARD_KEY_BDAY: hResult = ParseBday(&vcpf, lpData, lpMailUser); break; case VCARD_KEY_AGENT: case VCARD_KEY_LOGO: case VCARD_KEY_PHOTO: case VCARD_KEY_LABEL: case VCARD_KEY_FADR: case VCARD_KEY_SOUND: case VCARD_KEY_LANG: case VCARD_KEY_TZ: case VCARD_KEY_GEO: case VCARD_KEY_REV: case VCARD_KEY_UID: case VCARD_KEY_MAILER: // Not yet implemented: ignore #ifdef DEBUG { LPTSTR lpW = ConvertAtoW(lpName); DebugTrace( TEXT("===>>> NYI: %s\n"), lpW); LocalFreeAndNull(&lpW); } #endif break; case VCARD_KEY_KEY: { hResult = ParseCert( lpData, cbData, lpMailUser); break; } case VCARD_KEY_X_WAB_GENDER: { SPropValue spv[1] = {0}; if (lpData ) { INT fGender = (INT)lpData[0] - '0'; if( fGender < 0 || fGender > 2 ) fGender = 0; spv[0].Value.l = fGender; spv[0].ulPropTag = PR_GENDER; if (HR_FAILED(hResult = lpMailUser->lpVtbl->SetProps(lpMailUser, 1, spv, NULL))) { DebugTrace( TEXT("could not set props\n")); } } break; } case VCARD_KEY_X: case VCARD_KEY_NONE: // // Check if this is an X- named prop that we might care about // if(lpList) { LPEXTVCARDPROP lpTemp = lpList; while( lpTemp && lpTemp->ulExtPropTag && lpTemp->lpszExtPropName && lstrlenA(lpTemp->lpszExtPropName) ) { if(!lstrcmpiA(lpName, lpTemp->lpszExtPropName)) { hResult = ParseSimple(&vcpf, lpData, lpMailUser, lpTemp->ulExtPropTag); break; } lpTemp = lpTemp->lpNext; } } #ifdef DEBUG { LPTSTR lpW = ConvertAtoW(lpName); DebugTrace( TEXT("Unrecognized or extended vCard key %s\n"), lpW); LocalFreeAndNull(&lpW); } #endif //debug break; default: // Assert(FALSE); break; } if (lpvcs->vce == VCS_INITIAL) { // We are still in initial state. This is not a vCard. hResult = ResultFromScode(MAPI_E_INVALID_OBJECT); } exit: return(hResult); } /*************************************************************************** Name : ReadLn Purpose : Read a line from the handle Parameters: handle = open file handle ReadFn = function to read from handle lppLine -> returned pointer to this line's read into. lpcbItem -> [in] size of data in lppBuffer. [out] returned size of data in lppBuffer. If zero, there is no more data. (Does not include terminating NULL) lppBuffer -> [in] start of item buffer or NULL if none yet. [out] start of allocated item buffer. Caller must LocalFree this buffer once the item is read in. lpcbBuffer -> [in/out] size of lppBuffer allocation. Returns : hResult: 0 on no error (recognized) Comment : Reads a line from the handle, discarding any carriage return characters and empty lines. Will not overwrite buffer, and will always terminate the string with a null. Trims trailing white space. This is very inefficient since we're reading a byte at a time. I think we can get away with it since vCards are typically small. If not, we'll have to do some read caching. ***************************************************************************/ #define READ_BUFFER_GROW 256 HRESULT ReadLn(HANDLE hVCard, VCARD_READ ReadFn, LPSTR * lppLine, LPULONG lpcbItem, LPSTR * lppBuffer, LPULONG lpcbBuffer) { HRESULT hResult = hrSuccess; LPSTR lpBuffer = *lppBuffer; LPSTR lpBufferTemp; register LPSTR lpRead = NULL; ULONG cbRead; ULONG cbBuffer; char ch; ULONG cbItem; ULONG cbStart = 0; if (! lpBuffer) { cbBuffer = READ_BUFFER_GROW; cbItem = 0; if (! (lpBuffer = LocalAlloc(LPTR, cbBuffer))) { DebugTrace( TEXT("ReadLn:LocalAlloc -> %u\n"), GetLastError()); hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } } else { cbBuffer = *lpcbBuffer; cbItem = *lpcbItem; // Make certain we have room for at least one more character. if (cbItem >= cbBuffer) { // Time to grow the buffer cbBuffer += READ_BUFFER_GROW; if (! (lpRead = LocalReAlloc(lpBuffer, cbBuffer, LMEM_MOVEABLE | LMEM_ZEROINIT))) { DebugTrace( TEXT("ReadLn:LocalReAlloc(%u) -> %u\n"), cbBuffer, GetLastError()); goto exit; } lpBuffer = lpRead; } } cbStart = cbItem; lpRead = lpBuffer + cbItem; // read pointer do { // read next character if (hResult = ReadFn(hVCard, lpRead, 1, &cbRead)) { goto exit; } if (! cbRead) { // End of file *lpRead = '\0'; // eol goto exit; } else { // Assert(cbRead == 1); ch = *lpRead; switch (ch) { case '\r': // These are ignored break; case '\n': // Linefeed terminates string *lpRead = '\0'; // eol break; default: // All other characters are added to string cbItem += cbRead; if (cbItem >= cbBuffer) { // Time to grow the buffer cbBuffer += READ_BUFFER_GROW; lpBufferTemp = (LPSTR)LocalReAlloc(lpBuffer, cbBuffer, LMEM_MOVEABLE | LMEM_ZEROINIT); if (!lpBufferTemp) { DebugTrace( TEXT("ReadLn:LocalReAlloc(%u) -> %u\n"), cbBuffer, GetLastError()); hResult = E_OUTOFMEMORY; goto exit; } else { lpBuffer = lpBufferTemp; } lpRead = lpBuffer + cbItem; } else { lpRead++; } break; } } } while (ch != '\n'); exit: *lppLine = &lpBuffer[cbStart]; if (hResult || cbItem == 0) { LocalFreeAndNull(&lpBuffer); cbItem = 0; lpBuffer = NULL; } else { // If we didn't read anything more, we should return NULL in lppLine. if (cbItem == cbStart) { *lppLine = NULL; } else { // DebugTrace( TEXT("ReadLn: \")%s\ TEXT("\n"), *lppLine); } } *lpcbItem = cbItem; *lppBuffer = lpBuffer; *lpcbBuffer = cbBuffer; return(hResult); } /*************************************************************************** Name : FindSubstringBefore Purpose : Find a substring before a particular character Parameters: lpString = full string lpSubstring = search string chBefore = character to terminate search Returns : pointer to substring or NULL if not found Comment : ***************************************************************************/ LPSTR FindSubstringBefore(LPSTR lpString, LPSTR lpSubstring, char chBefore) { ULONG cbSubstring = lstrlenA(lpSubstring); register ULONG i; BOOL fFound = FALSE; char szU[MAX_PATH]; char szL[MAX_PATH]; StrCpyNA(szU, lpSubstring, ARRAYSIZE(szU)); StrCpyNA(szL, lpSubstring, ARRAYSIZE(szL)); CharUpperA(szU); CharLowerA(szL); while (*lpString && *lpString != chBefore) { for (i = 0; i < cbSubstring; i++) { if (lpString[i] != szU[i] && lpString[i] != szL[i]) { goto nomatch; } } return(lpString); nomatch: lpString++; } return(NULL); } /*************************************************************************** Name : ReadVCardItem Purpose : Read the next VCard item Parameters: handle = open file handle ReadFn = function to read from handle lppBuffer -> returned buffer containing the item. Caller must LocalFree this buffer. (on input, if this is non-NULL, the existing buffer should be used.) lpcbBuffer -> returned size of buffer. If zero, there is no more data. Returns : hResult: 0 on no error (recognized) Comment : Reads a vCard item from the handle, discarding any carriage return characters and empty lines. Will not overwrite buffer, and will always terminate the string with a null. Trims trailing white space. ***************************************************************************/ HRESULT ReadVCardItem(HANDLE hVCard, VCARD_READ ReadFn, LPSTR * lppBuffer, LPULONG lpcbBuffer) { HRESULT hResult; LPSTR lpLine = NULL; LPSTR lpBuffer = NULL; ULONG cbBuffer = 0; ULONG cbItem = 0; BOOL fDone = FALSE; BOOL fQuotedPrintable = FALSE; BOOL fBase64 = FALSE; BOOL fFirst = TRUE; ULONG cbStart; while (! fDone) { cbStart = cbItem; if (hResult = ReadLn(hVCard, ReadFn, &lpLine, &cbItem, &lpBuffer, &cbBuffer)) { if (HR_FAILED(hResult)) { DebugTrace( TEXT("ReadVCardItem: ReadLn -> %x\n"), GetScode(hResult)); } else if (GetScode(hResult) == WAB_W_END_OF_DATA) { // EOF // all } fDone = TRUE; } else { if (lpBuffer) { // Do we need to read more data? // Look for the following if (fFirst) { // look for the data type indications in the first line of the item. fQuotedPrintable = FindSubstringBefore(lpBuffer, (LPSTR)vceTable[VCARD_ENCODING_QUOTED_PRINTABLE], ':') ? TRUE : FALSE; fBase64 = FindSubstringBefore(lpBuffer, (LPSTR)vceTable[VCARD_ENCODING_BASE64], ':') ? TRUE : FALSE; fFirst = FALSE; } if (fQuotedPrintable) { // watch for soft line breaks (= before CRLF) if (lpBuffer[cbItem - 1] == '=') { // overwrite the soft break character cbItem--; lpBuffer[cbItem] = '\0'; } else { fDone = TRUE; } } else if (fBase64) { // looking for empty line if (cbStart == cbItem) { fDone = TRUE; } } else { fDone = TRUE; } } else { // BUG Fix - if we set fDone to true here, we will exit out of our // vCard reading loop. lpBuffer can also be NULL because the // vCard contained blank lines. Better we dont set fDone here. //fDone = TRUE; } } } if (! HR_FAILED(hResult)) { *lppBuffer = lpBuffer; if (lpBuffer) { TrimTrailingWhiteSpace(lpBuffer); } } return(hResult); } enum { ivcPR_GENERATION, ivcPR_GIVEN_NAME, ivcPR_SURNAME, ivcPR_NICKNAME, ivcPR_BUSINESS_TELEPHONE_NUMBER, ivcPR_HOME_TELEPHONE_NUMBER, ivcPR_LANGUAGE, ivcPR_POSTAL_ADDRESS, ivcPR_COMPANY_NAME, ivcPR_TITLE, ivcPR_DEPARTMENT_NAME, ivcPR_OFFICE_LOCATION, ivcPR_BUSINESS2_TELEPHONE_NUMBER, ivcPR_CELLULAR_TELEPHONE_NUMBER, ivcPR_RADIO_TELEPHONE_NUMBER, ivcPR_CAR_TELEPHONE_NUMBER, ivcPR_OTHER_TELEPHONE_NUMBER, ivcPR_DISPLAY_NAME, ivcPR_PAGER_TELEPHONE_NUMBER, ivcPR_BUSINESS_FAX_NUMBER, ivcPR_HOME_FAX_NUMBER, ivcPR_TELEX_NUMBER, ivcPR_ISDN_NUMBER, ivcPR_HOME2_TELEPHONE_NUMBER, ivcPR_MIDDLE_NAME, ivcPR_PERSONAL_HOME_PAGE, ivcPR_BUSINESS_HOME_PAGE, ivcPR_HOME_ADDRESS_CITY, ivcPR_HOME_ADDRESS_COUNTRY, ivcPR_HOME_ADDRESS_POSTAL_CODE, ivcPR_HOME_ADDRESS_STATE_OR_PROVINCE, ivcPR_HOME_ADDRESS_STREET, ivcPR_HOME_ADDRESS_POST_OFFICE_BOX, ivcPR_POST_OFFICE_BOX, ivcPR_BUSINESS_ADDRESS_CITY, ivcPR_BUSINESS_ADDRESS_COUNTRY, ivcPR_BUSINESS_ADDRESS_POSTAL_CODE, ivcPR_BUSINESS_ADDRESS_STATE_OR_PROVINCE, ivcPR_BUSINESS_ADDRESS_STREET, ivcPR_COMMENT, ivcPR_EMAIL_ADDRESS, ivcPR_ADDRTYPE, ivcPR_CONTACT_ADDRTYPES, ivcPR_CONTACT_DEFAULT_ADDRESS_INDEX, ivcPR_CONTACT_EMAIL_ADDRESSES, ivcPR_PROFESSION, ivcPR_BIRTHDAY, ivcPR_PRIMARY_TELEPHONE_NUMBER, ivcPR_OTHER_ADDRESS_CITY, ivcPR_OTHER_ADDRESS_COUNTRY, ivcPR_OTHER_ADDRESS_POSTAL_CODE, ivcPR_OTHER_ADDRESS_STATE_OR_PROVINCE, ivcPR_OTHER_ADDRESS_STREET, ivcPR_OTHER_ADDRESS_POST_OFFICE_BOX, ivcPR_DISPLAY_NAME_PREFIX, ivcPR_USER_X509_CERTIFICATE, ivcPR_GENDER, ivcMax }; const SizedSPropTagArray(ivcMax, tagaVCard) = { ivcMax, { PR_GENERATION, PR_GIVEN_NAME, PR_SURNAME, PR_NICKNAME, PR_BUSINESS_TELEPHONE_NUMBER, PR_HOME_TELEPHONE_NUMBER, PR_LANGUAGE, PR_POSTAL_ADDRESS, PR_COMPANY_NAME, PR_TITLE, PR_DEPARTMENT_NAME, PR_OFFICE_LOCATION, PR_BUSINESS2_TELEPHONE_NUMBER, PR_CELLULAR_TELEPHONE_NUMBER, PR_RADIO_TELEPHONE_NUMBER, PR_CAR_TELEPHONE_NUMBER, PR_OTHER_TELEPHONE_NUMBER, PR_DISPLAY_NAME, PR_PAGER_TELEPHONE_NUMBER, PR_BUSINESS_FAX_NUMBER, PR_HOME_FAX_NUMBER, PR_TELEX_NUMBER, PR_ISDN_NUMBER, PR_HOME2_TELEPHONE_NUMBER, PR_MIDDLE_NAME, PR_PERSONAL_HOME_PAGE, PR_BUSINESS_HOME_PAGE, PR_HOME_ADDRESS_CITY, PR_HOME_ADDRESS_COUNTRY, PR_HOME_ADDRESS_POSTAL_CODE, PR_HOME_ADDRESS_STATE_OR_PROVINCE, PR_HOME_ADDRESS_STREET, PR_HOME_ADDRESS_POST_OFFICE_BOX, PR_POST_OFFICE_BOX, PR_BUSINESS_ADDRESS_CITY, PR_BUSINESS_ADDRESS_COUNTRY, PR_BUSINESS_ADDRESS_POSTAL_CODE, PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE, PR_BUSINESS_ADDRESS_STREET, PR_COMMENT, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_CONTACT_ADDRTYPES, PR_CONTACT_DEFAULT_ADDRESS_INDEX, PR_CONTACT_EMAIL_ADDRESSES, PR_PROFESSION, PR_BIRTHDAY, PR_PRIMARY_TELEPHONE_NUMBER, PR_OTHER_ADDRESS_CITY, PR_OTHER_ADDRESS_COUNTRY, PR_OTHER_ADDRESS_POSTAL_CODE, PR_OTHER_ADDRESS_STATE_OR_PROVINCE, PR_OTHER_ADDRESS_STREET, PR_OTHER_ADDRESS_POST_OFFICE_BOX, PR_DISPLAY_NAME_PREFIX, PR_USER_X509_CERTIFICATE, PR_GENDER } }; HRESULT WriteOrExit(HANDLE hVCard, LPTSTR lpsz, VCARD_WRITE WriteFn) { LPSTR lpszA = NULL; HRESULT hr = S_OK; lpszA = ConvertWtoA(lpsz); hr = WriteFn(hVCard, lpszA, lstrlenA(lpszA), NULL); LocalFreeAndNull(&lpszA); return hr; } #define WRITE_OR_EXITW(string) {\ if (hResult = WriteOrExit(hVCard, string, WriteFn)) \ goto exit; \ } #define WRITE_OR_EXIT(string) {\ if (hResult = WriteFn(hVCard, string, lstrlenA(string), NULL)) \ goto exit; \ } HRESULT WriteValueOrExit(HANDLE hVCard, VCARD_WRITE WriteFn, LPBYTE data, ULONG size) { LPSTR lpszA = NULL; HRESULT hr = S_OK; if(!size) lpszA = ConvertWtoA((LPTSTR)data); hr = WriteVCardValue(hVCard, WriteFn, lpszA ? (LPBYTE)lpszA : data, size); LocalFreeAndNull(&lpszA); return hr; } #define WRITE_VALUE_OR_EXITW(data, size) {\ if (hResult = WriteValueOrExit(hVCard, WriteFn, (LPBYTE)data, size)) {\ goto exit;\ }\ } #define WRITE_VALUE_OR_EXIT(data, size) {\ if (hResult = WriteVCardValue(hVCard, WriteFn, (LPBYTE)data, size)) {\ goto exit;\ }\ } /*************************************************************************** Name : EncodeQuotedPrintable Purpose : Encodes QUOTED_PRINTABLE Parameters: lpBuffer -> input buffer Returns : encoded string buffer (must be LocalFree'd by caller) Comment : ***************************************************************************/ #define QUOTED_PRINTABLE_MAX_LINE 76 #define QP_LOWRANGE_MIN ' ' #define QP_LOWRANGE_MAX '<' #define QP_HIGHRANGE_MIN '>' #define QP_HIGHRANGE_MAX '~' LPSTR EncodeQuotedPrintable(LPBYTE lpInput) { LPSTR lpBuffer = NULL; register LPBYTE lpTempIn = lpInput; register LPSTR lpTempOut; ULONG cbBuffer = 0; ULONG cbLine; BYTE bOut; char ch; // How big must the buffer be? cbLine = 0; while (ch = *lpTempIn++) { if (ch == '\t' || (ch >= QP_LOWRANGE_MIN && ch <= QP_LOWRANGE_MAX) || (ch >= QP_HIGHRANGE_MIN && ch <= QP_HIGHRANGE_MAX)) { cbBuffer++; cbLine++; if (cbLine >= (QUOTED_PRINTABLE_MAX_LINE)) { // 1 chars would overshoot max, wrap here cbLine = 0; cbBuffer += 3; } } else { if (cbLine >= (QUOTED_PRINTABLE_MAX_LINE - 3)) { // 3 chars would overshoot max, wrap here cbLine = 0; cbBuffer += 3; } cbLine += 3; cbBuffer += 3; // TEXT("=xx") } } // BUGBUG: Should handle terminating spaces if (cbBuffer) { cbBuffer++; // Room for terminator if (lpBuffer = LocalAlloc(LPTR, sizeof(TCHAR)*cbBuffer)) { lpTempIn = lpInput; lpTempOut = lpBuffer; cbLine = 0; while (ch = *lpTempIn++) { if (ch == '\t' || (ch >= QP_LOWRANGE_MIN && ch <= QP_LOWRANGE_MAX) || (ch >= QP_HIGHRANGE_MIN && ch <= QP_HIGHRANGE_MAX)) { if (cbLine >= QUOTED_PRINTABLE_MAX_LINE) { // char would overshoot max, wrap here *(lpTempOut++) = '='; *(lpTempOut++) = '\r'; *(lpTempOut++) = '\n'; cbLine = 0; } *(lpTempOut++) = ch; cbLine++; } else { if (cbLine >= (QUOTED_PRINTABLE_MAX_LINE - 3)) { // 3 chars would overshoot max, wrap here *(lpTempOut++) = '='; *(lpTempOut++) = '\r'; *(lpTempOut++) = '\n'; cbLine = 0; } *(lpTempOut++) = '='; if ((bOut = ((ch & 0xF0) >> 4)) > 9) { *(lpTempOut++) = bOut + ('A' - 10); } else { *(lpTempOut++) = bOut + '0'; } if ((bOut = ch & 0x0F) > 9) { *(lpTempOut++) = bOut + ('A' - 10); } else { *(lpTempOut++) = bOut + '0'; } cbLine += 3; } } *lpTempOut = '\0'; // terminate the string } // else fail } return(lpBuffer); } /*************************************************************************** Name : EncodeBase64 Purpose : Encodes BASE64 Parameters: lpBuffer -> input buffer cbBuffer = size of input buffer lpcbReturn -> returned size of output buffer Returns : encoded string buffer (must be LocalFree'd by caller) Comment : ***************************************************************************/ #define BASE64_MAX_LINE 76 LPSTR EncodeBase64(LPBYTE lpInput, ULONG cbBuffer, LPULONG lpcbReturn) { //#ifdef NEW_STUFF LPSTR lpBuffer = NULL; PUCHAR outptr; UINT i, cExtras; UINT j, cCount, nBreakPt = ( (BASE64_MAX_LINE/4) - 1 ); // 72 encoded chars per line plus 4 spaces makes 76 // = (76 - 4)/ 4 for num of non space lines with 4 encoded characters per 3 data chars CONST CHAR * rgchDict = six2base64; // 4 spaces and 2 chars = 6 for new line cExtras = 6 * ((cbBuffer / BASE64_MAX_LINE) + 2); // want to add newline at beginning and end lpBuffer = LocalAlloc( LMEM_ZEROINIT, sizeof( TCHAR ) * (3 * cbBuffer + cExtras)); if (!lpBuffer) return NULL; // need to add a new line every 76 characters... outptr = (UCHAR *)lpBuffer; cCount = 0; for (i=0; i < cbBuffer; i += 3) {// want it to start on next line from tag anyways so it is okay when i=0 if( cCount++ % nBreakPt == 0 ) { *(outptr++) = (CHAR)(13); *(outptr++) = (CHAR)(10); // then 4 spaces for( j = 0; j < 4; j++) *(outptr++) = ' '; } *(outptr++) = rgchDict[*lpInput >> 2]; /* c1 */ *(outptr++) = rgchDict[((*lpInput << 4) & 060) | ((lpInput[1] >> 4) & 017)]; /*c2*/ *(outptr++) = rgchDict[((lpInput[1] << 2) & 074) | ((lpInput[2] >> 6) & 03)];/*c3*/ *(outptr++) = rgchDict[lpInput[2] & 077]; /* c4 */ lpInput += 3; } /* If cbBuffer was not a multiple of 3, then we have encoded too * many characters. Adjust appropriately. */ if(i == cbBuffer+1) { /* There were only 2 bytes in that last group */ outptr[-1] = '='; } else if(i == cbBuffer+2) { /* There was only 1 byte in that last group */ outptr[-1] = '='; outptr[-2] = '='; } cCount = ((cCount - 1) % nBreakPt != 0) ? 2 : 1; // prevent an extra newline for ( i = 0; i < cCount; i++) { *(outptr++) = (CHAR)(13); *(outptr++) = (CHAR)(10); } *outptr = '\0'; return lpBuffer; } /*************************************************************************** Name : WriteVCardValue Purpose : Encode and write the value of a vCard item. Parameters: hVCard = open handle to empty VCard file WriteFn = Write function to write hVCard lpData -> data to be written cbData = length of data (or 0 if null-terminated string data) Returns : HRESULT Comment : Assumes that the Key and any parameters have been written, and we are ready for a ':' and some value data. ***************************************************************************/ HRESULT WriteVCardValue(HANDLE hVCard, VCARD_WRITE WriteFn, LPBYTE lpData, ULONG cbData) { HRESULT hResult = hrSuccess; register LPSTR lpTemp = (LPSTR)lpData; BOOL fBase64 = FALSE, fQuotedPrintable = FALSE; LPSTR lpBuffer = NULL; register TCHAR ch; if (cbData) { // Binary data, use BASE64 encoding fBase64 = TRUE; // Mark it as BASE64 WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vcpTable[VCARD_PARAM_ENCODING]); WRITE_OR_EXIT(szEquals); WRITE_OR_EXIT(vceTable[VCARD_ENCODING_BASE64]); lpBuffer = EncodeBase64(lpData, cbData, &cbData); } else { // Text data, do we need to encode? while (ch = *lpTemp++) { // If there are characters with the high bit set or control characters, // then we must use QUOTED_PRINTABLE /* New vCard draft says default type is 8 bit so we should allow non-ASCII chars Some confusion about charsets if we need to fill that data in and also if we need to covert the current language to UTF-8 if (ch > 0x7f) { // high bits set. Not ASCII! DebugTrace( TEXT("WriteVCardValue found non-ASCII data\n")); hResult = ResultFromScode(WAB_E_VCARD_NOT_ASCII); goto exit; } */ if (ch < 0x20) { fQuotedPrintable = TRUE; // Mark it as QUOTED_PRINTABLE WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vcpTable[VCARD_PARAM_ENCODING]); WRITE_OR_EXIT(szEquals); WRITE_OR_EXIT(vceTable[VCARD_ENCODING_QUOTED_PRINTABLE]); lpBuffer = EncodeQuotedPrintable(lpData); break; } } } WRITE_OR_EXIT(szColonA); WRITE_OR_EXIT(lpBuffer ? lpBuffer : lpData); WRITE_OR_EXIT(szCRLFA); exit: if( lpBuffer) LocalFree(lpBuffer); return(hResult); } /*************************************************************************** Name: bIsValidStrProp Purpose: Checks if this is a valid string prop not an empty string (Outlook sometimes feeds us blank strings which we print out and then other apps go and die .. *****************************************************************************/ BOOL bIsValidStrProp(SPropValue spv) { return (!PROP_ERROR(spv) && spv.Value.LPSZ && lstrlen(spv.Value.LPSZ)); } /*************************************************************************** Name : WriteVCardTel Purpose : Writes a vCard Telephone entry Parameters: hVCard = open handle to empty VCard file WriteFn = Write function to write hVCard fPref = TRUE if prefered phone number fBusiness = TRUE if a work number fHome = TRUE if a home number fVoice = TRUE if a voice number fFax = TRUE if a fax number fISDN = TRUE if an ISDN number fCell = TRUE if a cellular number fPager = TRUE if a pager number fCar = TRUE if a car phone Returns : HRESULT Comment : ***************************************************************************/ HRESULT WriteVCardTel(HANDLE hVCard, VCARD_WRITE WriteFn, SPropValue spv, BOOL fPref, BOOL fBusiness, BOOL fHome, BOOL fVoice, BOOL fFax, BOOL fISDN, BOOL fCell, BOOL fPager, BOOL fCar) { HRESULT hResult = hrSuccess; if (!bIsValidStrProp(spv)) return hResult; if (! PROP_ERROR(spv)) { WRITE_OR_EXIT(vckTable[VCARD_KEY_TEL]); if (fPref) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_PREF]); } if (fBusiness) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_WORK]); } if (fHome) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_HOME]); } if (fFax) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_FAX]); } if (fCell) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_CELL]); } if (fCar) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_CAR]); } if (fPager) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_PAGER]); } if (fISDN) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_ISDN]); } if (fVoice) { WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_VOICE]); } WRITE_VALUE_OR_EXITW(spv.Value.LPSZ, 0); } exit: return(hResult); } /*************************************************************************** Name : WriteVCardEmail Purpose : Writes a vCard Email entry Parameters: hVCard = open handle to empty VCard file WriteFn = Write function to write hVCard lpEmailAddress -> Email address lpAddrType -> Addrtype or NULL (Default is SMTP) fDefault = TRUE if this is the preferred email address Returns : HRESULT Comment : ***************************************************************************/ HRESULT WriteVCardEmail(HANDLE hVCard, VCARD_WRITE WriteFn, LPTSTR lpEmailAddress, LPTSTR lpAddrType, BOOL fDefault) { HRESULT hResult = hrSuccess; if (lpEmailAddress && lstrlen(lpEmailAddress)) { WRITE_OR_EXIT(vckTable[VCARD_KEY_EMAIL]); WRITE_OR_EXITW(szSemicolon); if (fDefault) { WRITE_OR_EXIT(vctTable[VCARD_TYPE_PREF]); WRITE_OR_EXITW(szSemicolon); } if (lpAddrType && lstrlen(lpAddrType)) { if (! lstrcmpi(lpAddrType, szSMTP)) { WRITE_OR_EXIT(vctTable[VCARD_TYPE_INTERNET]); } else if (! lstrcmpi(lpAddrType, szX400)) { WRITE_OR_EXIT(vctTable[VCARD_TYPE_X400]); } else { // BUGBUG: This is questionable... we should stick to // the spec defined types, but what if they don't match? // Maybe I should ignore the type in that case. WRITE_OR_EXITW(lpAddrType); } } else { // Assume SMTP WRITE_OR_EXIT(vctTable[VCARD_TYPE_INTERNET]); } WRITE_VALUE_OR_EXITW(lpEmailAddress, 0); } exit: return(hResult); } /*************************************************************************** Name : PropLength Purpose : string length of string property Parameters: spv = SPropValue lppString -> return pointer to string value or NULL Returns : size of string (not including null) Comment : ***************************************************************************/ ULONG PropLength(SPropValue spv, LPTSTR * lppString) { ULONG cbRet = 0; if (! PROP_ERROR(spv) && spv.Value.LPSZ && lstrlen(spv.Value.LPSZ)) { *lppString = spv.Value.LPSZ; cbRet = sizeof(TCHAR)*lstrlen(*lppString); } else { *lppString = NULL; } return(cbRet); } /*************************************************************************** Name : WriteVCard Purpose : Writes a vCard to a file from a MAILUSER object. Parameters: hVCard = open handle to empty VCard file WriteFn = Write function to write hVCard lpMailUser -> open mailuser object Returns : HRESULT Comment : ***************************************************************************/ HRESULT WriteVCard(HANDLE hVCard, VCARD_WRITE WriteFn, LPMAILUSER lpMailUser) { HRESULT hResult = hrSuccess; ULONG ulcValues; LPSPropValue lpspv = NULL, lpspvAW = NULL; ULONG i; LPTSTR lpTemp = NULL; ULONG cbTemp = 0; LPTSTR lpSurname, lpGivenName, lpMiddleName, lpGeneration, lpPrefix; ULONG cbSurname, cbGivenName, cbMiddleName, cbGeneration, cbPrefix; LPTSTR lpCompanyName, lpDepartmentName; LPTSTR lpPOBox, lpOffice, lpStreet, lpCity, lpState, lpPostalCode, lpCountry; LPTSTR lpEmailAddress, lpAddrType; ULONG iDefaultEmail; LPEXTVCARDPROP lpList = NULL; LPBYTE lpDataBuffer = NULL; LPCERT_DISPLAY_INFO lpCDI = NULL, lpCDITemp = NULL; // See if there are any named props we need to export // HrGetExtVCardPropList(lpMailUser, &lpList); // Get the interesting properties from the MailUser object if (HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser, (LPSPropTagArray)&tagaVCard, MAPI_UNICODE, // flags &ulcValues, &lpspv))) { // @hack [bobn] {IE5-Raid 90265} Outlook cannot handle MAPI_UNICODE on Win9x // lets try not asking for unicode and converting... if(HR_FAILED(hResult = lpMailUser->lpVtbl->GetProps(lpMailUser, (LPSPropTagArray)&tagaVCard, 0, // flags &ulcValues, &lpspv))) { DebugTrace( TEXT("WriteVCard:GetProps -> %x\n"), GetScode(hResult)); goto exit; } if(HR_FAILED(hResult = HrDupeOlkPropsAtoWC(ulcValues, lpspv, &lpspvAW))) goto exit; FreeBufferAndNull(&lpspv); lpspv = lpspvAW; } if (ulcValues) { WRITE_OR_EXIT(vckTable[VCARD_KEY_BEGIN]); WRITE_VALUE_OR_EXIT(vckTable[VCARD_KEY_VCARD], 0); WRITE_OR_EXIT(vckTable[VCARD_KEY_VERSION]); WRITE_VALUE_OR_EXIT(CURRENT_VCARD_VERSION, 0); // // Required props // // // Name // // Make sure we have a name. // If there is no FML, create them from DN. If no DN, fail. cbSurname = PropLength(lpspv[ivcPR_SURNAME], &lpSurname); cbGivenName = PropLength(lpspv[ivcPR_GIVEN_NAME], &lpGivenName); cbMiddleName = PropLength(lpspv[ivcPR_MIDDLE_NAME], &lpMiddleName); cbGeneration = PropLength(lpspv[ivcPR_GENERATION], &lpGeneration); cbPrefix = PropLength(lpspv[ivcPR_DISPLAY_NAME_PREFIX], &lpPrefix); if (! lpSurname && ! lpGivenName && ! lpMiddleName) { // No FML, create them from DN. ParseDisplayName( lpspv[ivcPR_DISPLAY_NAME].Value.LPSZ, &lpGivenName, &lpSurname, lpspv, // lpvRoot NULL); // lppLocalFree cbGivenName = lstrlen(lpGivenName); cbSurname = lstrlen(lpSurname); } cbTemp = 0; cbTemp += cbSurname; cbTemp++; // ';' cbTemp += cbGivenName; cbTemp++; // ';' cbTemp += cbMiddleName; cbTemp++; // ';' cbTemp += cbPrefix; cbTemp++; // ';' cbTemp += cbGeneration; cbTemp++; if (! (lpSurname || lpGivenName || lpMiddleName)) { hResult = ResultFromScode(MAPI_E_MISSING_REQUIRED_COLUMN); goto exit; } if (! (lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*cbTemp))) { hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpTemp = '\0'; if (lpSurname) { StrCatBuff(lpTemp, lpSurname, cbTemp); } if (lpGivenName || lpMiddleName || lpPrefix || lpGeneration) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpGivenName) { StrCatBuff(lpTemp, lpGivenName, cbTemp); } if (lpMiddleName || lpPrefix || lpGeneration) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpMiddleName) { StrCatBuff(lpTemp, lpMiddleName, cbTemp); } if (lpPrefix || lpGeneration) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpPrefix) { StrCatBuff(lpTemp, lpPrefix, cbTemp); } if (lpGeneration) { StrCatBuff(lpTemp, szSemicolon, cbTemp); StrCatBuff(lpTemp, lpGeneration, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_N]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); // // Optional props // // // Formatted Name: PR_DISPLAY_NAME // if(bIsValidStrProp(lpspv[ivcPR_DISPLAY_NAME])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_FN]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_DISPLAY_NAME].Value.LPSZ, 0); } // // Title: PR_NICKNAME // if(bIsValidStrProp(lpspv[ivcPR_NICKNAME])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_NICKNAME]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_NICKNAME].Value.LPSZ, 0); } // // Organization: PR_COMPANY_NAME, PR_DEPARTMENT_NAME // cbTemp = 0; cbTemp += PropLength(lpspv[ivcPR_COMPANY_NAME], &lpCompanyName); cbTemp++; // semicolon cbTemp += PropLength(lpspv[ivcPR_DEPARTMENT_NAME], &lpDepartmentName); cbTemp++; if (lpCompanyName || lpDepartmentName) { if (! (lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*cbTemp))) { hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpTemp = '\0'; if (lpCompanyName) { StrCatBuff(lpTemp, lpCompanyName, cbTemp); } if (lpDepartmentName) { StrCatBuff(lpTemp, szSemicolon, cbTemp); StrCatBuff(lpTemp, lpDepartmentName, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_ORG]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // // Title: PR_TITLE // if(bIsValidStrProp(lpspv[ivcPR_TITLE])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_TITLE]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_TITLE].Value.LPSZ, 0); } // // Note: PR_COMMENT // if(bIsValidStrProp(lpspv[ivcPR_COMMENT])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_NOTE]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_COMMENT].Value.LPSZ, 0); } // // Phone numbers // // // PR_BUSINESS_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_BUSINESS_TELEPHONE_NUMBER], FALSE, // fPref TRUE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_BUSINESS2_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_BUSINESS2_TELEPHONE_NUMBER], FALSE, // fPref TRUE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_HOME_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_HOME_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness TRUE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_CELLULAR_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_CELLULAR_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN TRUE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_CAR_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_CAR_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager TRUE)) { // fCar goto exit; } // // PR_OTHER_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_OTHER_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_PAGER_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_PAGER_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness FALSE, // fHome TRUE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell TRUE, // fPager FALSE)) { // fCar goto exit; } // // PR_BUSINESS_FAX_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_BUSINESS_FAX_NUMBER], FALSE, // fPref TRUE, // fBusiness FALSE, // fHome FALSE, // fVoice TRUE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_HOME_FAX_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_HOME_FAX_NUMBER], FALSE, // fPref FALSE, // fBusiness TRUE, // fHome FALSE, // fVoice TRUE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_HOME2_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_HOME2_TELEPHONE_NUMBER], FALSE, // fPref FALSE, // fBusiness TRUE, // fHome FALSE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_ISDN_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_ISDN_NUMBER], FALSE, // fPref FALSE, // fBusiness FALSE, // fHome FALSE, // fVoice FALSE, // fFax TRUE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // PR_PRIMARY_TELEPHONE_NUMBER // if (hResult = WriteVCardTel(hVCard, WriteFn, lpspv[ivcPR_PRIMARY_TELEPHONE_NUMBER], TRUE, // fPref FALSE, // fBusiness FALSE, // fHome FALSE, // fVoice FALSE, // fFax FALSE, // fISDN FALSE, // fCell FALSE, // fPager FALSE)) { // fCar goto exit; } // // Business Address // cbTemp = 0; cbTemp += PropLength(lpspv[ivcPR_POST_OFFICE_BOX], &lpPOBox); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OFFICE_LOCATION], &lpOffice); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_BUSINESS_ADDRESS_STREET], &lpStreet); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_BUSINESS_ADDRESS_CITY], &lpCity); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_BUSINESS_ADDRESS_STATE_OR_PROVINCE], &lpState); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_BUSINESS_ADDRESS_POSTAL_CODE], &lpPostalCode); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_BUSINESS_ADDRESS_COUNTRY], &lpCountry); cbTemp++; if (lpPOBox || lpOffice || lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { if (! (lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*cbTemp))) { hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpTemp = '\0'; if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); } if (lpOffice || lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpOffice) { StrCatBuff(lpTemp, lpOffice, cbTemp); } if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); } if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); } if (lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); } if (lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); } if (lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_ADR]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_WORK]); WRITE_VALUE_OR_EXITW(lpTemp, 0); // Business Delivery Label // Use the same buffer *lpTemp = '\0'; if (lpOffice) { StrCatBuff(lpTemp, lpOffice, cbTemp); if (lpPOBox || lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); if (lpState) { StrCatBuff(lpTemp, szCommaSpace, cbTemp); } else if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCountry) { StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_LABEL]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_WORK]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // // Home Address // lpPOBox = lpStreet = lpCity = lpState = lpPostalCode = lpCountry = NULL; cbTemp = 0; cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_POST_OFFICE_BOX], &lpPOBox); cbTemp+= 2; // ';' or CRLF lpOffice = NULL; cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_STREET], &lpStreet); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_CITY], &lpCity); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_STATE_OR_PROVINCE], &lpState); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_POSTAL_CODE], &lpPostalCode); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_HOME_ADDRESS_COUNTRY], &lpCountry); cbTemp++; if (lpPOBox || lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { if (! (lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*cbTemp))) { hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpTemp = '\0'; if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); } if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); // WAB doesn't have extended on HOME address StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); } if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); } if (lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); } if (lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); } if (lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_ADR]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_HOME]); WRITE_VALUE_OR_EXITW(lpTemp, 0); // Home Delivery Label // Use the same buffer *lpTemp = '\0'; if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); if (lpState) { StrCatBuff(lpTemp, szCommaSpace, cbTemp); } else if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCountry) { StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_LABEL]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_HOME]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // // Other Address // lpPOBox = lpStreet = lpCity = lpState = lpPostalCode = lpCountry = NULL; cbTemp = 0; cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_POST_OFFICE_BOX], &lpPOBox); cbTemp+= 2; // ';' or CRLF lpOffice = NULL; cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_STREET], &lpStreet); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_CITY], &lpCity); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_STATE_OR_PROVINCE], &lpState); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_POSTAL_CODE], &lpPostalCode); cbTemp+= 2; // ';' or CRLF cbTemp += PropLength(lpspv[ivcPR_OTHER_ADDRESS_COUNTRY], &lpCountry); cbTemp++; if (lpPOBox || lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { if (! (lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*cbTemp))) { hResult = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); goto exit; } *lpTemp = '\0'; if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); } if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); // WAB doesn't have extended on HOME address StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); } if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); } if (lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); } if (lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); } if (lpCountry) { StrCatBuff(lpTemp, szSemicolon, cbTemp); StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_ADR]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_POSTAL]); WRITE_VALUE_OR_EXITW(lpTemp, 0); // Adr Label // Use the same buffer *lpTemp = '\0'; if (lpPOBox) { StrCatBuff(lpTemp, lpPOBox, cbTemp); if (lpStreet || lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpStreet) { StrCatBuff(lpTemp, lpStreet, cbTemp); if (lpCity || lpState || lpPostalCode || lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCity) { StrCatBuff(lpTemp, lpCity, cbTemp); if (lpState) { StrCatBuff(lpTemp, szCommaSpace, cbTemp); } else if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpState) { StrCatBuff(lpTemp, lpState, cbTemp); if (lpPostalCode) { StrCatBuff(lpTemp, szSpace, cbTemp); } else if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpPostalCode) { StrCatBuff(lpTemp, lpPostalCode, cbTemp); if (lpCountry) { StrCatBuff(lpTemp, szCRLF, cbTemp); } } if (lpCountry) { StrCatBuff(lpTemp, lpCountry, cbTemp); } WRITE_OR_EXIT(vckTable[VCARD_KEY_LABEL]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_POSTAL]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // GENDER if(! PROP_ERROR(lpspv[ivcPR_GENDER] ) ) { TCHAR szBuf[4]; INT fGender = lpspv[ivcPR_GENDER].Value.l; // don't want to export gender data if // it is unspecified if( fGender == 1 || fGender == 2 ) { szBuf[0] = '0' + fGender; szBuf[1] = '\0'; WRITE_OR_EXIT(vckTable[VCARD_KEY_X_WAB_GENDER]); WRITE_OR_EXIT(szColonA); WRITE_OR_EXITW(szBuf); WRITE_OR_EXIT(szCRLFA); } } // // URL's. Must do personal first. Note that the vCard 2.0 standard does // not distinguish between HOME and WORK URL's. Too bad. Thus, if we export // a contact with only a business home page, then import it, we will end up // with a contact that has a personal home page. Hopefully, the vCard 3.0 standard // will fix this. // // 62808: The above is really a big problem in Outlook because there is perceived data loss // Hence to prevent this, we will take advantage of a bug in WAB code .. blank URLS are not // ignored .. we will write out a blank URL for the personal one when only a business URL exists // That way, when round-tripping the business URL shows up in the right place // // // It's September of 2000. The European Commission is looking at Outlook for their mail client, // one of the things that is hanging them up is this bug, the WORK URL jumps from the WORK URL // box to the HOME URL if you export/import the vCard. We need this functioning, so I looked // for the vCard 3.0 standard to see how they are handling the URL. Every place I look says that // the people in charge of the vCard standard is www.versit.com, this however is a now defunct web // site, I queried the other companies that use the vCard, Apple, IBM, AT&T all give press releases // telling you to look at the www.versit.com web site, they also give a 1-800 number to call. I've // called the 1-800 number and that number is now a yellow pages operator. I can't find a vCard 3.0 // standard, so...... // // Now, we all wish we could do this: URL;HOME: and URL;WORK:. Well I'm going to do it! // // // URL: PR_PERSONAL_HOME_PAGE // if(bIsValidStrProp(lpspv[ivcPR_PERSONAL_HOME_PAGE])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_URL]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_HOME]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_PERSONAL_HOME_PAGE].Value.LPSZ, 0); } // // URL: PR_BUSINESS_HOME_PAGE // if(bIsValidStrProp(lpspv[ivcPR_BUSINESS_HOME_PAGE])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_URL]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_WORK]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_BUSINESS_HOME_PAGE].Value.LPSZ, 0); } // // ROLE: PR_PROFESSION // if(bIsValidStrProp(lpspv[ivcPR_PROFESSION])) { WRITE_OR_EXIT(vckTable[VCARD_KEY_ROLE]); WRITE_VALUE_OR_EXITW(lpspv[ivcPR_PROFESSION].Value.LPSZ, 0); } // // BDAY: PR_BIRTHDAY // // Format is YYYYMMDD e.g. 19970911 for September 11, 1997 // if (! PROP_ERROR(lpspv[ivcPR_BIRTHDAY])) { SYSTEMTIME st = {0}; FileTimeToSystemTime((FILETIME *) (&lpspv[ivcPR_BIRTHDAY].Value.ft), &st); lpTemp = LocalAlloc(LPTR, sizeof(TCHAR)*32); wnsprintf(lpTemp, 32, TEXT("%.4d%.2d%.2d"), st.wYear, st.wMonth, st.wDay); WRITE_OR_EXIT(vckTable[VCARD_KEY_BDAY]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // // DIGITAL CERTIFICATES // if(! PROP_ERROR(lpspv[ivcPR_USER_X509_CERTIFICATE] ) // && ! PROP_ERROR(lpspv[ivcPR_EMAIL_ADDRESS]) ) { // LPTSTR lpszDefaultEmailAddress = lpspv[ivcPR_EMAIL_ADDRESS].Value.LPSZ; LPSPropValue lpSProp = &lpspv[ivcPR_USER_X509_CERTIFICATE]; lpCDI = lpCDITemp = NULL; if( HR_FAILED(hResult = HrGetCertsDisplayInfo( NULL, lpSProp, &lpCDI) ) ) { DebugTrace( TEXT("get cert display info failed\n")); } else { lpCDITemp = lpCDI; while( lpCDITemp ) { /* if( (lstrcmp(lpCDITemp->lpszEmailAddress, lpszDefaultEmailAddress) == 0) && lpCDITemp->bIsDefault ) break;*/ if( lpCDITemp ) // found a certificate now export it to buffer and write to file { ULONG cbBufLen; if( HR_SUCCEEDED(hResult = HrExportCertToFile( NULL, lpCDITemp->pccert, &lpDataBuffer, &cbBufLen, TRUE) ) ) { WRITE_OR_EXIT(vckTable[VCARD_KEY_KEY]); WRITE_OR_EXITW(szSemicolon); WRITE_OR_EXIT(vctTable[VCARD_TYPE_X509]); WRITE_VALUE_OR_EXITW(lpDataBuffer, cbBufLen); } else { DebugTrace( TEXT("unable to write to buffer at address %x\n"), lpDataBuffer); } LocalFreeAndNull(&lpDataBuffer); } lpCDITemp = lpCDITemp->lpNext; } } while( lpCDI ) // free the cert info { lpCDITemp = lpCDI->lpNext; FreeCertdisplayinfo(lpCDI); lpCDI = lpCDITemp; } lpCDI = lpCDITemp = NULL; } // // E-Mail addresses // if (! PROP_ERROR(lpspv[ivcPR_CONTACT_EMAIL_ADDRESSES])) { // What's the default? if (PROP_ERROR(lpspv[ivcPR_CONTACT_DEFAULT_ADDRESS_INDEX])) { iDefaultEmail = 0; } else { iDefaultEmail = lpspv[ivcPR_CONTACT_DEFAULT_ADDRESS_INDEX].Value.l; } // for each email address, add an EMAIL key for (i = 0; i < lpspv[ivcPR_CONTACT_EMAIL_ADDRESSES].Value.MVSZ.cValues; i++) { lpEmailAddress = lpspv[ivcPR_CONTACT_EMAIL_ADDRESSES].Value.MVSZ.LPPSZ[i]; if (PROP_ERROR(lpspv[ivcPR_CONTACT_ADDRTYPES])) { lpAddrType = (LPTSTR)szSMTP; } else { lpAddrType = lpspv[ivcPR_CONTACT_ADDRTYPES].Value.MVSZ.LPPSZ[i]; } if (hResult = WriteVCardEmail(hVCard, WriteFn, lpEmailAddress, lpAddrType, (iDefaultEmail == i))) { goto exit; } } } else { // no PR_CONTACT_EMAIL_ADDRESSES, try PR_EMAIL_ADDRESS PropLength(lpspv[ivcPR_EMAIL_ADDRESS], &lpEmailAddress); PropLength(lpspv[ivcPR_ADDRTYPE], &lpAddrType); if (hResult = WriteVCardEmail(hVCard, WriteFn, lpEmailAddress, lpAddrType, TRUE)) { goto exit; } } // // EMAIL;TLX: PR_TELEX_NUMBER // // There is no place to put a telex number in a vCard but the EMAIL field // allows us to specify any AddrType .. hence under pressure from Outlook, // we force this Telex number into email .. Must make sure to filter this out // when we read in a vCard // if(bIsValidStrProp(lpspv[ivcPR_TELEX_NUMBER])) { if (hResult = WriteVCardEmail(hVCard, WriteFn, lpspv[ivcPR_TELEX_NUMBER].Value.LPSZ, TEXT("TLX"), FALSE)) { goto exit; } } // Check if there are any outlook specific named properties // that need to be written out to the vCard if(lpList) { LPEXTVCARDPROP lpTemp = lpList; while( lpTemp && lpTemp->ulExtPropTag && lpTemp->lpszExtPropName && lstrlenA(lpTemp->lpszExtPropName)) { LPSPropValue lpspv = NULL; if(!HR_FAILED(HrGetOneProp( (LPMAPIPROP)lpMailUser, lpTemp->ulExtPropTag, &lpspv ) )) { if(lpspv->Value.LPSZ && lstrlen(lpspv->Value.LPSZ)) { WRITE_OR_EXIT(lpTemp->lpszExtPropName); WRITE_VALUE_OR_EXITW(lpspv->Value.LPSZ, 0); } FreeBufferAndNull(&lpspv); } lpTemp = lpTemp->lpNext; } } // // REV: Current Modification Time // // Format is YYYYMMDD e.g. 19970911 for September 11, 1997 // { SYSTEMTIME st = {0}; DWORD ccSize = 32; GetSystemTime(&st); lpTemp = LocalAlloc(LPTR, sizeof(TCHAR) * ccSize); wnsprintf(lpTemp, ccSize, TEXT("%.4d%.2d%.2dT%.2d%.2d%.2dZ"), st.wYear, st.wMonth, st.wDay, st.wHour,st.wMinute,st.wSecond); WRITE_OR_EXIT(vckTable[VCARD_KEY_REV]); WRITE_VALUE_OR_EXITW(lpTemp, 0); LocalFreeAndNull(&lpTemp); } // End of VCARD WRITE_OR_EXIT(vckTable[VCARD_KEY_END]); WRITE_VALUE_OR_EXIT(vckTable[VCARD_KEY_VCARD], 0); } exit: if(lpList) FreeExtVCardPropList(lpList); while( lpCDI ) // free the cert info { lpCDITemp = lpCDI->lpNext; FreeCertdisplayinfo(lpCDI); lpCDI = lpCDITemp; } lpCDI = lpCDITemp = NULL; LocalFreeAndNull(&lpTemp); FreeBufferAndNull(&lpspv); LocalFreeAndNull(&lpDataBuffer); return(hResult); } /*************************************************************************** Name : FileWriteFn Purpose : write to the file handle Parameters: handle = open file handle lpBuffer -> buffer to write uBytes = size of lpBuffer lpcbWritten -> returned bytes written (may be NULL) Returns : HRESULT Comment : WriteFile callback for WriteVCard ***************************************************************************/ HRESULT FileWriteFn(HANDLE handle, LPVOID lpBuffer, ULONG uBytes, LPULONG lpcbWritten) { ULONG cbWritten = 0; if (lpcbWritten) { *lpcbWritten = 0; } else { lpcbWritten = &cbWritten; } #ifdef DEBUG { LPTSTR lpW = ConvertAtoW((LPCSTR)lpBuffer); DebugTrace(lpW); LocalFreeAndNull(&lpW); } #endif if (! WriteFile(handle, lpBuffer, uBytes, lpcbWritten, NULL)) { DebugTrace( TEXT("FileWriteFn:WriteFile -> %u\n"), GetLastError()); return(ResultFromScode(MAPI_E_DISK_ERROR)); } return(hrSuccess); } //////////////////////////////////////////////////////////////// /* - - VCardGetBuffer - * Retreives a vCard Buffer from a given filename or * retrieves a copy of a given buffer * Also inspects the buffer to see how many vCard * files are nested in it * * lpszFileName - File to open * lpszBuf - Stream to open * ulFlags - MAPI_DIALOG or none * lppBuf - Local Alloced returned buf */ BOOL VCardGetBuffer(LPTSTR lpszFileName, LPSTR lpszBuf, LPSTR * lppBuf) { BOOL bRet = FALSE; LPSTR lpBuf = NULL; HANDLE hFile = NULL; if(!lpszFileName && !lpszBuf) goto out; // first look for a buffer and not for the filename if(lpszBuf && lstrlenA(lpszBuf)) { ULONG cbBuf = lstrlenA(lpszBuf)+1; lpBuf = LocalAlloc(LMEM_ZEROINIT, cbBuf); if(!lpBuf) goto out; StrCpyNA(lpBuf, lpszBuf, cbBuf); } else if(lpszFileName && lstrlen(lpszFileName)) { if (INVALID_HANDLE_VALUE == (hFile = CreateFile(lpszFileName,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL))) { goto out; } // Read the whole file into a buffer { DWORD dwSize = GetFileSize(hFile, NULL); DWORD dwRead = 0; if(!dwSize || dwSize == 0xFFFFFFFF) goto out; //err lpBuf = LocalAlloc(LMEM_ZEROINIT, dwSize+1); if(!lpBuf) goto out; if(!ReadFile(hFile, lpBuf, dwSize, &dwRead, NULL)) goto out; } } *lppBuf = lpBuf; bRet = TRUE; out: if(hFile) IF_WIN32(CloseHandle(hFile);) IF_WIN16(CloseFile(hFile);) return bRet; } /* - - VCardGetNextBuffer - * Scans a vCard buffer and returns pointers to the next vCard and the one after that * */ static const LPSTR szVBegin = "BEGIN:VCARD"; static const LPSTR szVEnd = "END:VCARD"; BOOL VCardGetNextBuffer(LPSTR lpBuf, LPSTR * lppVCard, LPSTR * lppNext) { LPSTR lpTemp = lpBuf; char sz[64]; int nStr = lstrlenA(szVEnd); BOOL bFound = FALSE; BOOL bRet = TRUE; Assert(lppVCard); Assert(lppNext); *lppVCard = lpBuf; *lppNext = NULL; // Scan along lpBuf till we get to END:VCARD // After finding END:VCARD - insert a NULL to terminate the string // and find the start of the next string if (!lpTemp) return FALSE; while((lstrlenA(lpTemp) >= nStr) && !bFound) { CopyMemory(sz,lpTemp,nStr); sz[nStr] = '\0'; if(!lstrcmpiA(sz, szVEnd)) { // Add a terminating NULL to isolate the vCard *(lpTemp + nStr) = '\0'; lpTemp += nStr + 1; bFound = TRUE; } // scan to the end of the line while(*lpTemp && *lpTemp != '\n') lpTemp++; // Start from the next line if (*lpTemp) lpTemp++; } bFound = FALSE; nStr = lstrlenA(szVBegin); // Find the starting of the next BEGIN:VCARD while((lstrlenA(lpTemp) >= nStr) && !bFound) { CopyMemory(sz,lpTemp,nStr); sz[nStr] = '\0'; if(!lstrcmpiA(sz, szVBegin)) { *lppNext = lpTemp; bFound = TRUE; } else { // scan to the end of the line while(*lpTemp && *lpTemp != '\n') lpTemp++; // Start from the next line if (*lpTemp) lpTemp++; } } return bRet; } SizedSPropTagArray(2, tagaCerts) = { 2, { PR_USER_X509_CERTIFICATE, PR_WAB_TEMP_CERT_HASH } }; /** ParseCert: will parse the binary data in the buffer and set the certificate as a prop for the specified mailuser. [IN] lpData - address of the binary data buffer containing the certificate [IN] cbData - length of the binary data buffer [IN] lpMailUser - access to the mail user so the certificate can be set */ HRESULT ParseCert( LPSTR lpData, ULONG cbData, LPMAILUSER lpMailUser) { HRESULT hr = hrSuccess; ULONG ulcProps = 0; LPSPropValue lpSpv = NULL; if( lpData && *lpData ) { if( HR_FAILED( hr = lpMailUser->lpVtbl->GetProps( lpMailUser, (LPSPropTagArray)&tagaCerts, MAPI_UNICODE, &ulcProps, &lpSpv) ) ) { DebugTrace( TEXT("could not get Props\n")); return hr; } if(lpSpv[0].ulPropTag != PR_USER_X509_CERTIFICATE ) { MAPIFreeBuffer( lpSpv ); MAPIAllocateBuffer( sizeof(SPropValue) * 2, &lpSpv); if( lpSpv ) { lpSpv[0].ulPropTag = PR_USER_X509_CERTIFICATE; lpSpv[0].dwAlignPad = 0; lpSpv[0].Value.MVbin.cValues = 0; lpSpv[1].ulPropTag = PR_WAB_TEMP_CERT_HASH; lpSpv[1].dwAlignPad = 0; lpSpv[1].Value.MVbin.cValues = 0; } else { DebugTrace( TEXT("could not allocate mem for props\n")); hr = ResultFromScode(MAPI_E_NOT_ENOUGH_MEMORY); return hr; } } else { // [PaulHi] 5/3/99 Check the PR_WAB_TEMP_CERT_HASH to see if it is // of type PT_ERROR. If it is then this is Ok, it is just empty of // data. We only use this to hold temporary data which is freed below. if ( PROP_TYPE(lpSpv[1].ulPropTag) == PT_ERROR ) { lpSpv[1].ulPropTag = PR_WAB_TEMP_CERT_HASH; lpSpv[1].Value.MVbin.cValues = 0; lpSpv[1].Value.MVbin.lpbin = NULL; } } // Put the certs into the prop array. hr = HrLDAPCertToMAPICert( lpSpv, 0, 1, cbData, (LPBYTE)lpData, 1); if( HR_SUCCEEDED( hr ) ) { if (HR_FAILED(hr = lpMailUser->lpVtbl->SetProps(lpMailUser, 1, lpSpv, NULL))) { DebugTrace( TEXT("failed setting props\n")); } } else { DebugTrace( TEXT("LDAPCertToMapiCert failed\n")); } MAPIFreeBuffer( lpSpv ); } else { DebugTrace( TEXT("lpData was null\n")); hr = E_FAIL; } return hr; } /** DecodeBase64: decode BASE64 data [IN] bufcoded - access to the BASE64 encoded data [OUT] pbuffdecoded - address of the buffer where decoded data will go [OUT] pcbDecode - length of the decoded data buffer */ HRESULT DecodeBase64(LPSTR bufcoded, LPSTR pbuffdecoded, PDWORD pcbDecoded) { INT nbytesdecoded; LPSTR bufin; LPSTR bufout; INT nprbytes; CONST INT *rgiDict = base642six; /* Strip leading whitespace. */ while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++; /* Figure out how many characters are in the input buffer. * If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */ bufin = bufcoded; while(rgiDict[*(bufin++)] <= 63); nprbytes = (INT) (bufin - bufcoded - 1); nbytesdecoded = ((nprbytes+3)/4) * 3; if ( pcbDecoded ) *pcbDecoded = nbytesdecoded; bufout = (LPSTR)pbuffdecoded; bufin = bufcoded; while (nprbytes > 0) { *(bufout++) = (char) (rgiDict[*bufin] << 2 | rgiDict[bufin[1]] >> 4); *(bufout++) = (char) (rgiDict[bufin[1]] << 4 | rgiDict[bufin[2]] >> 2); *(bufout++) = (char) (rgiDict[bufin[2]] << 6 | rgiDict[bufin[3]]); bufin += 4; nprbytes -= 4; } if(nprbytes & 03) { if(rgiDict[bufin[-2]] > 63) nbytesdecoded -= 2; else nbytesdecoded -= 1; } ((LPSTR)pbuffdecoded)[nbytesdecoded] = '\0'; return S_OK; } #endif