Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4976 lines
164 KiB

/*
* 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