POPPST.CPP
// --poppst.cpp--------------------------------------------------------------- 
// 
// Implementation of mail storage file populator program. 
//   
// Copyright (C) Microsoft Corp. 1986-1996.  All rights reserved. 
// 
//----------------------------------------------------------------------------- 
 
// 
// Begin program documentation 
// 
 
/* 
 
Poppst is a command line program that uses input from an ini file to create 
messages in either a new PST or an existing PST.  If a new PST is desired, it 
will be created in a temporary profile that is deleted when PST creation is 
done.  If it is desired to create the messages in an existing PST, the profile 
name is given for the PST. 
 
Command line arguments: 
 
One command line argument, the name of the ini file, is required. 
 
Format of the ini file: 
 
The following notation is used in describing the ini file grammar: 
 
- Angle brackets (ie. <>) are used to indicate a token, or syntactic element. 
- Braces (ie. {}) are used to indicate an optional syntactic element. 
- Ellipsis (ie. ...) are used to indicate that the preceding syntactic element 
  may be repeated. 
- // indicates embedded comments (ie., not part of the file format). 
- | indicates alternation (ie., a | b implies a or b is acceptable). 
 
File format generalities: 
 
The format used is basically consistent with the standard Microsoft ini file 
format.  Thus, there are section header lines and section entry lines.  The 
section header lines are distinguished from section entry lines by enclosing 
the section keyword in square brackets.  Section entry lines have an entry 
keyword that is immediately followed by an '='.  The '=' is then immediately 
followed by a value for the section entry.  All characters following the '=' 
up to the newline are used for the value (ie., if there is whitespace, it will 
be included in the value).  Leading whitespace is permitted in all lines, and 
is not significant.  Whitespace everywhere else IS significant.  Comments may 
be included by using ';' as the first nonwhitespace character on a line. 
Blank lines may also be included in the ini file and are ignored.  Either a 
newpst section or a profile section is required, and must be the first section 
in the ini file.  Note that both a newpst section and a profile section 
should NOT be used in one ini file.  One or more message sections should then 
follow the newpst or profile section.  Unless otherwise noted, all section 
entry lines are required, and must occur exactly once in a section.  The order 
of section entries within a section is not important.The poppst program is 
not case sensitive with regard to keywords.  The maximum line length permitted 
is 512 chars (cbMaxIniLine) including the newline. 
 
!!!!!IMPORTANT INFORMATION!!!!! 
 
The [newpst] and [profile] sections are mutually exclusive.   
 
If you specify the [newpst] section, a temporary profile will be used. 
This means that no name resolution will be possible. 
 
If you specify the [profile] section, the message create 
will be placed in the specified folder in the default message store. 
If you want the message to be placed into your PST, then you must 
FIRST set the PST "Personal Folders" store to be your default store 
for the specified profile (Select Options | Delivery in the Exchange  
client). 
 
!!!!!!!!END IMPORTANT INFORMATION!!!!!!!!!!!!!!! 
 
Newpst section format (use to create a new pst and add msgs to it):\ 
 
[newpst] 
NAME=<pst display name string> 
ENCRYPTION=<encryption state> 
PASSWORD=<pst password string> 
PATH=<file path> 
 
Profile section format (use to add msgs to existing pst in existing profile): 
 
[profile] 
NAME=<profile name string> 
PASSWORD=<profile password string> 
 
Message section format: 
 
[message<whitespace><number>] 
FROM=<user name>{;<user name>}... 
TO=<user name>{;<user name>}... 
CC=<user name>{;<user name>}...// Optional. 
BCC=<user name>{;<user name>}...// Optional. 
FOLDER=<folder path> 
SUBJECT=<string> 
XTEXT=<file path>                   // Optional 
DATE=<date> 
TIME=<time> 
PRIORITY=<priority> 
XATTACH=<file path>{;<file path>}...// Optional. 
 
 
 
// The <number> value in the message section header should be unique.  Poppst 
// doesn't acutally use the number.  It is there only for Windows API compatibility. 
 
BNF for syntactic elements used above: 
 
//This is an incomplete BNF, in that in some instances, we resolve an element 
//to <string> or <nonnull string>, and then add a comment indicating that 
//the string must be acceptable input for a given parameter of a given api 
//or a valid value for a given property. 
 
<pst display name string> ::= <string>// Must be valid PR_DISPLAY_NAME 
// value. 
<pst password string> ::= <string>// Must be valid PR_PST_PW_SZ_NEW 
// value. 
 
<profile name string> ::= <nonnull string>// Must be valid lpszProfileName 
// param of MAPILogonEx(). 
 
<profile password string> ::= <string>// Must be valid lpszPassword 
// param of MAPILogonEx(). 
 
<whitespace> ::= <char>// Where isspace(<char>) == TRUE. 
 
<number> ::= <digit>... 
 
<user name> ::= <user display name>{[<address type>:<email address>]} 
 
<user display name> ::= <nonnull string>// Must be valid PR_DISPLAY_NAME 
// value. 
 
<address type> ::= <nonnull string>// Must be valid PR_ADDRTYPE value. 
 
<email address> ::= <nonnull string>// Must be valid PR_EMAIL_ADDRESS 
// value. 
 
<folder path> ::= {<folder namestring>{\<folder namestring>}...}// See note 
// below. 
<string> ::= {<char>}... 
 
<file path> ::= <nonnull string>// Must be valid filename param of 
// fopen() api. 
 
<date> ::= YYYY-MM-DD// ie., year-month-day, as digits. 
 
<time> ::= HH:MM{:SS}// ie., hour:min{sec} as digits. 
 
<encryption state> ::= ON | OFF 
 
<priority> ::= HIGH | NORMAL | LOW 
 
<nonnull string> ::= <char>... 
 
<char> ::= Any char other than '\n'. 
 
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 
 
<folder namestring> ::= <nonnull string>// Must be valid PR_DISPLAY_NAME 
// value for a mailstore folder. 
 
// Additional note on <folder path>: 
 
A null folder path is used to specify the root folder of a mail store.  If you 
want to add mail to the user inbox, the correct folder path is: 
"Microsoft Personal Information Store\Inbox". 
 
// Notes on message properties: 
 
The properties that are created for each entry line in a message section are 
documented below.  In general, we have used the properties that will result 
in the information being displayed by the Exchange client frontend. 
 
FROM:PR_SENDER_NAME = <user name> 
PR_SENT_REPRESENTING_NAME = <user name> 
 
// The following three lines are used to set up the recipient table in 
// a message.  If the address book is available and <user name> can be 
// resolved, then PR_DISPLAY_NAME will be used to derive PR_ADDRTYPE, 
// PR_EMAIL_ADDRESS, and PR_ENTRYID.  If the address book is available, 
// PR_ADDRTYPE and PR_EMAIL_ADDRESS may also be provided explicitly by using 
// a <user name> of form <user display name>[<address type>:<email address>]. 
// All the <user display name>'s provided will also be used to set 
// PR_DISPLAY_TO, PR_DISPLAY_CC, and PR_DISPLAY_BCC. 
 
TO:PR_RECIPIENT_TYPE = MAPI_TO; 
PR_DISPLAY_NAME = <user name> for each <user name>; 
 
CC:PR_RECIPIENT_TYPE = MAPI_CC; 
PR_DISPLAY_NAME = <user name> for each <user name>; 
 
BCC:PR_RECIPIENT_TYPE = MAPI_BCC; 
PR_DISPLAY_NAME = <user name> for each <user name>; 
 
FOLDER:Used to find folder message should be placed in.  If it does not 
exist, it will be created. 
 
SUBJECT:Used to set PR_SUBJECT. 
 
XTEXT:Contents of <file path> used to set PR_BODY & PR_RTF_COMPRESSED 
 
DATE:Used to set PR_MESSAGE_DELIVERY_TIME. 
 
TIME:Used to set PR_MESSAGE_DELIVERY_TIME. 
 
PRIORITY:Used to set PR_PRIORITY & PR_IMPORTANCE 
 
XATTACH:Contents of <file path> used to set PR_ATTACH_DATA_BIN. 
PR_ATTACH_METHOD = ATTACH_BY_VALUE. 
PR_ATTACH_FILENAME = <file path>. 
PR_ATTACH_LONG_FILENAME = <file path>. 
 
*/ 
 
// 
// End program documentation 
// 
 
#include "edk.h" 
#include "mspst.h" 
#include "popstrs.h" 
#include "poppstmc.h"// generated by the message compiler from poppstmc.mc 
#include "poppst.chk" 
 
// 
// Manifest constants 
// 
 
const ULONG ulMapiLogonFlags    =    
(MAPI_NEW_SESSION | MAPI_EXPLICIT_PROFILE | MAPI_NO_MAIL); 
 
const UINT  cbMaxIniLine    =   512; 
 
// 
// Macros 
// 
 
// No macros defined. 
 
// 
// Enumeration, structure, and other type definitions 
// 
 
enum INIFILESECTIONTYPE// ifst 
{ 
    IFST_MESSAGE, 
IFST_NEWPST, 
    IFST_PROFILE, 
    IFST_UNKNOWN 
}; 
 
enum LINEREADSTATUS// lrs 
{ 
    LRS_OKAY,           // Line was read and is available. 
    LRS_EOF,            // We are at end of file; no more lines. 
    LRS_IOERROR,        // There was an I/O error in the last read. 
    LRS_LINETOOLONG     // Last line too long to return. 
}; 
 
struct INIFILELINEVALUE// iflv 
{ 
BOOLfValid; 
    CHAR            achValue[cbMaxIniLine]; 
}; 
 
struct INIFILEPARSEINFO// ifpi 
{ 
    CHAR *          pszKeyword; 
    HRESULT            (*HrParseFunction)(INCHAR *pch); 
INIFILELINEVALUE *piflv; 
}; 
 
// 
// Forward function declarations 
// 
 
static 
HRESULT 
HrCreateMessageAttachment( 
INLPMESSAGElpMsg, 
INLPSTRlpszAttachPath 
); 
 
static 
HRESULT 
HrGetNextLine(OUT LINEREADSTATUS * lpReadStatus); 
 
static 
VOID 
InitSectionData( 
INBOOLfCommented);// TRUE if section data is commented 
 
static 
HRESULT 
HrIsSectionLine( 
    IN      CHAR *  pch, 
    OUT     BOOL * bIsSectionLine 
    ); 
 
static 
HRESULT 
HrOpenFolder(OUT LPMAPIFOLDER * lppFolder); 
 
static 
HRESULT 
HrParseDateValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParseEncryptionValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParseEntryLine( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParseLine(); 
 
static 
HRESULT 
HrParseNonNullStringValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParsePriorityValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParseSectionLine( 
    IN      CHAR *  pch,// pointer to section keyword 
INBOOLfCommented// TRUE is section is commented out 
    ); 
 
static 
HRESULT 
HrParseStringValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrParseTimeValue( 
    IN      CHAR *  pch 
    ); 
 
static 
HRESULT 
HrProcessMessageSection(VOID); 
 
static 
HRESULT 
HrProcessNewPSTSection(VOID); 
 
static 
HRESULT 
HrProcessProfileSection(VOID); 
 
static 
HRESULT 
HrResolveRecipientNames( 
OUTLPADRLIST *lppAdrList 
); 
 
static 
HRESULT 
HrResolveSenderName( 
    IN      LPSTR pszName, 
OUTLPADRLIST *ppAdrList 
); 
 
static 
HRESULT 
HrSetMessageProperties( 
INLPMESSAGElpMsg 
); 
 
static 
HRESULT 
HrWriteMessageText( 
INLPMESSAGElpMsg, 
INLPSTRlpszFilePath 
); 
 
// 
// Static storage 
// 
 
// Temporary profile name 
static LPSTRlpszProfileName =   NULL; 
 
// Temporary profile password 
static LPSTRlpszProfilePassword =   NULL; 
 
// Storage used to input and parse ini file lines and store values. 
 
// Ini file pointer: 
 
static FILE *           IniFile =NULL; 
 
// Current input line: 
 
static CHAR             achIniLine[cbMaxIniLine]  =   {0}; 
 
// Parser state variables: 
 
static BOOLfTmpProf =FALSE; 
static INIFILESECTIONTYPEifstCurSection  =   IFST_UNKNOWN; 
 
// The following variable is TRUE if the current section has been  
// commented out (e.g. ;[newpst]) 
static BOOLfCurSectionComment=FALSE; 
 
// Parsed value storage (uses ~0.5KB per value): 
 
static INIFILELINEVALUEiflvBCC =   {0};// For Message 
static INIFILELINEVALUEiflvCC  =   {0};// For Message 
static INIFILELINEVALUEiflvDATE    =   {0};// For Message 
static INIFILELINEVALUEiflvENCRYPTION  =   {0};// For NewPST 
static INIFILELINEVALUEiflvFOLDER  =   {0};// For Message 
static INIFILELINEVALUEiflvFROM    =   {0};// For Message 
static INIFILELINEVALUEiflvPROFILENAME =   {0};// For Profile 
static INIFILELINEVALUEiflvPSTNAME =   {0};// For NewPST 
static INIFILELINEVALUEiflvPROFILEPASSWORD =   {0};// For Profile 
static INIFILELINEVALUEiflvPSTPASSWORD =   {0};// For NewPST 
static INIFILELINEVALUEiflvPATH    =   {0};// For NewPST 
static INIFILELINEVALUEiflvPRIORITY    =   {0};// For Message 
static INIFILELINEVALUEiflvSUBJECT =   {0};// For Message 
static INIFILELINEVALUEiflvTIME    =   {0};// For Message 
static INIFILELINEVALUEiflvTO  =   {0};// For Message 
static INIFILELINEVALUEiflvXATTACH =   {0};// For Message 
static INIFILELINEVALUEiflvXTEXT   =   {0};// For Message 
 
// We translate some values and store them below during parsing: 
 
static BOOLbEncryptionNewPST   =   0; 
static FILETIMEftCurMsg            =   {0}; 
static LONGlPriorityCurMsg     =   0; 
static LONG             lImportanceCurMsg   =   0; 
static SYSTEMTIMEstCurMsg ={0, 0, 0, 0, 0, 0, 0, 0}; 
 
// Parser tables for entries: 
 
static 
INIFILEPARSEINFO    aifpiMessageEntry[] =   { 
                                            "BCC", HrParseNonNullStringValue, 
&iflvBCC, 
                                            "CC", HrParseNonNullStringValue, 
&iflvCC, 
                                            "DATE", HrParseDateValue, 
&iflvDATE, 
                                            "FOLDER", HrParseStringValue, 
&iflvFOLDER, 
                                            "FROM", HrParseNonNullStringValue, 
&iflvFROM, 
                                            "PRIORITY", HrParsePriorityValue, 
&iflvPRIORITY, 
                                            "SUBJECT", HrParseStringValue, 
&iflvSUBJECT, 
                                            "TIME", HrParseTimeValue, 
&iflvTIME, 
                                            "TO", HrParseNonNullStringValue, 
&iflvTO, 
                                            "XATTACH", HrParseNonNullStringValue, 
&iflvXATTACH, 
                                            "XTEXT", HrParseNonNullStringValue, 
&iflvXTEXT 
                                            }; 
static 
INIFILEPARSEINFO  aifpiNewPSTEntry[] =     { 
                                            "NAME", HrParseStringValue, 
&iflvPSTNAME, 
                                            "PASSWORD", HrParseStringValue, 
&iflvPSTPASSWORD, 
"ENCRYPTION", HrParseEncryptionValue, 
&iflvENCRYPTION, 
"PATH", HrParseNonNullStringValue, 
&iflvPATH 
                                            }; 
static 
INIFILEPARSEINFO  aifpiProfileEntry[] =     { 
                                            "NAME", HrParseNonNullStringValue, 
&iflvPROFILENAME, 
                                            "PASSWORD", HrParseStringValue, 
&iflvPROFILEPASSWORD 
                                            }; 
// Error handling variables: 
 
static ULONGcMsgs =0; 
static DWORDiLine =1; 
static DWORDiSection =1; 
 
// MAPI-related storage: 
 
static LPADRBOOKlpAdrBook =NULL; 
static LPMDBlpMdb =NULL; 
static LPMAPIFOLDERlpRootFolder =NULL; 
static LPMAPISESSIONlpSession =NULL; 
 
// character constants 
const CHARcSemiColon=';';// delimiter character or comment 
const CHARcBeginSectLine='[';// begins section line 
const CHAR cEndSectLine=']';// ends section line 
const CHARcEquals='=';// keyword equals value character 
const CHARcNewLine='\n';// new line character 
const CHAR cColon=':';// colon character 
const CHARcBackSlash='\\';// backslash character 
const CHARcDate='d';// date template character 
 
// string constants 
LPCSTRpszSeparator=";";// delimiter character 
 
// Application name 
static CHARszAppName[]="POPPST"; 
 
// Static inline "macros" 
// Used instead of #define macros, since they can do type checking 
 
// in-line static for determining if should process section 
static inline BOOL fSectionReady(// RETURNS: BOOL 
IN INIFILESECTIONTYPE ifstDesiredSection)// section want to process 
{ 
 // If current section is desired section and current section 
// is not commented out, we should try to process it. 
return ( ( (ifstCurSection == ifstDesiredSection) &&  
  (fCurSectionComment == FALSE) ) ? TRUE : FALSE ); 
 
} 
 
// 
// Functions 
// 
 
//$--INTERNAL_ERROR---------------------------------------------------------- 
//  Helper functions for EventLogMsg 
// -------------------------------------------------------------------------- 
static inline VOID INTERNAL_ERROR( 
    IN LPCTSTR str, 
    IN const HRESULT hr) 
{ 
    TCHAR szErrorCode[16] = {0}; 
 
    EventLogMsg( 
        POPPST_INTERNAL_ERROR, 
        2, str, _itot( hr, szErrorCode, 16), 
        0); 
} 
 
static inline LPCTSTR SZ_BASE10( 
    IN const INT iNumber) 
{ 
    static TCHAR szTemp[16] = {0}; 
    return( (LPCTSTR)_itot( iNumber, szTemp, 10)); 
} 
 
static inline LPCTSTR SZ_BASE16( 
    IN const ULONG ulNumber) 
{ 
    static TCHAR szTemp[16] = {0}; 
    return( (LPCTSTR)_itot( ulNumber, szTemp, 16)); 
} 
 
 
//$--DisplayUserMsg---------------------------------------------------------- 
// 
// DESCRIPTION: Utility to display message to command line user. 
// 
// INPUT:       hInst   --  program instance handle 
//              nResID  --  Resource string identifier 
//  
// RETURNS:     VOID 
// 
// ---------------------------------------------------------------------------- 
VOID DisplayUserMsg(            // RETURNS: nothing 
            IN UINT nResID,     // resource string identifier 
            ...)                // additional arguments 
{     
    INT     nRetCode    = 0;// return value 
    va_list vArgList    =   {0};        // variable argument list 
static HMODULEhModule=NULL;// application instance handle 
static BOOLfInit=FALSE;// TRUE if module handle has is initialized 
DWORDnLastErr=0;// last Win32 API error 
 
    const INTnMsgLength=   256;        // maximum length of printed messages 
 
// LoadString message buffer 
    CHAR  szMessage[nMsgLength]  =   ""; 
 
    DEBUGPRIVATE("DisplayUserMsg()\n"); 
 
    // No CHK_DisplayUserMsg() call. 
    // We check arguments as we go, as we may be in an error state. 
 
    // Check the instance handle 
    if ( fInit == FALSE ) 
    { 
// Retrieve this programs instance handle. 
hModule = GetModuleHandle(NULL); 
 
fInit = TRUE;// have retrieved instance handle 
 
// Print out error if have failed. 
if ( hModule == NULL ) 
{ 
nLastErr = GetLastError(); 
 
fprintf(stderr, "ERROR: Unable to display resource messages to console: " 
"error code 0x%lx,\n", nLastErr); 
 
HR_LOG(HRESULT_FROM_WIN32(nLastErr)); 
} 
    } 
 
// Check validity of module handle 
if ( hModule == NULL ) 
{ 
// can't do anything 
goto cleanup; 
} 
 
// retrieve resource string 
    nRetCode = LoadString( 
    hModule,  
    nResID,  
    szMessage,  
    nMsgLength); 
 
    if ( nRetCode == 0 ) 
    { 
        // Print out default error string. 
nLastErr = GetLastError(); 
 
fprintf(stderr, "ERROR: Can't load resource string %ld: " 
"error code 0x%lx,\n", nResID, nLastErr); 
 
        HR_LOG(HRESULT_FROM_WIN32(nLastErr)); 
 
        goto cleanup; 
    } 
 
    // Get an optional argument list pointer 
    va_start(vArgList, nResID); 
 
    // Print the message to standard out 
    vprintf(szMessage,          // format string 
            vArgList);          // variable argument list 
 
    va_end(vArgList); 
 
// we are done. 
 
cleanup: 
 
    // Return 
    return; 
 
}   // end DisplayUserMsg() 
 
//$--main-------------------------------------------------------------------- 
// 
// DESCRIPTION: poppst.exe main() routine. 
// 
// INPUT:       argc    --  argument count 
//              argv    --  command line arguments 
// 
// RETURNS:     INT --     0 on success; 
//                         E_INVALIDARG if invalid parameter, 
//                         E_OUTOFMEMORY if memory problems, 
//                         E_FAIL if internal error. 
// 
//--------------------------------------------------------------------------- 
 
INT main(               // RETURNS: INT 
    IN  INT     argc,       // # arguments on command line 
    IN  CHAR *  argv[]      // command line arguments array 
    ) 
{ 
    HRESULT hr  =   NOERROR; 
INT            iCounter        =0;// argument index 
BOOLbMAPIInitialized =FALSE; 
    LINEREADSTATUS      lrs =   LRS_OKAY; 
    BOOL    bIsSectionLine  =   FALSE;  // TRUE if the line is a section line 
HANDLEhEvent=NULL;// event logging handle 
EDKEVENTCOUNTsEventCount={0};// event log count structure 
BOOLfEventLogOpened=FALSE;// TRUE if event log is open 
 
    DEBUGPUBLIC("main()\n"); 
 
    // Check input parameters. 
hr = CHK_main(argc, argv); 
if ( FAILED(hr) ) 
goto cleanup; 
 
// parse comand line 
    if (argc == 1) 
    { 
// Display usage message 
DisplayUserMsg( 
IDS_USAGE); 
DisplayUserMsg( 
IDS_NEWLINE); 
DisplayUserMsg( 
IDS_USAGEFILE); 
DisplayUserMsg( 
IDS_USAGEHELP); 
 
        hr = HR_LOG(E_INVALIDARG); 
 
        goto cleanup; 
    } 
 
    // Check to see if user wants help (/? or /HELP flag) 
    for ( iCounter = 0; iCounter < argc; iCounter++ ) 
    { 
        ASSERT_STRING_PTR(argv[iCounter], "Bad argv[iCounter]"); 
 
        if ( argv[iCounter][0] == '/' || argv[iCounter][0] == '-') 
        { 
            if ( (argv[iCounter][1] == '?') ||  
                 (argv[iCounter][1] == 'H') || 
                 (argv[iCounter][1] == 'h') ) 
            { 
                // display help message 
DisplayUserMsg( 
IDS_HELP); 
DisplayUserMsg( 
IDS_NEWLINE); 
DisplayUserMsg( 
IDS_USAGE); 
DisplayUserMsg( 
IDS_NEWLINE); 
DisplayUserMsg( 
IDS_USAGEFILE); 
DisplayUserMsg( 
IDS_HELPDOC); 
 
                hr = HR_LOG(E_INVALIDARG); 
 
                goto cleanup; 
            } 
        } 
    }   // end for 
 
    // Check number of parameters.  (Should be 2, including the 
    // program name). 
    if ( argc != 2 ) 
    { 
DisplayUserMsg( 
IDS_PARAM_NUMBER); 
 
        hr = HR_LOG(E_INVALIDARG); 
 
        goto cleanup; 
    } 
 
    if ( FAILED(hr) ) 
        goto cleanup; 
 
// Open an event logging handle for this application. 
 hr = HrEventOpenLog( 
szAppName,// application name 
NULL,// executable name (computed) 
    NULL,// event message file (computed) 
NULL,// parameter message file 
NULL,// category message file 
&hEvent);// event logging handle 
 
if ( FAILED(hr) ) 
{ 
DisplayUserMsg( 
IDS_EVENT_LOG, 
hr); 
 
goto cleanup; 
} 
 
ASSERTERROR((hEvent != NULL), "Bad hEvent"); 
 
fEventLogOpened = TRUE; 
 
// Let user know that we are doing something. 
DisplayUserMsg( 
IDS_PROCESSING,  
argv[1]); 
 
    // open the INI file. 
    IniFile = fopen(argv[1], "r"); 
 
if ( IniFile == NULL ) 
{ 
EventLogMsg( 
POPPST_OPEN_FILE_ERROR, 
1,// # of string replacements 
argv[1], 
0);// # of Win32 error codes 
 
        hr = HR_LOG(E_INVALIDARG); 
        goto cleanup; 
} 
 
hr = MAPIInitialize(0); 
 
if (FAILED(hr)) 
{ 
EventLogMsg( 
POPPST_MAPIINIT_ERROR, 
1, SZ_BASE16(hr), 
0); 
 
        hr = HR_LOG(E_FAIL); 
 
goto cleanup; 
} 
 
bMAPIInitialized = TRUE; 
 
InitSectionData(FALSE); 
 
    while (TRUE) 
    { 
hr = HrGetNextLine(&lrs); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
if (lrs == LRS_OKAY) 
{ 
            hr = HrIsSectionLine(achIniLine, &bIsSectionLine); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
 
if ( bIsSectionLine ) 
{ 
if ( fSectionReady(IFST_MESSAGE) == TRUE ) 
{ 
    hr = HrProcessMessageSection(); 
 
                    if ( FAILED(hr) ) 
                    { 
                        goto cleanup; 
                    } 
} 
else if ( fSectionReady(IFST_NEWPST) == TRUE ) 
{ 
                    hr = HrProcessNewPSTSection(); 
 
if ( FAILED(hr) ) 
                    { 
goto cleanup; 
                    } 
} 
else if ( fSectionReady(IFST_PROFILE) == TRUE ) 
{ 
                    hr = HrProcessProfileSection(); 
 
if ( FAILED(hr) ) 
                    { 
goto cleanup; 
                    } 
} 
 
// If ifstCurSection == IFST_UNKNOWN, we don't have a valid 
// section at present, and do nothing. 
} 
 
            hr = HrParseLine(); 
 
        if ( FAILED(hr) ) 
        { 
EventLogMsg( 
POPPST_SYNTAX_ERROR, 
1, SZ_BASE10(iLine), 
0); 
            } 
} 
else if (lrs == LRS_LINETOOLONG) 
{ 
EventLogMsg( 
POPPST_LENGTH_ERROR, 
1, SZ_BASE10(iLine), 
0); 
} 
else 
{ 
break; 
} 
 
        iLine++; 
    } 
 
    if (lrs == LRS_EOF)  
    { 
if ( fSectionReady(IFST_MESSAGE) == TRUE ) 
{ 
hr = HrProcessMessageSection(); 
 
            if ( FAILED(hr) ) 
            { 
                goto cleanup; 
            } 
} 
else if ( fSectionReady(IFST_NEWPST) == TRUE ) 
{ 
            hr = HrProcessNewPSTSection(); 
 
if ( FAILED(hr) ) 
            { 
goto cleanup; 
            } 
} 
else if ( fSectionReady(IFST_PROFILE) == TRUE ) 
{ 
            hr = HrProcessProfileSection(); 
 
if ( FAILED(hr) ) 
            { 
goto cleanup; 
            } 
} 
 
    }// end if have reached end of file 
 
// Dertermine number of errors, warnings and informationsl 
// messages logged to the event log. 
hr = HrEventGetCounts( 
&sEventCount);// structure to hold event logging statistics 
 
if ( FAILED(hr) ) 
{ 
        INTERNAL_ERROR( TEXT("HrEventGetCounts()"), hr); 
 
sEventCount.cError = 1;// have at least one error 
} 
 
// if no errors, check for proper program termination state 
if ( sEventCount.cError == 0 ) 
{ 
if ( (lrs == LRS_EOF) && (ifstCurSection == IFST_UNKNOWN) ) 
{ 
// Have an empty INI file.   
// Print out error to the user. 
hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_EMPTY_ERROR, 
1, 
argv[1], 
0); 
 
goto cleanup; 
 
}// end if have an empty file 
 
    else if (lrs == LRS_IOERROR) 
    { 
EventLogMsg( 
POPPST_IO_ERROR, 
1, SZ_BASE10(iLine), 
0); 
 
goto cleanup; 
    } 
 
// Otherwise, we are successful 
   else 
    { 
// Log this fact. 
EventLogMsg( 
POPPST_POPULATE_SUCCEEDED, 
0, 
0); 
    } 
 
}// end if no error message printed out so far 
 
// We are done. 
 
cleanup: 
 
// Print out a completion message to the user. 
// First, determine the number of errors, warnings and informationsl 
// messages logged to the event log. 
if ( fEventLogOpened == TRUE ) 
{ 
ZeroMemory(&sEventCount, sizeof(sEventCount)); 
hr = HrEventGetCounts( 
&sEventCount);// structure to hold event logging statistics 
 
if ( FAILED(hr) ) 
{ 
            INTERNAL_ERROR( TEXT("HrEventGetCounts()"), hr); 
 
sEventCount.cError = 1;// have at least one error 
} 
 
if ( sEventCount.cError == 0 ) 
{ 
// print success message  
DisplayUserMsg( 
IDS_SUCCESS); 
} 
 
else if ( sEventCount.cError == 1 ) 
{ 
// print one error message 
DisplayUserMsg( 
IDS_1ERROR); 
} 
 
else  
{ 
// print multiple error message 
DisplayUserMsg( 
IDS_ERRORS); 
} 
 
if ( sEventCount.cWarning == 1 ) 
{ 
// print out one warning message 
DisplayUserMsg( 
IDS_1WARNING); 
} 
 
else if ( sEventCount.cWarning > 1 ) 
{ 
// print out mutliple warnings message 
DisplayUserMsg( 
IDS_WARNINGS); 
} 
 
// close event log 
(VOID)HrEventCloseLog(); 
hEvent = NULL; 
fEventLogOpened = FALSE; 
 
}// end if have event log handle 
 
// Release MAPI and OLE objects 
ULRELEASE(lpAdrBook); 
ULRELEASE(lpRootFolder); 
ULRELEASE(lpMdb); 
 
if (lpSession) 
{ 
    lpSession->Logoff(0, 0, 0); 
ULRELEASE(lpSession); 
} 
 
if (fTmpProf) 
{ 
HrCleanupPSTGlobals(lpszProfileName); 
 
        MAPIFREEBUFFER(lpszProfileName); 
MAPIFREEBUFFER(lpszProfilePassword); 
} 
 
if (bMAPIInitialized) 
    { 
    MAPIUninitialize(); 
    } 
 
    if (IniFile) 
    { 
        fclose(IniFile); 
    } 
 
    // return the appropriate exit code, based on our HRESULT. 
    return _nEcFromHr(hr); 
} 
 
//$--HrCreateMessageAttachment----------------------------------------------- 
// 
// DESCRIPTION: Create a message attachment and write the contents of the 
//specified file to it.  Also create various required properties 
//for the message attachment. 
// 
// INPUT: 
// 
//[lpMsg]-- Message for which attachment is to be created. 
//[lpszAttachPath]-- Path of file containing attachment text. 
// 
// RETURNS: HRESULT --  NOERROR if O.K.,  
//                      E_INVALIDARG if invalid parameter, 
//                      E_OUTOFMEMORY if memory problems, 
//                      E_FAIL if call fails 
// 
//--------------------------------------------------------------------------- 
 
 
HRESULT HrCreateMessageAttachment(          // RETURNS: HRESULT 
INLPMESSAGElpMsg,              // MAPI message pointer 
INLPSTRlpszAttachPath      // list of attachment file names 
) 
{ 
    HRESULT         hr              =   NOERROR;    // return code 
ULONGcb              =   0; 
LPATTACHlpAttach        =NULL; 
ULONGulAttachmentNum =   0; 
    ULONG           nAttachments    =   0;      // # of attachments 
    ULONG           iAttach         =   0;      // attachment index 
    LPSTR           lpszCurrent     =   NULL;   // current attachment 
LPSTR *lppszAttachTok=NULL;// array of tokenized attachments 
 
    // MAPI property creation flags.     
    const ULONG     ulFlags     =   MAPI_CREATE | MAPI_MODIFY |  
                                    MAPI_DEFERRED_ERRORS;   // reduces RPCs 
 
    // Buffer read/write size 
    const UINT      cbBufSize           =   4096; 
 
    // read/write data buffer 
BYTEab[cbBufSize]       =   {0}; 
 
    // # of attachment properties 
    const ULONGcValues             =3; 
 
    // property value array 
    SPropValue      aPropVals[cValues]  =   {0}; 
 
    DEBUGPRIVATE("HrCreateMessageAttachment()\n"); 
 
    // Check input parameters 
    hr = CHK_HrCreateMessageAttachment(lpMsg, lpszAttachPath); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    // Determine number of attachments by breaking into tokens 
hr = HrStrTokAll( 
lpszAttachPath,// file name list 
(LPSTR) pszSeparator,// separator character 
&nAttachments,// # of file names 
&lppszAttachTok);// array of strings pointer 
 
if ( FAILED(hr) ) 
{ 
        INTERNAL_ERROR( TEXT("HrStrTokAll()"), hr); 
 
goto cleanup; 
} 
 
    ASSERTERROR((nAttachments != 0), "Bad nAttachments"); 
ASSERT_READ_PTR(lppszAttachTok, sizeof(LPSTR) * nAttachments,  
"Bad lppszAttachTok"); 
 
    // Process each attachment. 
    for ( iAttach = 0; iAttach < nAttachments; iAttach++ ) 
    { 
        // Release MAPI objects 
    ULRELEASE(lpAttach); 
 
// Retrieve next attachment 
lpszCurrent = lppszAttachTok[iAttach]; 
 
ASSERT_STRINGA_PTR(lpszCurrent, "Bad lpszCurrent"); 
 
    // Create the attachment in the message. 
    hr = lpMsg->CreateAttach(NULL, 
 MAPI_DEFERRED_ERRORS,  // reduces RPCs 
 &ulAttachmentNum, 
 &lpAttach); 
 
    if ( FAILED(hr) ) 
    {   
            INTERNAL_ERROR( TEXT("IMessage::CreateAttach()"), hr); 
 
            hr = HR_LOG(E_FAIL); 
 
    goto cleanup; 
    } 
 
        ASSERT_IUNKNOWN_PTR(lpAttach, "Bad lpAttach"); 
         
    // Create required additional properties. 
LPSPropProblemArraylpProblems =NULL; 
LPSTRlpszFileName    =   NULL; 
 
aPropVals[0].ulPropTag =PR_ATTACH_METHOD; 
aPropVals[0].Value.l =ATTACH_BY_VALUE; 
 
lpszFileName = strrchr(lpszCurrent, cBackSlash); 
 
if (!lpszFileName) 
lpszFileName = lpszCurrent; 
else 
lpszFileName++;// Advance past the backslash. 
 
// CAVEAT - We don't do anything here to check for invalid FAT 8.3 
//    names.  This may be a good idea. 
aPropVals[1].ulPropTag =PR_ATTACH_FILENAME; 
aPropVals[1].Value.lpszA =lpszFileName; 
 
aPropVals[2].ulPropTag =PR_ATTACH_LONG_FILENAME; 
aPropVals[2].Value.lpszA =lpszFileName; 
 
hr = lpAttach->SetProps(cValues, aPropVals, &lpProblems); 
 
if (FAILED(hr)) 
{ 
            INTERNAL_ERROR( TEXT("IMAPIProp::SetProps()"), hr); 
 
            hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
if (lpProblems != NULL) 
{ 
ULONGi   =   0; 
for (i = 0; i < lpProblems->cProblem; i++) 
{ 
                INTERNAL_ERROR( TEXT("IMAPIProp::SetProps()"), lpProblems->aProblem[i].scode); 
} 
 
MAPIFREEBUFFER(lpProblems); 
 
            hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
        // Create the PR_ATTACH_DATA_BIN property from the attachment 
        // file text. 
        hr = HrMAPISetPropFromFile( 
            lpAttach,               // pointer to attachment object 
            PR_ATTACH_DATA_BIN,     // property tag 
            lpszCurrent,            // fully-pathed file name from which to read data 
            &cb);                   // # bytes read in 
 
        if ( FAILED(hr) ) 
        { 
            INTERNAL_ERROR( TEXT("HrMAPISetPropFromFile()"), hr); 
 
            goto cleanup; 
        } 
 
    // Commit the attachment changes. 
    hr = lpAttach->SaveChanges(0); 
 
    if (FAILED(hr)) 
    { 
            INTERNAL_ERROR( TEXT("IMAPIProp::SaveChanges()"), hr); 
 
            hr = HR_LOG(E_FAIL); 
    goto cleanup; 
    } 
    }   // end for each attachment 
 
    // we are done 
 
cleanup: 
 
    // Release MAPI and OLE objects 
ULRELEASE(lpAttach); 
 
// Free MAPI buffers 
MAPIFREEBUFFER(lppszAttachTok); 
 
RETURN(hr); 
 
} 
 
//$--HrGetNextLine----------------------------------------------------------- 
// 
// DESCRIPTION: Get the next input line from the ini file and deposit it in 
//the input buffer (achIniLine). 
// 
// OUTPUT:  LINEREADSTATUS *    --  pointer to line read status 
// 
//  LRS_OKAY if line was read and is available. 
//  LRS_EOF if we are at end of file; no more lines. 
//  LRS_IOERROR if there was an I/O error in the last read. 
//  LRS_LINETOOLONG if last line was too long to fit in the buffer. 
// 
// RETURNS: HRESULT --  NOERROR if successful, 
//                      E_INVALIDARG if bad parameter, 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrGetNextLine(                  // RETURNS: HRESULT 
    OUT LINEREADSTATUS * lpReadStatus)  // pointer to line read status 
{ 
    HRESULT         hr  =   NOERROR;    // return code 
    INT             ch  =   0; 
    CHAR *          pch =       &achIniLine[0]; 
    CHAR *          pchInv =    &achIniLine[cbMaxIniLine]; 
 
    DEBUGPRIVATE("HrGetNextLine()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrGetNextLine(lpReadStatus); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    *lpReadStatus = LRS_OKAY; 
 
    while (TRUE) 
    { 
        if (pch == pchInv) 
        { 
            // Line is too long 
            *lpReadStatus =           LRS_LINETOOLONG; 
            achIniLine[0] = 0; 
            while ((ch = getc(IniFile)) != EOF && (ch != cNewLine)); 
 
            break; 
        } 
 
        ch = getc(IniFile); 
 
        if (ch == cNewLine) 
        { 
            // Just read end of this line 
            *lpReadStatus =   LRS_OKAY; 
            *pch =  0; 
 
            break; 
        } 
 
        if (ch == EOF) 
        { 
            // Just read last line in file 
            // We want to process the last line, if any. 
            *pch = 0; 
 
            if ( pch == &achIniLine[0] ) 
            { 
                // we have passed the last line in the file. 
                *lpReadStatus = feof(IniFile) ? LRS_EOF : LRS_IOERROR; 
            } 
 
            else 
            { 
                // we just read last line.  We want to process 
                // its data. 
                *lpReadStatus = feof(IniFile) ? LRS_OKAY : LRS_IOERROR; 
            } 
 
            break; 
 
        }   // end if end of file reached 
 
        *pch = ch; 
        pch++; 
    } 
 
    RETURN(hr); 
 
} 
 
 
//$--InitSectionData--------------------------------------------------------- 
// 
// DESCRIPTION: Initialize static variables that will be used in parsing an 
//ini file section to default values. 
// 
// INPUT:       None. 
// 
// RETURNS:     Nothing. 
// 
//--------------------------------------------------------------------------- 
 
VOID InitSectionData(           // RETURNS: VOID 
IN BOOL fCommented)// TRUE if section is commented out 
{ 
    DEBUGPRIVATE("InitSectionData()\n"); 
 
// Record whether or not section is commented out 
fCurSectionComment = fCommented; 
 
ifstCurSection =IFST_UNKNOWN; 
 
iflvBCC.fValid =FALSE; 
iflvCC.fValid =FALSE; 
iflvDATE.fValid =FALSE; 
iflvENCRYPTION.fValid =FALSE; 
iflvFOLDER.fValid =FALSE; 
iflvFROM.fValid =FALSE; 
iflvPATH.fValid =FALSE; 
iflvPRIORITY.fValid =FALSE; 
iflvPROFILENAME.fValid =FALSE; 
iflvPROFILEPASSWORD.fValid =FALSE; 
iflvPSTNAME.fValid =FALSE; 
iflvPSTPASSWORD.fValid =FALSE; 
iflvSUBJECT.fValid =FALSE; 
iflvTIME.fValid =FALSE; 
iflvTO.fValid =FALSE; 
iflvXATTACH.fValid =FALSE; 
iflvXTEXT.fValid =FALSE; 
} 
 
 
//$--HrIsSectionLine----------------------------------------------------------- 
// 
// DESCRIPTION: Determine if an input line is a section header (ie., first 
//non-whitespace char is '['). 
// 
// INPUT:       [pch]-- Ptr to input line. 
// 
// OUTPUT:      lpbIsSectionLine    --  BOOLEAN pointer.  TRUE if is section line. 
// 
// RETURNS:     HRESULT --  NOERROR if successful 
//                          E_INVALIDARG if bad parameter 
//--------------------------------------------------------------------------- 
 
HRESULT HrIsSectionLine(                    // RETURNS: HRESULT 
    IN      CHAR *  pch,                    // input line 
    OUT     BOOL * lpbIsSectionLine     // boolean pointer 
    ) 
{ 
    HRESULT hr  =   NOERROR;    // return code 
 
    DEBUGPRIVATE("HrIsSectionLine()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrIsSectionLine(pch, lpbIsSectionLine); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
// skip leading white space and comment characters 
    while ( (isspace(*pch) > 0) || (*pch == cSemiColon) ) 
{ 
        pch++; 
} 
 
*lpbIsSectionLine = (*pch == cBeginSectLine);// All sections must start with an '[' 
 
    RETURN(hr); 
 
} 
 
 
//$--HrOpenFolder------------------------------------------------------------ 
// 
// DESCRIPTION: Open the folder specified by the iflvFOLDER static variable. 
// 
// INPUT:       None. 
// 
// OUTPUT:      lppFolder   --  folder pointer pointer 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//              E_INVALIDARG if invalid parameters, 
//              E_FAIL otherwise 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrOpenFolder(                   // RETURNS: HRESULT 
    OUT LPMAPIFOLDER * lppFolder)   // output folder pointer pointer 
{ 
    HRESULT         hr          =   NOERROR;   
    UINT            cchFolder   =   0;      // # characters in folder 
 
    const CHAR      chSep    =   cBackSlash;      // separator character 
 
    DEBUGPRIVATE("HrOpenFolder()\n"); 
 
    // Check input parameters 
    hr = CHK_HrOpenFolder(lppFolder); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
     
    *lppFolder = NULL;  // initialize 
 
    // Strip the trailing '\', if any. 
    cchFolder = lstrlen(iflvFOLDER.achValue); 
 
    ASSERTERROR((cchFolder > 0), "Bad cchFolder"); 
 
    if ( iflvFOLDER.achValue[cchFolder - 1] == cBackSlash ) 
    { 
        iflvFOLDER.achValue[cchFolder - 1] = 0; 
    } 
 
    // Determine if getting root folder 
if (iflvFOLDER.achValue[0] == 0) 
    { 
*lppFolder = lpRootFolder; 
         
        goto cleanup; 
    } 
 
    // Otherwise, opent the pathed folder. 
    hr = HrMAPIOpenFolderEx( 
        lpMdb,                      // message store pointer 
        chSep,                      // folder path separator character 
        iflvFOLDER.achValue,        // folder path 
        lppFolder);                 // pointer to folder opened 
 
    if ( FAILED(hr) ) 
    { 
EventLogMsg( 
POPPST_FOLDER_ERROR, 
2, iflvFOLDER.achValue, SZ_BASE16(hr), 
            0); 
 
        goto cleanup; 
    } 
 
cleanup: 
 
RETURN(hr); 
 
} 
 
 
//$--HrParseDateValue-------------------------------------------------------- 
// 
// DESCRIPTION: Parse a DATE= entry for the value. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// RETURNS:     HRESULT --  NOERROR if success, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL if call fails. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseDateValue(           // RETURNS: HRESULT 
    IN      CHAR *  pch             // date string 
    ) 
{ 
    HRESULT hr  =   NOERROR;    // return value 
    static CHAR    DateTemplate[] = "dddd-dd-dd"; 
    INT     i = 0; 
 
    DEBUGPRIVATE("HrParseDateValue()\n"); 
 
    // Check input parameters 
    hr = CHK_HrParseDateValue(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    for (i = 0; i < sizeof(DateTemplate); i++) 
    { 
        if (DateTemplate[i] == cDate) 
        { 
            if (!isdigit(*(pch + i))) 
            { 
                hr = HR_LOG(E_FAIL); 
 
                goto cleanup; 
            } 
        } 
        else 
        { 
            if (DateTemplate[i] != *(pch + i)) 
            { 
                hr = HR_LOG(E_FAIL); 
 
                goto cleanup; 
            } 
        } 
    } 
 
// Given that we can't REALLY do range checking without knowing about 
// leap years, we rely on SystemTimeToFileTime() to later do complete range 
// checking on the year/month/day values.  We can cast the values to words 
// without a problem, given we know there are a most 4 decimal digits. 
 
stCurMsg.wYear =(WORD)atol(pch); 
stCurMsg.wMonth =(WORD)atol(pch + 5); 
stCurMsg.wDay =(WORD)atol(pch + 8); 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseEncryptionValue---------------------------------------------------- 
// 
// DESCRIPTION: Parse an ENCRYPTION= entry for the value. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL if call fails 
// 
//--------------------------------------------------------------------------- 
HRESULT HrParseEncryptionValue(         // RETURNS: HRESULT 
    IN      CHAR *  pch                 // input encryption string 
    ) 
{ 
    HRESULT hr  =   NOERROR;    // return value 
 
    DEBUGPRIVATE("HrParseEncryptionValue()\n"); 
 
    // check input parameters 
    hr = CHK_HrParseEncryptionValue(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
if (!_stricmp(pch, "ON")) 
bEncryptionNewPST = TRUE; 
else if (!_stricmp(pch, "OFF")) 
bEncryptionNewPST = FALSE; 
else 
    { 
    hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseEntryLine-------------------------------------------------------- 
// 
// DESCRIPTION: Parse an ini file section entry line to determine what kind of 
//entry it is, and store the value.  Section entry lines are the 
//lines that follow the section header. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL if call fails. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseEntryLine(       // RETURNS: HRESULT 
    IN      CHAR *  pch         // input line 
    ) 
{ 
    HRESULT             hr  =                   NOERROR; 
    INIFILEPARSEINFO *  aifpiEntry  =           NULL; 
    CHAR                chTmp   =               0; 
    INT                 cKeywords   =           0; 
    INT                 i   =                   0; 
    CHAR *              pchKeyword  =           NULL; 
 
    DEBUGPRIVATE("HrParseEntryLine()\n"); 
 
    // Check input parameters 
    hr = CHK_HrParseEntryLine(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    if (ifstCurSection == IFST_MESSAGE) 
    { 
        aifpiEntry = aifpiMessageEntry; 
        cKeywords = sizeof(aifpiMessageEntry)/sizeof(aifpiMessageEntry[0]); 
    } 
    else if (ifstCurSection == IFST_NEWPST) 
    { 
        aifpiEntry = aifpiNewPSTEntry; 
        cKeywords = sizeof(aifpiNewPSTEntry)/sizeof(aifpiNewPSTEntry[0]); 
    } 
    else if (ifstCurSection == IFST_PROFILE) 
    { 
        aifpiEntry = aifpiProfileEntry; 
        cKeywords = sizeof(aifpiProfileEntry)/sizeof(aifpiProfileEntry[0]); 
    } 
    else 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    // Save ptr to keyword. 
 
    pchKeyword = pch; 
 
    // Scan for end of keyword and delimit keyword, saving replaced char. 
 
    while (*pch != cEquals && *pch != 0) 
        pch++; 
 
    if (*pch == 0) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    chTmp = *pch; 
    *pch  = 0; 
 
    // Compare putative keyword against valid section keywords. 
 
    for (i = 0; i < cKeywords; i++) 
        if (!_stricmp(pchKeyword, aifpiEntry[i].pszKeyword)) 
            break; 
 
    *pch = chTmp; 
 
    if (i == cKeywords) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    hr = aifpiEntry[i].HrParseFunction(pch + 1); 
 
    // Store result if success. 
 
    if ( SUCCEEDED(hr) ) 
    { 
// Check for duplicate entries in a section: 
 
if (aifpiEntry[i].piflv->fValid) 
        { 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
        } 
 
aifpiEntry[i].piflv->fValid = TRUE; 
        strcpy(aifpiEntry[i].piflv->achValue, pch + 1); 
    } 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseLine--------------------------------------------------------------- 
// 
// DESCRIPTION: Parse an ini file line, and store the value if it is a section 
//entry line. 
// 
// INPUT:       None. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_FAIL otherwise. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseLine(VOID)       // RETURNS: HRESULT 
{ 
    HRESULT     hr  =       NOERROR; 
    CHAR *      pch =       &achIniLine[0]; 
BOOLfCommentedSection=FALSE;// TRUE if see commented section 
 
    DEBUGPRIVATE("HrParseLine()\n"); 
 
    while ( isspace(*pch) > 0 )// Skip leading line whitespace. 
        pch++; 
 
    if (*pch == 0)               // It is a blank line. 
    { 
        goto cleanup; 
    } 
 
    if (*pch == cSemiColon)           // It is a comment. 
    { 
// Skip intervening white spaces and comments 
while ( (isspace(*pch) > 0) || (*pch == cSemiColon) ) 
{ 
pch++; 
} 
 
// Check to see if the commented out line is a section header. 
// If if is, then the effect is that the entire section is 
// commented out. 
if ( *pch == cBeginSectLine ) 
{ 
// have a commented-out section 
fCommentedSection = TRUE; 
 
// continue on to next if statement 
} 
 
else 
{ 
// commented out line, ignore it. 
        goto cleanup; 
} 
    } 
 
    if ( *pch == cBeginSectLine )  // It is a section, or is invalid 
    { 
iSection = iLine; 
        hr = HrParseSectionLine( 
        pch + 1, // pointer to section keyword 
        fCommentedSection);// TRUE if have a commented-out section 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
    } 
    else                            // It is a section entry, or is invalid. 
    { 
        hr = HrParseEntryLine(pch); 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
    } 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseNonNullStringValue----------------------------------------------- 
// 
// DESCRIPTION: Parse a non-null string value for an entry line (ie., the 
//null string is not a permissible value). 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// HRESULT:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if invalid parameter, 
//                          E_FAIL if call fails 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseNonNullStringValue(          // RETURNS: HRESULT 
    IN      CHAR *  pch                     // input line 
    ) 
{ 
    HRESULT hr  =   NOERROR; 
 
    DEBUGPRIVATE("HrParseNonNullStringValue()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrParseNonNullStringValue(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    if ( *pch == 0 ) 
    { 
        hr = HR_LOG(E_FAIL); 
    } 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParsePriorityValue---------------------------------------------------- 
// 
// DESCRIPTION: Parse a PRIORITY= section entry line. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// HRESULT:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL if call fails 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParsePriorityValue(           // RETURNS: HRESULT 
    IN      CHAR *  pch                 // input line 
    ) 
{ 
    HRESULT hr  =   NOERROR;    // return value 
 
    DEBUGPRIVATE("HrParsePriorityValue()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrParsePriorityValue(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
if (!_stricmp(pch, "NORMAL")) 
    { 
lPriorityCurMsg = PRIO_NORMAL; 
        lImportanceCurMsg = IMPORTANCE_NORMAL; 
    } 
else if (!_stricmp(pch, "HIGH")) 
    { 
lPriorityCurMsg = PRIO_URGENT; 
        lImportanceCurMsg = IMPORTANCE_HIGH; 
    } 
else if (!_stricmp(pch, "LOW")) 
    { 
lPriorityCurMsg = PRIO_NONURGENT; 
        lImportanceCurMsg = IMPORTANCE_LOW; 
    } 
else 
    { 
        hr = HR_LOG(E_FAIL); 
    } 
 
RETURN(hr); 
 
} 
 
 
//$--HrParseSectionLine------------------------------------------------------ 
// 
// DESCRIPTION: Parse an ini file section header line.  
// 
// INPUT:       [pch]-- Ptr to section keyword 
//fComment--TRUE if section is commented-out 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if invalid input, 
//                          E_FAIL if call fails. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseSectionLine( 
    IN      CHAR *  pch,            // input line 
INBOOL fComment// TRUE if section is commented 
    ) 
{ 
    HRESULT     hr  =   NOERROR; 
    CHAR        chTmp   =   0; 
    CHAR *      pchKeyword  =   NULL; 
 
    DEBUGPRIVATE("HrParseSectionLine()\n"); 
 
    // Check input parameters 
    hr = CHK_HrParseSectionLine(pch, fComment); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
InitSectionData( 
fComment);// TRUE if section is commented 
 
    // Save ptr to keyword. 
 
    pchKeyword = pch; 
 
    // Scan for end of keyword and delimit keyword, saving replaced char. 
 
    while (!isspace(*pch) && *pch != cEndSectLine && *pch != 0) 
        pch++; 
 
    if (*pch == 0) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    chTmp = *pch; 
    *pch  = 0; 
 
    // Compare putative keyword against valid section keywords. 
 
    if (!_stricmp(pchKeyword, "message")) 
    { 
// Numeric value may follow "message" keyword.  We don't use it, 
// but we do a rough check that what we are seeing is consistent 
// with what we expect. 
 
        *pch = chTmp; 
 
        while ( isspace(*pch) > 0 ) 
            pch++; 
 
        if (!isdigit(*pch)) 
        { 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
        } 
 
pch++; 
 
        while (isdigit(*pch)) 
            pch++; 
 
        // Check for proper section entry termination. 
 
        if (*pch == cEndSectLine && *(pch + 1) == 0) 
        { 
            ifstCurSection =IFST_MESSAGE; 
        } 
    } 
    else if (!_stricmp(pchKeyword, "newpst")) 
    { 
        // Check for proper section entry termination. 
        // NewPST sections don't have a value. 
 
        *pch = chTmp; 
 
        if (*pch == cEndSectLine && *(pch + 1) == 0) 
        { 
            ifstCurSection =IFST_NEWPST; 
        } 
    } 
    else if (!_stricmp(pchKeyword, "profile")) 
    { 
        // Check for proper section entry termination. 
        // Profile sections don't have a value. 
 
        *pch = chTmp; 
 
        if (*pch == cEndSectLine && *(pch + 1) == 0) 
        { 
            ifstCurSection =IFST_PROFILE; 
        } 
    } 
    else 
    { 
        hr = HR_LOG(E_FAIL); 
        *pch = chTmp;   // Invalid section keyword. 
 
        goto cleanup; 
 
    } 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseStringValue-------------------------------------------------------- 
// 
// DESCRIPTION: Parse a string value for an entry line. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// HRESULT:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseStringValue(     // RETURNS: HRESULT 
    IN      CHAR *  pch         // input line 
    ) 
{ 
    HRESULT hr  =   NOERROR; 
 
    DEBUGPRIVATE("HrParseStringValue()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrParseStringValue(pch); 
     
    // Anything is a valid string! 
 
    RETURN(hr); 
 
} 
 
 
//$--HrParseTimeValue-------------------------------------------------------- 
// 
// DESCRIPTION: Parse a TIME= section entry line. 
// 
// INPUT:       [pch]-- Ptr to input string. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL if call fails. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrParseTimeValue(           // RETURNS: HRESULT 
    IN      CHAR *  pch             // input line 
    ) 
{ 
    HRESULT hr  =   NOERROR; 
    long    Hour    =   0; 
    long    Minute  =   0; 
    long    Second = 0; 
 
    DEBUGPRIVATE("HrParseTimeValue()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrParseTimeValue(pch); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    if (!isdigit(*pch)          || 
        !isdigit(*(pch + 1))    || 
        *(pch + 2) != cColon) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    Hour = atol(pch); 
 
    if (Hour > 23) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
stCurMsg.wHour = (WORD)Hour; 
 
    pch += 3; 
 
    if (!isdigit(*pch)                              || 
        !isdigit(*(pch + 1))                        || 
        (*(pch + 2) != cColon && *(pch + 2) != 0)) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
    Minute = atol(pch); 
 
    if (Minute > 59) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
stCurMsg.wMinute = (WORD)Minute; 
 
    if (*(pch + 2) == cColon) 
    { 
        pch += 3; 
 
        if (!isdigit(*pch)                              || 
            !isdigit(*(pch + 1))                        || 
            *(pch + 2) != 0) 
        { 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
        } 
 
        Second = atol(pch); 
 
        if (Second > 59) 
        { 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
        } 
    } 
 
stCurMsg.wSecond = (WORD)Second; 
 
    if (Hour * 60L * 60L + Minute * 60L + Second >= 24L * 60L * 60L) 
    { 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
cleanup: 
 
    RETURN(hr); 
 
} 
 
//$--HrProcessMessageSection--------------------------------------------------- 
// 
// DESCRIPTION: Process the information parsed in a message section, creating 
//a message in a pst. 
// 
// INPUT:       None. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_FAIL otherwise. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrProcessMessageSection(VOID)        // RETURNS: HRESULT 
{ 
HRESULThr  =   NOERROR; 
LPADRLISTlpRecips =NULL; 
LPMAPIFOLDERlpFolder =NULL; 
LPMESSAGElpMsg =NULL; 
 
    DEBUGPRIVATE("HrProcessMessageSection()\n"); 
 
// Confirm that a NewPST or Profile section has been parsed and a MAPI 
// session has been established. 
if (!lpSession) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_ORDER_ERROR, 
1, SZ_BASE10(iSection), 
0); 
 
goto cleanup; 
} 
 
// Confirm that the Message section has the required section entries. 
 
if (!iflvDATE.fValid|| 
!iflvFOLDER.fValid|| 
!iflvFROM.fValid|| 
!iflvPRIORITY.fValid|| 
!iflvSUBJECT.fValid|| 
!iflvTIME.fValid|| 
!iflvTO.fValid) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_MISSING_ERROR, 
2, TEXT("Message"), SZ_BASE10(iSection), 
0); 
 
goto cleanup; 
} 
 
// Convert message date/time to FILETIME. 
 
if (!SystemTimeToFileTime(&stCurMsg, &ftCurMsg)) 
{ 
        INTERNAL_ERROR( TEXT("SystemTimeToFileTime()"), GetLastError()); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
// Open the specified folder, or if it does not exist, create it. 
// If the root folder was specified, the value of pRootFolder will 
// be returned. 
    hr = HrOpenFolder(&lpFolder); 
 
    if ( FAILED(hr) ) 
    { 
        goto cleanup; 
    } 
 
// Create the message. 
hr = lpFolder->CreateMessage(NULL, MAPI_DEFERRED_ERRORS, &lpMsg); 
 
if (FAILED(hr)) 
{ 
        INTERNAL_ERROR( TEXT("IMAPIFolder::CreateMessage()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
// Set the sender, subject, date/time, and priority properties. 
    hr = HrSetMessageProperties(lpMsg); 
 
    if ( FAILED(hr) ) 
    { 
goto cleanup; 
    } 
 
// Write the message text, if any 
hr = HrWriteMessageText(lpMsg, iflvXTEXT.achValue); 
 
    if ( FAILED(hr) ) 
    { 
goto cleanup; 
    } 
 
// Resolve recipient name information. 
hr = HrResolveRecipientNames(&lpRecips); 
 
    if ( FAILED(hr) ) 
    { 
goto cleanup; 
    } 
 
// Set recipient info. 
hr = lpMsg->ModifyRecipients(MODRECIP_ADD, lpRecips); 
 
if (FAILED(hr)) 
{ 
        INTERNAL_ERROR( TEXT("IMessage::ModifyRecipients()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
// If there is an attachment, create it. 
if (iflvXATTACH.fValid) 
{ 
hr = HrCreateMessageAttachment(lpMsg, iflvXATTACH.achValue); 
 
        if ( FAILED(hr) ) 
        { 
goto cleanup; 
        } 
} 
 
    // save the message! 
hr = lpMsg->SaveChanges(0); 
 
if (FAILED(hr)) 
{ 
        INTERNAL_ERROR( TEXT("IMAPIProp::SaveChanges()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
    goto cleanup; 
    } 
 
// Done! 
cMsgs++; 
 
cleanup: 
 
if (lpRecips) 
{ 
// Have to release the SPropVal allocations first. 
ULONGi; 
 
for (i = 0; i < lpRecips->cEntries; i++) 
MAPIFREEBUFFER(lpRecips->aEntries[i].rgPropVals); 
 
MAPIFREEBUFFER(lpRecips); 
} 
 
ULRELEASE(lpMsg); 
 
if (lpFolder && lpFolder != lpRootFolder) 
ULRELEASE(lpFolder); 
 
    RETURN(hr); 
 
} 
 
 
//$--HrProcessNewPSTSection---------------------------------------------------- 
// 
// DESCRIPTION: Process the information parsed in a newpst section, creating 
//a temporary profile and new pst and logging onto a MAPI session 
//for the temporary profile.  Also open the new pst and its 
//root folder. 
// 
// INPUT:       None. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_FAIL otherwise. 
// 
// Notes:If this function fails, the error should be treated as a 
//fatal error. 
//--------------------------------------------------------------------------- 
 
HRESULT HrProcessNewPSTSection(VOID)        // RETURNS: HRESULT 
{ 
HRESULThr  =   NOERROR; 
 
    DEBUGPRIVATE("HrProcessNewPSTSection()\n"); 
 
// Confirm that this is the first NewPST section we have encountered, 
// and there was not a Profile section. 
if (lpSession) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_MUTUAL_EXCLUSIVENESS_ERROR, 
1, SZ_BASE10(iSection), 
0); 
 
goto cleanup; 
} 
 
// Confirm that it has the required section entries. 
 
if (!iflvENCRYPTION.fValid|| 
!iflvPATH.fValid|| 
!iflvPSTNAME.fValid|| 
!iflvPSTPASSWORD.fValid) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_MISSING_ERROR, 
2, TEXT("NewPST"), SZ_BASE10(iSection), 
            0); 
 
goto cleanup; 
} 
 
fTmpProf = TRUE; // Flag used to indicate we are using a temporary profile. 
 
// Create the requested PST.  This also creates a new profile, and we must 
// get a session on the new profile in order to further manipulate the PST. 
hr = HrCreatePersonalStore(iflvPATH.achValue, 
  bEncryptionNewPST ? 
  PSTF_COMPRESSABLE_ENCRYPTION : 
  PSTF_NO_ENCRYPTION, 
  iflvPSTNAME.achValue, 
  iflvPSTPASSWORD.achValue, 
  &lpszProfileName, 
  &lpszProfilePassword); 
 
if (FAILED(hr)) 
{ 
EventLogMsg( 
POPPST_NEWPST_ERROR, 
2, iflvPSTNAME.achValue, SZ_BASE16(hr), 
0); 
 
goto cleanup; 
} 
 
// Save profile values (they will be used later by HrProcessProfileSection()). 
 
iflvPROFILENAME.fValid = TRUE; 
 
strcpy(iflvPROFILENAME.achValue, lpszProfileName); 
 
iflvPROFILEPASSWORD.fValid = TRUE; 
 
strcpy(iflvPROFILEPASSWORD.achValue, lpszProfilePassword); 
 
// Log onto a session. 
 
hr = HrProcessProfileSection(); 
 
    if ( FAILED(hr) ) 
    { 
goto cleanup; 
    } 
 
    // warn the user that a temporary profile is being used 
EventLogMsg( 
POPPST_TEMPORARY_PROFILE_WARNING, 
0, 
0); 
 
cleanup: 
 
RETURN(hr); 
 
} 
 
 
//$--HrProcessProfileSection------------------------------------------------- 
// 
// DESCRIPTION: Process the information parsed in a profile section, logging 
//onto a MAPI profile session and opening the message store, its 
//root folder, and the pab if one is available. 
// 
// INPUT:       None. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_FAIL otherwise. 
// 
// Notes:If this function fails, the error should be treated as a 
//fatal error.  On successful completion, lpSession, lpMdb, and 
//lpRootFolder are valid.  These are all opened here and left 
//open because they need to be open during message processing. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrProcessProfileSection(VOID)       // RETURNS: HRESULT 
{ 
HRESULThr          =   NOERROR; 
LPSRowSetlpRows      =NULL; 
    ULONG           cbEid       =   0;      // # bytes in entry identifier 
    LPENTRYID       lpEid       =   NULL;   // entry identifier 
    ULONG    ulObjType   =   0;      // MAPI object type 
 
    DEBUGPRIVATE("HrProcessProfileSection()\n"); 
 
// Confirm that this is the first Profile section we have encountered, 
// and there was not a NewPST section. 
if (lpSession) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_MUTUAL_EXCLUSIVENESS_ERROR, 
1, SZ_BASE10(iSection), 
0); 
 
goto cleanup; 
} 
 
// Confirm that it has the required section entries. 
 
if (!iflvPROFILEPASSWORD.fValid|| 
!iflvPROFILENAME.fValid) 
{ 
        hr = HR_LOG(E_FAIL); 
 
EventLogMsg( 
POPPST_MISSING_ERROR, 
2, TEXT("Profile"), SZ_BASE10(iSection), 
            0); 
 
goto cleanup; 
} 
 
// Log on to the specified profile. 
 
hr = MAPILogonEx(0, 
   iflvPROFILENAME.achValue, 
   iflvPROFILEPASSWORD.achValue, 
   ulMapiLogonFlags, 
   &lpSession); 
 
if (FAILED(hr)) 
{ 
EventLogMsg( 
POPPST_LOGON_ERROR, 
2, iflvPROFILENAME.achValue, SZ_BASE16(hr), 
            0); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
    ASSERTERROR(!FBadUnknown(lpSession), "Bad lpSession"); 
 
// Find the default message store in the message stores table. 
    hr = HrMAPIFindDefaultMsgStore( 
                lpSession,      // MAPI session pointer 
                &cbEid,         // entry identifier byte count 
                &lpEid);        // entry identifier 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("HrMAPIFindDefaultMsgStore"), hr); 
 
        goto cleanup; 
    } 
 
// Open the default message store. 
hr = lpSession->OpenMsgStore( 
                0, 
    cbEid,  // entry id byte count 
lpEid,  // entry id 
NULL, 
MDB_WRITE | MAPI_DEFERRED_ERRORS,  // want write access 
&lpMdb);    // MDB pointer 
 
if (FAILED(hr)) 
{ 
EventLogMsg( 
POPPST_OPEN_STORE_ERROR, 
2, iflvPROFILENAME.achValue, SZ_BASE16(hr), 
            0); 
 
        hr = HR_LOG(E_FAIL); 
    goto cleanup; 
} 
 
    ASSERT_IUNKNOWN_PTR(lpMdb, "Bad lpMdb"); 
 
// Open the root folder in the default message store.  OpenEntry() opens 
// the root folder if lpEntryID is NULL. 
hr = lpMdb->OpenEntry(0, 
  NULL, 
  NULL, 
  MAPI_MODIFY|MAPI_DEFERRED_ERRORS, 
  &ulObjType, 
  (LPUNKNOWN *)&lpRootFolder); 
 
if (FAILED(hr)) 
{ 
        INTERNAL_ERROR( TEXT("IMsgStore::OpenEntry()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
if (ulObjType != MAPI_FOLDER) 
{ 
        INTERNAL_ERROR( TEXT("IMsgStore::OpenEntry()"), MAPI_E_UNEXPECTED_TYPE); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
    ASSERTERROR(!FBadUnknown(lpRootFolder), "Bad lpRootFolder"); 
 
    // open address book if not using a temporary profile 
    if ( fTmpProf == FALSE ) 
    { 
        // open the address book 
    hr = lpSession->OpenAddressBook( 
            0,              // window handle 
            NULL,           // interface pointer 
            AB_NO_DIALOG,   // don't want dialogue box 
            &lpAdrBook);    // address book pointer 
 
    if (FAILED(hr)) 
    { 
EventLogMsg( 
POPPST_AB_ERROR, 
2, iflvPROFILENAME.achValue, SZ_BASE16(hr), 
0); 
 
            hr = HR_LOG(E_FAIL); 
            goto cleanup; 
    } 
 
        ASSERTERROR(!FBadUnknown(lpAdrBook), "Bad lpAdrBook"); 
 
    }   // end if not using temporary profile 
 
cleanup: 
 
    // free MAPI buffers and release MAPI objects 
    MAPIFREEBUFFER(lpEid); 
 
FREEPROWS(lpRows); 
 
RETURN(hr); 
 
} 
 
//$--HrResolveSenderName------------------------------------------------------ 
// 
// DESCRIPTION: Resolve the sender's name, if possible.   
//              Also checks number of sender names. 
// 
// INPUT:       pszName --  sender's display name 
// 
// OUTPUT:      ppAdrList   --  pointer to address list pointer 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_FAIL otherwise 
// 
// --------------------------------------------------------------------------- 
HRESULT HrResolveSenderName( 
        IN LPSTR pszName,   // sender's display name 
        OUT LPADRLIST * ppAdrList)  // pointer to address list pointer 
{ 
    HRESULT hr      =   NOERROR; 
    ULONG   cNames  =   0;          // # of sender names 
LPSTR *lppszTemp=NULL;// array of string pointers 
 
    const ULONG cProps  =   2;      // # of property values 
const LPSTRpszSep=";";// separator character 
 
    // property value array 
    SPropValue  rgPropVals[cProps]  =   {0}; 
     
    DEBUGPRIVATE("HrResolveSenderName()\n"); 
 
    hr = CHK_HrResolveSenderName(pszName, ppAdrList); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    // Check number of sender names.  There should only be one. 
hr = HrStrTokAll( 
pszName,// sender names 
(LPSTR) pszSeparator,// separator character 
&cNames,// number of sender names 
&lppszTemp);// array of string pointers 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("HrStrTokAll()"), hr); 
 
        goto cleanup; 
    } 
 
    if ( cNames != 1 ) 
    { 
EventLogMsg( 
POPPST_FROM_ERROR, 
1, SZ_BASE10(iSection), 
0); 
 
        hr = HR_LOG(E_FAIL); 
 
        goto cleanup; 
    } 
 
ASSERT_READ_PTR(lppszTemp, sizeof(LPSTR) * cNames, "Bad lppszTemp"); 
 
    // Only resolve sender name if we are not using a temporary 
    // profile! 
    if ( fTmpProf == FALSE ) 
    { 
        // Set up property value with know values. 
        rgPropVals[0].ulPropTag = PR_DISPLAY_NAME; 
        rgPropVals[0].Value.lpszA = pszName; 
 
        rgPropVals[1].ulPropTag = PR_RECIPIENT_TYPE; 
        rgPropVals[1].Value.l = MAPI_TO; 
 
        // Create an address list for our sender name. 
        hr = HrMAPICreateAddressList( 
                cProps,         // # of property values 
                rgPropVals,     // property value array 
                ppAdrList);     // address list pointer 
 
        if ( FAILED(hr) ) 
        { 
            INTERNAL_ERROR( TEXT("HrMAPICreateAddressList()"), hr); 
 
            goto cleanup; 
        } 
 
        // Resolve the name 
    hr = lpAdrBook->ResolveName( 
            0,              // window handle 
            0,  
            NULL,  
            *ppAdrList);    // address list pointer 
 
        // Handle case where sender name can't be resolved. 
        if ( FAILED(hr) ) 
        { 
            // some fatal error 
EventLogMsg( 
POPPST_SENDER_ERROR, 
3, pszName, SZ_BASE10(iSection), SZ_BASE16(hr), 
0); 
 
            hr = HR_LOG(E_FAIL); 
 
            goto cleanup; 
        } 
 
        ASSERTERROR(!IsBadReadPtr(ppAdrList, sizeof(LPADRLIST)), 
                    "Bad ppAdrList"); 
        ASSERTERROR(!IsBadReadPtr(*ppAdrList, sizeof(ADRLIST)), 
                    "Bad *ppAdrList"); 
 
    }   // end if have a "real" address book 
 
cleanup: 
 
// Free MAPI buffers 
MAPIFREEBUFFER(lppszTemp); 
 
    RETURN(hr); 
 
} 
 
//$--HrResolveRecipientNames------------------------------------------------- 
// 
// DESCRIPTION: Get the properties needed for the recipients using the input 
//information and resolves the recipient names, if possible. 
// 
// OUTPUT       [lppAdrList]-- Ptr to output address list with resolved 
//   names. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input parameter, 
//                          E_OUTOFMEMORY if memory problems 
//                          E_FAIL if call fails. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrResolveRecipientNames(            // RETURNS: HRESULT 
OUTLPADRLIST *lppAdrList      // address list pointer 
) 
{ 
#defineCRECIPLST3// # of recipient lists 
 
    HRESULT         hr  =   NOERROR; 
ULONGcRecips =   0;// total # of recipients 
ULONGcToRecips=0;// # of TO recipients 
ULONGcCCRecips=0;// # of CC recipients 
ULONGcBCCRecips=0;// # of BCC recipients 
LPADRLISTlpAdrList =NULL; 
LPSTR *ppch =   0; 
LPSTR *lppszToRecips=NULL;// array of tokenized TO recipients 
LPSTR *lppszCCRecips=NULL;// array of tokenized CC recipients 
LPSTR *lppszBCCRecips=NULL;// array of tokenized BCC recipients 
 
// Array of recipient INI file lines 
INIFILELINEVALUE *apiflv[CRECIPLST]= 
{ 
&iflvTO, 
&iflvCC, 
&iflvBCC 
}; 
 
// Array of recipient types 
LONGaRecipTypes[CRECIPLST]= 
{ 
MAPI_TO, 
MAPI_CC, 
MAPI_BCC 
}; 
 
// Array of tokenized recipient names 
LPSTR **rgRecipTokens[CRECIPLST]= 
{ 
&lppszToRecips, 
&lppszCCRecips, 
&lppszBCCRecips 
}; 
 
// Array of recipient counts 
ULONG *rgcRecips[CRECIPLST]= 
{ 
&cToRecips, 
&cCCRecips, 
&cBCCRecips 
}; 
 
    DEBUGPRIVATE("HrResolveRecipientNames()\n"); 
 
    // Check input parameters. 
    hr = CHK_HrResolveRecipientNames(lppAdrList); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    *lppAdrList = NULL; // initialize 
 
if ( iflvTO.fValid ) 
{ 
// Count the number of recipients to be resolved by scanning the strings 
// for semicolons. 
hr = HrStrTokAll( 
iflvTO.achValue,// MAPI TO recipients 
(LPSTR) pszSeparator,// separator character 
&cToRecips,// # of TO recipients 
&lppszToRecips);// array of tokenized recipients 
     
    if ( FAILED(hr) ) 
    { 
            INTERNAL_ERROR( TEXT("HrStrTokAll()"), hr); 
 
        goto cleanup; 
    } 
 
// increment total # of recipients 
cRecips += cToRecips; 
 
ASSERT_READ_PTR(lppszToRecips, sizeof(LPSTR) * cToRecips,  
"Bad lppszToRecips"); 
} 
 
if (iflvCC.fValid) 
    { 
hr = HrStrTokAll( 
iflvCC.achValue,// MAPI CC recipients 
(LPSTR) pszSeparator,// separator character 
&cCCRecips,// # of CC recipients 
&lppszCCRecips);// array of tokenized recipients 
 
        if ( FAILED(hr) ) 
        { 
            INTERNAL_ERROR( TEXT("HrStrTokAll()"), hr); 
 
            goto cleanup; 
        } 
 
        cRecips += cCCRecips; 
 
ASSERT_READ_PTR(lppszCCRecips, sizeof(LPSTR) * cCCRecips,  
"Bad lppszCCRecips"); 
    } 
 
if (iflvBCC.fValid) 
    { 
hr = HrStrTokAll( 
iflvBCC.achValue,// MAPI BCC recipients 
(LPSTR) pszSeparator,// separator character 
&cBCCRecips,// # of BCC recipients 
&lppszBCCRecips);// array of tokenized recipients 
 
        if ( FAILED(hr) ) 
        { 
            INTERNAL_ERROR( TEXT("HrStrTokAll()"), hr); 
 
            goto cleanup; 
        } 
 
        cRecips += cBCCRecips; 
 
ASSERT_READ_PTR(lppszBCCRecips, sizeof(LPSTR) * cBCCRecips,  
"Bad lppszBCCRecips"); 
    } 
 
// Check that there is at least one recipient. 
if ( cRecips <= 0 ) 
{ 
EventLogMsg( 
POPPST_NEED_RECIPIENT_ERROR, 
1, SZ_BASE10(iSection), 
0); 
 
hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
// Set up an ADRLIST to be returned to caller.  THREE levels of allocation 
// are required! 
hr = MAPIAllocateBuffer(CbNewADRLIST(cRecips), (LPVOID *)&lpAdrList); 
 
if ( FAILED(hr) ) 
{ 
        INTERNAL_ERROR( TEXT("MAPIAllocateBuffer()"), hr); 
 
        hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
*lppAdrList = lpAdrList; 
 
{ 
LPSPropValueaPropVals   =   NULL; 
        ULONG           cPropVals   =   2; 
ULONGi           =   0; 
ULONGj           =   0; 
LPADRENTRYlpAdrEntry  =   NULL; 
LPSTRlpszName    =   NULL; 
 
lpAdrList->cEntries = cRecips; 
 
// Initialize the ADRENTRY's prior to actually allocating the 
// SPropValue arrays.  This makes cleanup possible if there is an error. 
 
for (i = 0; i < cRecips; i++) 
{ 
lpAdrList->aEntries[i].ulReserved1 =0; 
lpAdrList->aEntries[i].cValues =cPropVals; 
lpAdrList->aEntries[i].rgPropVals =NULL; 
} 
 
// Now allocate and initialize the SPropValue arrays. 
for (i = 0; i < cRecips; i++) 
{ 
hr = MAPIAllocateBuffer(cPropVals * sizeof(SPropValue), 
  (LPVOID *)&(aPropVals)); 
if ( FAILED(hr) ) 
{ 
                INTERNAL_ERROR( TEXT("MAPIAllocateBuffer()"), hr); 
 
                hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
lpAdrList->aEntries[i].rgPropVals = aPropVals; 
 
aPropVals[0].ulPropTag =PR_RECIPIENT_TYPE; 
aPropVals[0].Value.l =0; 
 
aPropVals[1].ulPropTag =PR_DISPLAY_NAME; 
aPropVals[1].Value.lpszA =NULL; 
} 
 
// Now fill in the property values.  Note that we can count on the 
// recipient lists to be non-null strings, because we checked for 
// this during parsing. 
 
{ 
lpAdrEntry = lpAdrList->aEntries; 
 
for (i = 0; i < CRECIPLST; i++) 
{ 
if (!apiflv[i]->fValid) 
continue; 
 
// Retrieve current recipient list of type X. 
ppch =*(rgRecipTokens[i]); 
 
ASSERTERROR(ppch != NULL, "Bad ppch"); 
 
for ( j = 0; j < *(rgcRecips[i]); j++ ) 
{ 
// Retrieve current recipient 
lpszName = ppch[j]; 
 
ASSERT_STRINGA_PTR(lpszName, "Bad lpszName"); 
 
lpAdrEntry->rgPropVals[0].Value.l = aRecipTypes[i]; 
 
hr = MAPIAllocateMore( 
cbStrLenA(lpszName), 
lpAdrEntry->rgPropVals, 
(LPVOID *) &lpAdrEntry-> 
rgPropVals[1].Value.lpszA); 
 
if ( FAILED(hr) ) 
{ 
                        INTERNAL_ERROR( TEXT("MAPIAllocateMore()"), hr); 
 
                        hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
lstrcpyA(lpAdrEntry->rgPropVals[1].Value.lpszA, lpszName); 
 
lpAdrEntry++; 
 
} 
} 
} 
 
        // if not using a temporary profile, try to resolve the 
        // recipient names 
        if ( fTmpProf == FALSE ) 
        { 
    // If we have an address book available, use it to resolve any names 
    // that you can.  If we don't, then whatever got entered as the user 
    // name remains as the display name. 
 
    // CAVEAT - It seems that MAPI has some sort of bug whereby the first 
    //attempt to resolve a given name fails.  A subsequent 
    //identical request will succeed, so the first of two 
    //messages referencing a recipient won't be resolved, but 
    //the second will. 
        hr = lpAdrBook->ResolveName(0, 0, NULL, lpAdrList); 
 
            if ( (hr == MAPI_E_NOT_FOUND) ||  
                 (hr == MAPI_E_AMBIGUOUS_RECIP) ) 
            { 
                // non-fatal errors.  Just can't resolve 
                // this recipient name. 
EventLogMsg( 
POPPST_RESOLVE_WARNING, 
2, SZ_BASE10(iSection), SZ_BASE16(hr), 
                    0); 
 
                hr = HR_LOG(NOERROR); 
 
                // continue on. 
            } 
 
    if ( FAILED(hr) ) 
    { 
                // Some un-recoverable error. 
EventLogMsg( 
POPPST_RESOLVE_ERROR, 
2, SZ_BASE10(iSection), SZ_BASE16(hr), 
0); 
 
                hr = HR_LOG(E_FAIL); 
 
    goto cleanup; 
    } 
 
    // Now look for problems reported in the returned ADRLIST. 
    for (i = 0; i < lpAdrList->cEntries; i++) 
    { 
                HRESULT         hrT =   NOERROR; 
                 
    lpAdrEntry = &lpAdrList->aEntries[i]; 
 
    for (j = 0; j < lpAdrEntry->cValues; j++) 
    { 
    LPSPropValuepPropVal; 
     
    pPropVal = &lpAdrEntry->rgPropVals[j]; 
 
    if (PROP_TYPE(pPropVal->ulPropTag == PT_ERROR)) 
{ 
    hrT = pPropVal->Value.err; 
 
break; 
} 
                }   // end for this recipient 
 
                if ( (hrT == MAPI_E_NOT_FOUND) || 
                     (hrT == MAPI_E_AMBIGUOUS_RECIP) ) 
                { 
                    // not a fatal error.  Just can't 
                    // resolve a particular recipient. 
EventLogMsg( 
POPPST_RESOLVE_WARNING, 
2, SZ_BASE10(iSection), SZ_BASE16(hrT), 
                        0); 
 
                    hrT = HR_LOG(NOERROR); 
 
                    // continue on. 
                } 
 
                // Print out error for this recipient, if any 
    if ( FAILED(hrT) ) 
    { 
                    // Some non-recoverable error. 
EventLogMsg( 
POPPST_RESOLVE_ERROR, 
2, SZ_BASE10(iSection), SZ_BASE16(hrT), 
                        0); 
 
                    hr = HR_LOG(hrT); 
                    goto cleanup; 
    } 
 
                // continue with next recipient 
 
    }   // end for 
        }   // end if not using temporary PST 
} 
 
cleanup: 
 
// Free MAPI buffers 
MAPIFREEBUFFER(lppszToRecips); 
MAPIFREEBUFFER(lppszCCRecips); 
MAPIFREEBUFFER(lppszBCCRecips); 
 
// Actual cleanup of *lppAdrList and its substructures must be done by 
// caller! 
 
    RETURN(hr); 
 
} 
 
 
//$--HrSetMessageProperties-------------------------------------------------- 
// 
// DESCRIPTION: Set the sender, subject, date/time, and priority properties 
//for the input message. 
// 
// INPUT/OUTPUT:lpMsg -- Message for which properties are to be set. 
// 
// RETURNS:     HRESULT --  NOERROR if successful, 
//                          E_INVALIDARG if bad input, 
//                          E_OUTOFMEMORY if memory problems. 
//                          E_FAIL if call fails 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrSetMessageProperties(     // RETURNS: HRESULT 
IN OUT LPMESSAGElpMsg       // message pointer 
) 
{ 
HRESULThr  =           NOERROR; 
LPSPropProblemArraylpProblems =NULL; 
    LPADRLIST           lpAdrList =     NULL;   // used to resolve sender's address 
    ULONG               iProp   =       0;      // property value index 
    LPSPropValue        lpEntry =       NULL;   // address list entry pointer 
    ULONG               nValues =       NULL;   // # of property values 
 
    // String buffers for resolved sender name values. 
    CHAR                szFromAddr[cbMaxIniLine]  =   ""; 
    CHAR                szFromAdrTyp[cbMaxIniLine]=   "";     
 
    // property value indices 
    const UINT          iMsgClass   =   0; 
    const UINT          iPriority   =   1; 
    const UINT          iImportance =   2; 
    const UINT          iftDeliver  =   3; 
    const UINT          iftSubmit   =   4; 
    const UINT          iSubject    =   5; 
    const UINT          iFromName   =   6; 
    const UINT          iRepFromName=   7; 
    const UINT          iDelete     =   8; 
 
    const ULONG         cMinValues  =   9; 
         
    // properties which are only set if using a real profile 
    const UINT          iFromAddr   =   9; 
    const UINT          iFromAdrTyp =   10; 
    const UINT          iFromEID    =   11; 
    const UINT          iFromKey    =   12; 
    const UINT          iRepFromAddr=   13; 
    const UINT          iRepFromAdrTyp= 14; 
    const UINT          iRepFromEID =   15; 
    const UINT          iRepFromKey =   16; 
 
    // maximum number of property values 
const ULONG    cMaxValues =17; 
 
    // array of property values 
    SPropValue          aPropVals[cMaxValues]  =   {0}; 
 
    const UINT          cbEidMax            =   256;    // max EID size 
 
    // entry identifier buffer 
    SizedENTRYID(cbEidMax, eidFrom)         =   {0}; 
 
    DEBUGPRIVATE("HrSetMessageProperties()\n"); 
 
    // Check input parameters 
    hr = CHK_HrSetMessageProperties(lpMsg); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    // initiliaze # of property values 
    nValues = cMinValues; 
 
aPropVals[iFromName].ulPropTag = PR_SENDER_NAME; 
aPropVals[iFromName].Value.lpszA = iflvFROM.achValue; 
 
aPropVals[iRepFromName].ulPropTag =PR_SENT_REPRESENTING_NAME; 
aPropVals[iRepFromName].Value.lpszA =iflvFROM.achValue; 
 
aPropVals[iSubject].ulPropTag =PR_SUBJECT; 
aPropVals[iSubject].Value.lpszA = iflvSUBJECT.achValue; 
 
    // PR_DELIVER_TIME is not transmittable. 
    aPropVals[iftDeliver].ulPropTag = PR_DELIVER_TIME; 
    aPropVals[iftDeliver].Value.ft = ftCurMsg; 
 
    // PR_CLIENT_SUBMIT_TIME is transmittable 
aPropVals[iftSubmit].ulPropTag = PR_CLIENT_SUBMIT_TIME; 
aPropVals[iftSubmit].Value.ft =ftCurMsg; 
 
    aPropVals[iMsgClass].ulPropTag = PR_MESSAGE_CLASS; 
    aPropVals[iMsgClass].Value.lpszA = "IPM.Note"; 
 
    aPropVals[iPriority].ulPropTag = PR_PRIORITY; 
    aPropVals[iPriority].Value.l = lPriorityCurMsg; 
 
    aPropVals[iImportance].ulPropTag = PR_IMPORTANCE;     
    aPropVals[iImportance].Value.l = lImportanceCurMsg; 
 
    aPropVals[iDelete].ulPropTag = PR_DELETE_AFTER_SUBMIT; 
    aPropVals[iDelete].Value.b = TRUE; 
 
    // Resolve the sender's address if we are using a real 
    // profile 
    if ( fTmpProf == FALSE ) 
    { 
        hr = HrResolveSenderName( 
                iflvFROM.achValue,  // sender's display name 
                &lpAdrList);        // address list pointer 
 
        if ( FAILED(hr) ) 
        { 
            goto cleanup; 
        } 
 
        // Set the EMAIL_ADDRESS, ADDR_TYPE and ENTRYID properties 
        // based on what HrResolveSenderName returned. 
        lpEntry = lpAdrList->aEntries[0].rgPropVals;   // intialize entry pointer 
        for ( iProp = 0; iProp < lpAdrList->aEntries[0].cValues; iProp++ ) 
        { 
            switch ( lpEntry[iProp].ulPropTag ) 
            { 
                case PR_DISPLAY_NAME:            
                    // update sender's name with resolved name 
                    lstrcpyn(iflvFROM.achValue, 
                    lpEntry[iProp].Value.lpszA, cbMaxIniLine); 
 
break; 
 
                case PR_ADDRTYPE:             
                    // fill in address type 
                    lstrcpyn( 
                        szFromAdrTyp,  
                        lpEntry[iProp].Value.lpszA, 
                        cbMaxIniLine); 
 
                    aPropVals[iFromAdrTyp].ulPropTag = PR_SENDER_ADDRTYPE; 
                    aPropVals[iFromAdrTyp].Value.lpszA = 
                        szFromAdrTyp; 
 
                    aPropVals[iRepFromAdrTyp].ulPropTag = PR_SENT_REPRESENTING_ADDRTYPE; 
                    aPropVals[iRepFromAdrTyp].Value.lpszA = 
                        szFromAdrTyp; 
 
                    break; 
 
                case PR_EMAIL_ADDRESS: 
                    // Fill in email address 
                    lstrcpyn( 
                        szFromAddr, 
                        lpEntry[iProp].Value.lpszA, 
                        cbMaxIniLine); 
 
                    aPropVals[iFromAddr].ulPropTag = PR_SENDER_EMAIL_ADDRESS; 
                    aPropVals[iFromAddr].Value.lpszA = 
                        szFromAddr; 
 
                    aPropVals[iRepFromAddr].ulPropTag =  
                        PR_SENT_REPRESENTING_EMAIL_ADDRESS; 
                    aPropVals[iRepFromAddr].Value.lpszA = 
                        szFromAddr; 
 
                    break; 
 
                case PR_ENTRYID: 
                    // fill in entry identifier                
                    CopyMemory( 
                        &eidFrom.ab, 
                        lpEntry[iProp].Value.bin.lpb, 
                        lpEntry[iProp].Value.bin.cb); 
 
                    aPropVals[iFromEID].ulPropTag = PR_SENDER_ENTRYID;                                                         
                    aPropVals[iFromEID].Value.bin.cb = 
                        lpEntry[iProp].Value.bin.cb; 
                    aPropVals[iFromEID].Value.bin.lpb = 
                        (BYTE *) &eidFrom.ab;                     
 
                    aPropVals[iRepFromEID].ulPropTag = PR_SENT_REPRESENTING_ENTRYID; 
                    aPropVals[iRepFromEID].Value.bin.cb = 
                        lpEntry[iProp].Value.bin.cb; 
                    aPropVals[iRepFromEID].Value.bin.lpb = 
                        (BYTE *) &eidFrom.ab;                     
 
                    break; 
         
                case PR_SEARCH_KEY: 
                    // fill in sender's seach key 
                    aPropVals[iFromKey].ulPropTag = PR_SENDER_SEARCH_KEY; 
                    aPropVals[iFromKey].Value.bin.cb = 
                        lpEntry[iProp].Value.bin.cb; 
                    aPropVals[iFromKey].Value.bin.lpb = 
                        lpEntry[iProp].Value.bin.lpb; 
 
                    aPropVals[iRepFromKey].ulPropTag =  
                        PR_SENT_REPRESENTING_SEARCH_KEY; 
                    aPropVals[iRepFromKey].Value.bin.cb = 
                        lpEntry[iProp].Value.bin.cb; 
                    aPropVals[iRepFromKey].Value.bin.lpb = 
                        lpEntry[iProp].Value.bin.lpb; 
 
                    break; 
 
                default: 
                    // not concerned with this property 
                    break; 
 
            }   // end switch 
        }   // end for 
 
        // Re-set number of property values 
        nValues = cMaxValues; 
 
    }   // end if has real profile 
 
    ASSERTERROR((nValues != 0), "Bad nValues"); 
 
hr = lpMsg->SetProps(nValues, aPropVals, &lpProblems); 
 
if (FAILED(hr)) 
{ 
        INTERNAL_ERROR( TEXT("IMAPIProp::SetProps()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
if (lpProblems != NULL) 
{ 
ULONGi; 
 
for (i = 0; i < lpProblems->cProblem; i++) 
            INTERNAL_ERROR( TEXT("IMAPIProp::SetProps()"), lpProblems->aProblem[1].scode); 
MAPIFREEBUFFER(lpProblems); 
 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
 
cleanup: 
 
    // Free MAPI buffers 
    FREEPADRLIST(lpAdrList); 
 
RETURN(hr); 
 
} 
 
 
//$--HrWriteMessageText------------------------------------------------------ 
// 
// DESCRIPTION:    Write the message text (PR_BODY & PR_RTF_COMPRESSED)  
//                 to the message, if any 
// 
// INPUT: 
// 
//[lpMsg]-- Message for which message text is to be written. 
//[lpszFilePath]-- Path of file containing message text (may be null) 
// 
// RETURNS:     NOERROR if successful, 
//              E_INVALIDARGS if bad input, 
//              E_FAIL if call fails. 
// 
// Notes:We use stream methods to do this, since the SetProps() has a 
//4KB limit for the size of a property you can set. 
// 
//--------------------------------------------------------------------------- 
 
HRESULT HrWriteMessageText(             // RETURNS: HRESULT 
INLPMESSAGElpMsg,          // message pointer 
INLPSTRlpszFilePath    // message text path, may be null 
) 
{ 
    HRESULT         hr          =   NOERROR; 
LPSTREAM    lpStrm      =NULL; 
    ULONG           cbText      =   0;      // # bytes in message text 
    ULONG           cbBody      =   0;      // # bytes in PR_BODY 
    STATSTG         sStatStg    =   {0};    // stream status structure 
 
    DEBUGPRIVATE("HrWriteMessageText()\n"); 
 
    // Check input parameters 
    hr = CHK_HrWriteMessageText(lpMsg, lpszFilePath); 
 
    if ( FAILED(hr) ) 
    { 
        RETURN(hr); 
    } 
 
    // If no message text, then we are done. 
    if ( *lpszFilePath == 0 ) 
    { 
        goto cleanup; 
    } 
 
    // Create the PR_BODY property (needed by the PST, which is not an 
    // rich-text aware store) from the message body file data. 
    hr = HrMAPISetPropFromFile( 
        lpMsg,              // pointer to message object 
        PR_BODY,            // property tag 
        lpszFilePath,       // pointer to source file name 
        &cbBody);           // # bytes in message body 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("HrMAPISetPropFromFile()"), hr); 
 
        goto cleanup; 
    } 
 
    // Build the RTF compressed data for the message (needed 
    // for RTF-aware stores, such as the Exchange MDB). 
    // First, open a virtual (bufferred) stream on the input text file. 
    hr = HrOpenVirtualStreamOnFile( 
        MAPIAllocateBuffer,     // allocation routine 
        MAPIFreeBuffer,         // deallocation routine 
        STGM_READ | STGM_SHARE_EXCLUSIVE,// interface flags 
        lpszFilePath,           // file name 
        NULL,                   // file prefix 
        &lpStrm);               // stream pointer 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("HrOpenVirtualStreamOnFile()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
        goto cleanup; 
    } 
 
    ASSERT_IUNKNOWN_PTR(lpStrm, "Bad lpStrm"); 
 
    // Determine size of stream (not necessarily size of PR_BODY!) 
    hr = lpStrm->Stat( 
        &sStatStg,          // status structure pointer 
        0);                 // flags 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("VSOF::Stat()"), hr); 
 
        hr = HR_LOG(E_FAIL); 
        goto cleanup; 
    } 
 
    cbText = sStatStg.cbSize.LowPart; 
 
    // See if body if too big for us to handle 
    if ( sStatStg.cbSize.HighPart > 0 ) 
    { 
EventLogMsg( 
POPPST_BODY_ERROR, 
1, SZ_BASE10(iSection), 
            0); 
 
        hr = HR_LOG(E_NOTIMPL); 
        goto cleanup; 
    } 
 
    // Create RTF compressed property from the ASCII text 
    hr = HrTextToRTFCompressed( 
            cbText, // # of bytes of text 
            lpStrm, // text stream pointer 
0,// # of attachment w/ rendering positions 
NULL,// array of attachment rendering positions 
            lpMsg,  // message pointer 
0);// use current code page 
 
    if ( FAILED(hr) ) 
    { 
        INTERNAL_ERROR( TEXT("HrTextToRTFCompressed()"), hr); 
 
        goto cleanup; 
    } 
 
    // we are done. 
 
cleanup: 
 
ULRELEASE(lpStrm); 
 
RETURN(hr); 
}