/*
	File: AMSMailTool.cpp

	Copyright:	© 2003 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:
*/


// System  headers
#include <stdlib.h>
#include <fcntl.h>					// for open() and O_* flags
#include <paths.h>					// for _PATH_DEVNULL
#include <stdio.h>
#include <sys/syslog.h>				// for syslog
#include <sys/sysctl.h>				// for struct kinfo_proc and sysctl()
#include <sys/stat.h>				// for file and dir stat calls
#include <sys/resource.h>			// for getrlimit()
#include <sys/mount.h>				// for file system check
#include <sys/wait.h>
#include <mach/mach.h>
#include <pwd.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>

// Application headers
#include "AMSMailTool.h"
#include "CString.h"
#include "CFile.h"
#include "CAccount.h"
#include "CAccount.10.1.h"
#include "CEnvelope.h"
#include "CEnvelope.10.1.h"
#include "CMailSpool.h"
#include "CMailSpool.10.1.h"
#include "CMailboxes.h"
#include "CMailboxes.10.1.h"
#include "CRootObject.h"
#include "CRootObject.10.1.h"
#include "CMessagePart.10.1.h"
#include "CSmallMsgPart.10.1.h"
#include "CHostEntry.h"
#include "CHostEntry.10.1.h"
#include "CGlobals.h"
#include "COSUtils.h"
#include "CUtils.h"
#include "Database.h"
#include "Database.10.1.h"
#include "DSMgr.h"
#include "DSHostPrefs.h"
#include "DSServerPrefs.h"
#include "MailTypes.h"

#define RECONSTRUCT_PATH	"/usr/bin/cyrus/bin/reconstruct"
#define POSTCONF			"postconf"
#define POSTCONF_PATH		"/usr/sbin/postconf"
#define POSTMAP_PATH		"/usr/sbin/postmap"

uInt64	kMinFileSystemSizeKB	= 50 * 1024UL;	// Minimum size of free space on the file system needed, in Kilobytes

uInt32 		gAllowedSMTPClients		= 10;
uInt32 		gAllowedPOP3Clients		= 10;
uInt32 		gMaxInConnectionsSMTP	= 10;
uInt32 		gMaxInConnectionsMail	= 10;
uInt32		gSyncFoldersStamp		= 0;
StrListObj *gLocalHostList          = NULL;
CString		gCSLocalHostList( "mydestination=$myhostname,localhost.$mydomain" );
CString		gSrcDir;

static const uInt32	kOpenFileLimit	= 4096;

// Static Prototypes ---------------------------------------------------------

static void			sHelp					( FILE *inFile );
static void			sVersion				( FILE *inFile );
static void			GetAltLocation			( CString &inSrcPath );
static void			GetAltDestination		( CString &inDstPath );
static void			EnableIMAPKey			( const char *inKey, const char *inValue );
static void			DoExport				( const char *inSrcPath, const char *inDstPath, eOptions inOptions );
static void			ExportPrefs				( void );
static void			ExportMail				( const char *inDestPath );
static void			ExportMail_10_1			( const char *inDestPath );
static void			FixPath					( CString *inStr );
static CFile *		OpenDataFile			( CString *inStr, int *inOutFileUID, uid_t inUID, gid_t inGID, CFile **inOutExtra );

static void			sAppleVersion			( FILE *inFile );
static void			sUsage					( FILE *inFile, const char *inArgv0, int inErr = 0, const char *inStr = NULL );
static pid_t		sIsRunning				( const char *inProcName );
static eAppError	sInitDirectory			( void );

static void			sHandle_SIGTERM_SIGALRM	( ... );
static void			sHandle_SIGHUP			( ... );
static void			sHandle_SIGPIPE			( ... );
static void			sHandle_SIGABRT			( ... );
static void			sHandle_SIGTRAP			( ... );

// ---------------------------------------------------------------------------
//	* Version Strings
// ---------------------------------------------------------------------------

MailVersionInfo			gkAppVersion 	= { 10, 3, 0, 0 };
static const char	   *spAppName		= "Apple Mail Service Mail Tool";
static const char	   *spVersion		= " : 10.3.0.0";
static const char	   *spBuild			= " : build: 1 rev 1";

// ---------------------------------------------------------------------------
//	* Static Locals
// ---------------------------------------------------------------------------

static const char	   *spMacOSX10_1_MailDBName		= "MacOSXMailDB";
static const char	   *spMacOSX10_2_MailDBName		= "MacOSXMailDatabase";
static const char	   *spDefaultMailSourceDBPath	= "/Library/AppleMailServer";
static const char	   *spDefaultDestinationPath	= "/var/spool/imap";
static bool				sbReset						= false;
static bool				sbMigrate					= false;
static int				siMaxSize					= 0;

// ---------------------------------------------------------------------------
//	* sHandle_SIGTERM_SIGALRM ()
// ---------------------------------------------------------------------------

static void sHandle_SIGTERM_SIGALRM ( ... )
{
	syslog( LOG_INFO, "Caught a terminating signal (SIGTERM or SIGALRM)" );
} // sHandle_SIGTERM_SIGALRM


// ---------------------------------------------------------------------------
//	* sHandle_SIGHUP ()
//
// ---------------------------------------------------------------------------

static void sHandle_SIGHUP ( ... )
{
	syslog( LOG_INFO, "Caught a signal: SIGHUP" );
} // sHandle_SIGHUP


// ---------------------------------------------------------------------------
//	* sHandle_SIGPIPE ()
// ---------------------------------------------------------------------------

static void sHandle_SIGPIPE ( ... )
{
	syslog( LOG_INFO, "Caught a signal: SIGPIPE" );
} // sHandle_SIGPIPE


// ---------------------------------------------------------------------------
//	* sHandle_SIGABRT ()
// ---------------------------------------------------------------------------

static void sHandle_SIGABRT ( ... )
{
	syslog( LOG_INFO, "Caught a signal: SIGABRT" );
} // sHandle_SIGABRT


// ---------------------------------------------------------------------------
//	* sHandle_SIGTRAP ()
// ---------------------------------------------------------------------------

static void sHandle_SIGTRAP ( ... )
{
	syslog( LOG_INFO, "Caught a signal: SIGTRAP" );
} // sHandle_SIGTRAP


// ---------------------------------------------------------------------------
//	* Main ()
// ---------------------------------------------------------------------------

int main ( int argc, char **argv )
{
	int					i			= 2;
	char			   *p			= NULL;
	uid_t				userID		= 99;	// Don't init to '0', it's root
	eOptions			options		= kDoNothing;
	CString				csDst;
	CString				csSrc;
	struct sigaction 	sa;
	struct sigaction 	oldSA;

	/* version & help options - available to any user */
	if ( argc > 1 )
	{
		p = CUtils::Strstr( argv[ 1 ], "-" );
		if ( p != nil )
		{
			if ( CUtils::Strcmp( p, "-help" ) == 0 )
			{
				sUsage( stdout, argv[ 0 ] );
				exit( kNoErrors );
			}
			else if ( CUtils::Strcmp( p, "-ver" ) == 0 )
			{
				sVersion( stdout );
				exit( kNoErrors );
			}
			else if ( CUtils::Strcmp( p, "-appleversion" ) == 0 )
			{
				sAppleVersion( stdout );
				exit( kNoErrors );
			}
		}
	}

	/* must be root to run */
	userID = ::geteuid();
	if ( userID != 0 )
	{
		sUsage( stdout, argv[ 0 ], kNotRoot );
		exit( kNotRoot );
	}

	options = kMigrateDefault;
	gSrcDir.Clear();
	csSrc.Clear();
	csDst.Clear();

	/* root only options */
	if ( argc == 1 )
	{
		options = kMigrateDefault;
	}
	else if ( (argc > 1) && (argc < 7) )
	{
		for ( i = 1; i < argc; i++ )
		{
			p = CUtils::Strstr( argv[ i ], "-" );
			if  ( p != NULL )
			{
				if ( CUtils::Strcmp( p, "-migrate.10.1" ) == 0 )
				{
					options = kMigrate10_1_Only;
				}
				else if ( CUtils::Strcmp( p, "-migrate.10.2" ) == 0 )
				{
					options = kMigrate10_2_Only;
				}
				else if ( CUtils::Strcmp( p, "-reset" ) == 0 )
				{
					sbReset = true;
				}
				else if ( CUtils::Strcmp( p, "-maxsize" ) == 0 )
				{
					if ( argc <= i + 1 )
					{
						sUsage( stderr, argv[ 0 ], kMissingSize );
						exit( kMissingSize );
					}
					else
					{
						i++;
						siMaxSize = ::strtol( argv[ i ], nil, 0 );
						if ( siMaxSize == 0 )
						{
							sUsage( stderr, argv[ 0 ], kBadSizeArg, argv[ i ] );
							exit( kBadSizeArg );
						}
					}
				}
				else if ( CUtils::Strcmp( p, "-source" ) == 0 )
				{
					if ( argc <= i + 1 )
					{
						sUsage( stderr, argv[ 0 ], kMissingSrcPath );
						exit( kMissingSrcPath );
					}
					else
					{
						i++;
						if ( COSUtils::DoesDirectoryExist( argv[ i ] ) == false )
						{
							sUsage( stderr, argv[ 0 ], kBadSrcPath, argv[ i ] );
							exit( kBadSrcPath );
						}
						csSrc.Set( argv[ i ] );
						gSrcDir.Set( argv[ i ] );
					}
				}
				else if ( CUtils::Strcmp( p, "-destination" ) == 0 )
				{
					if ( argc <= i + 1 )
					{
						sUsage( stderr, argv[ 0 ], kMissingDestPath );
						exit( kMissingDestPath );
					}
					else
					{
						i++;
						csDst.Set( argv[ i ] );
					}
				}
				else
				{
					sUsage( stderr, argv[ 0 ], kTooManyArgs, p );
					exit( kTooManyArgs );
				}
			}
			else
			{
				sUsage( stderr, argv[ 0 ], kIllegalArg, argv[ i ] );
				exit( kIllegalArg );
			}
		}
	}
	else
	{
		sUsage( stdout, argv[0] );
		exit( 0 );
	}

	// Is the server already running?
	if ( sIsRunning( "MailService" ) != -1 )
	{
		::fprintf( stderr, "%s cannot run while MailService is running.\n", argv[ 0 ] );
		::fprintf( stderr, "Please quit MailService and try again.\n" );
		::fprintf( stderr, "Terminating this process.\n" );
		exit( 9 );
	}

	if ( sIsRunning( argv[ 0 ] ) != -1 )
	{
		::fprintf( stderr, "%s is already running.\n", argv[ 0 ] );
		::fprintf( stderr, "Terminating this process.\n" );
		exit( 10 );
	}

	// Initialize the directory
	if ( sInitDirectory() != kAppNoErr )
	{
		::fprintf( stderr, "Error when attempting to intialize directory.\n" );
		::fprintf( stderr, "  Verify that DirectoryServices is running before attempting to launch again.\n" );
		::fprintf( stderr, "Terminating this process.\n" );
		exit( 11);
	}


	::memset( &sa, 0, sizeof( sa ) );
	::memset( &oldSA, 0, sizeof( struct sigaction ) );

	sigfillset( &sa.sa_mask );	// block all signals during the execution of the signal handler
	sa.sa_flags = SA_RESTART;

	sa.sa_handler = (void (*) (int))sHandle_SIGTERM_SIGALRM;
	::sigaction( SIGTERM, &sa, NULL );

	sa.sa_handler = (void (*) (int))sHandle_SIGHUP;
	sa.sa_flags = 0;
	::sigaction( SIGHUP, &sa, &oldSA );

	sa.sa_handler = (void (*) (int))sHandle_SIGPIPE;
	sa.sa_flags = 0;
	::sigaction( SIGPIPE, &sa, &oldSA );

	sa.sa_handler = (void (*) (int))sHandle_SIGABRT;
	sa.sa_flags = 0;
	::sigaction( SIGABRT, &sa, &oldSA );

	sa.sa_handler = (void (*) (int))sHandle_SIGTRAP;
	sa.sa_flags = 0;
	::sigaction( SIGTRAP, &sa, &oldSA );

	::sigaction( SIGALRM, &sa, NULL );
	::sigaction( SIGQUIT, &sa, NULL );
	::sigaction( SIGABRT, &sa, NULL );
	::sigaction( SIGPIPE, &sa, NULL );
	::sigaction( SIGABRT, &sa, NULL );


	// Get custom source file path
	if ( csSrc.GetLength() == 0 )
	{
		GetAltLocation( csSrc );
		gSrcDir.Set( csSrc.GetData() );
	}

	// Get custom destinatin file path
	if ( csDst.GetLength() == 0 )
	{
		GetAltDestination( csDst );
	}

	::fprintf( stdout, "Migrating mail:\n" );
	::fprintf( stdout, "  From: %s\n", csSrc.GetData() );
	::fprintf( stdout, "  To  : %s\n", csDst.GetData() );
	::fprintf( stdout, "Please wait...\n" );

	DoExport( csSrc.GetData(), csDst.GetData(), options );

	ExportPrefs();

	::fprintf( stdout, "User mail export done.\n" );

	return( 0 );

} // main


// ---------------------------------------------------------------------------
//	* GetAltLocation ()
// ---------------------------------------------------------------------------

void GetAltLocation ( CString &inSrcPath )
{
	Bool				bFlag			= false;
	DSServerPrefs	   *pPrefsObj		= nil;

	pPrefsObj = DSMgr::GetServerPrefsObj();
	if ( pPrefsObj != nil )
	{
		/* was the server using a custom path */
		pPrefsObj->GetAltDBLocationFlag ( &bFlag, TRUE );
		if ( bFlag == TRUE )
		{
			pPrefsObj->GetAltDBLocationValue( inSrcPath );
			if ( inSrcPath.GetLength() == 0 )
			{
				inSrcPath.Set( spDefaultMailSourceDBPath );
			}
		}
		else
		{
			inSrcPath.Set( spDefaultMailSourceDBPath );
		}
	}
} // GetAltLocation


// ---------------------------------------------------------------------------
//	* GetAltDestination ()
// ---------------------------------------------------------------------------

void GetAltDestination ( CString &inDestPath )
{
	char		   *p		= NULL;
	char		   *r		= NULL;
	CFile		   *pFile	= NULL;

	/* set to default location */
	inDestPath.Set( spDefaultDestinationPath );

	try
	{
		pFile = new CFile( "/etc/imapd.conf", false, false );
		if ( pFile == NULL )
		{
			return;
		}
	
		if ( pFile->FileSize() == 0 )
		{
			delete( pFile );
			return;
		}
	
		CString csBuff( pFile->FileSize() + 1 );
	
		csBuff.Clear();
	
		pFile->read( csBuff.GetData(), pFile->FileSize() );
	
		p = ::strstr( csBuff.GetData(), "partition-default:" );
		if ( p != NULL )
		{
			p += ::strlen( "partition-default:" );
			if ( (p != NULL) && (::strlen( p ) > 0) )
			{
				while ( (p != NULL) && ((*p == ' ') || (*p == '\t')) )
				{
					p++;
				}

				if ( p != NULL )
				{
					r = p;
					while ( (r != NULL) && (*r != '\n') && (*p != ' ') && (*p != '\t') )
					{
						r++;
					}
					if ( r == NULL )
					{
						inDestPath.Set( p );
					}
					else
					{
						inDestPath.Set( p, r - p );
					}
				}
			}
		}
	}

	catch ( long err )
	{
	}

	catch ( ... )
	{
	}

} /* GetAltDestination */


// ---------------------------------------------------------------------------
//	* EnableIMAPKey ()
// ---------------------------------------------------------------------------

void EnableIMAPKey ( const char *inKey, const char *inValue )
{
	int				len		= 128;
	char		   *p		= NULL;
	char		   *r		= NULL;
	CFile		   *pFile	= NULL;
	CString			csNewData;
	CString			csNewKeyValue;

	if ( !inKey || !inValue )
	{
		return;
	}

	try
	{
		pFile = new CFile( "/etc/imapd.conf", k644, true );
		if ( pFile == NULL )
		{
			return;
		}
	
		csNewKeyValue.Sprintf( "%s: %s\n", inKey, inValue );

		if ( pFile->FileSize() == 0 )
		{
			pFile->write( csNewKeyValue.GetData(), csNewKeyValue.GetLength() );
			delete( pFile );
			return;
		}
	
		CString csBuff( pFile->FileSize() + 1 );
	
		csBuff.Clear();
		csNewData.Clear();

		pFile->read( csBuff.GetData(), pFile->FileSize() );
	
		p = ::strstr( csBuff.GetData(), inKey );
		if ( p != NULL )
		{
			/* we found the key */
			csNewData.Set( csBuff.GetData(), p - csBuff.GetData() );
			csNewData.Append( csNewKeyValue.GetData(), csNewKeyValue.GetLength() );

			/* skip past the key */
			while ( (p != NULL) && (*p != '\n') )
			{
				p++;
			}

			if ( p != NULL )
			{
				p++;
				if ( p != NULL )
				{
					csNewData.Append( p );
				}
			}
		}
		else
		{
			csNewData.Set( csBuff.GetData(), csBuff.GetLength() );
			csNewData.Append( csNewKeyValue.GetData(), csNewKeyValue.GetLength() );
		}

		pFile->write( csNewData.GetData(), csNewData.GetLength() );
		delete( pFile );
	}

	catch ( long err )
	{
	}

	catch ( ... )
	{
	}

} /* EnableIMAPKey */


// ---------------------------------------------------------------------------
//	* ExportPrefs ()
// ---------------------------------------------------------------------------

void ExportPrefs ( void )
{
    pid_t					ourPid					= 0;
    int						status					= 0;
	uInt32					uiValue					= 0;
	Bool					bValue					= false;
	bool					bBad					= false;
	char				   *p						= NULL;
	CFBooleanRef			cfBoolVal				= kCFBooleanFalse;
	DSServerPrefs		   *pPrefsObj				= NULL;
	StrListObj			   *pIPStrStrList			= NULL;
	StrListObj			   *pStrObj					= NULL;
	tDirStatus				dsStatus				= eDSNoErr;
	CFile				   *pFile					= NULL;
	CFDictionaryRef			cfDictRef				= NULL;
	CFDictionaryRef			cfDictRefPostfix		= NULL;
	CFDictionaryRef			cfDictRefCyrus			= NULL;
	
	CFMutableDictionaryRef  cfMutableDictRef		= NULL;
	CFMutableDictionaryRef  cfMutableDictRefPostfix = NULL;
	CFMutableDictionaryRef  cfMutableDictRefCyrus	= NULL;
	CFMutableDictionaryRef  cfMutableDictRefMailman	= NULL;

	CFNumberRef				cfNumberRef				= NULL;
	CFDataRef				cfDataRef				= NULL;
	CFPropertyListRef       cfPlistRef				= NULL;
	CFArrayCallBacks		cfArrayCallbacks		= kCFTypeArrayCallBacks;
	CFArrayRef				cfArray					= NULL;
	CFMutableArrayRef		cfMutableArray			= NULL;
	CFStringRef				cfString				= NULL;
	CString					csTmp;
	CString					csValue;

	cfMutableDictRef = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );

	cfMutableDictRefPostfix = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
	CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "enable_postfix" ), cfBoolVal );
	CFDictionarySetValue( cfMutableDictRef, CFSTR( "postfix" ), cfMutableDictRefPostfix );

	cfMutableDictRefCyrus = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
	CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_imap" ), cfBoolVal );
	CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_pop" ), cfBoolVal );
	CFDictionarySetValue( cfMutableDictRef, CFSTR( "cyrus" ), cfMutableDictRefCyrus );

	cfMutableDictRefMailman = CFDictionaryCreateMutable( NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
	CFDictionarySetValue( cfMutableDictRefMailman, CFSTR( "enable_mailman" ), cfBoolVal );
	CFDictionarySetValue( cfMutableDictRef, CFSTR( "mailman" ), cfMutableDictRefMailman );

	pPrefsObj = DSMgr::GetServerPrefsObj();
	if ( pPrefsObj != nil )
	{
		if ( cfMutableDictRefPostfix != NULL )
		{
			/* get max message size */
			bValue = false;
			uiValue = 0;
			pPrefsObj->GetMaxMessageSizeFlag( &bValue, true );
			pPrefsObj->GetMaxMessageSize( &uiValue, true );

			// Set the default value for max message size incase it == 0
			if ( uiValue != 0 )
			{
				uiValue = uiValue * 1024;
			}
			else
			{
				uiValue = 102400000;
			}

			if ( bValue )
			{
				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "message_size_limit_enabled" ), kCFBooleanTrue );
			}
			else
			{
				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "message_size_limit_enabled" ), kCFBooleanFalse );
			}

			cfNumberRef = ::CFNumberCreate( NULL, kCFNumberIntType, &uiValue );
			if ( cfNumberRef != NULL )
			{
				::CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "message_size_limit" ), cfNumberRef );
			}

			if ( bValue && uiValue )
			{
				csTmp.Sprintf( "message_size_limit=%d", uiValue );

				if ( (ourPid = vfork()) < 0 )
				{
					// can't fork
				}
				if ( ourPid == 0 )
				{
					execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
					exit( errno );
				}
				else
				{
					waitpid( ourPid, &status, 0 );
				}
			}
		

			// Set mailbox size to 0, it is ignored by cyrus
			if ( (ourPid = vfork()) < 0 )
			{
				// can't fork
			}
			if ( ourPid == 0 )
			{
				csTmp.Set( "mailbox_size_limit=0" );
				execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
				exit( errno );
			}
			else
			{
				waitpid( ourPid, &status, 0 );
			}

			/* get black hole server */
			bValue = false;
			csValue.Clear();
			pPrefsObj->GetUseBlackholeServerFlag( &bValue, true );
			pPrefsObj->GetBlackholeServerName( csValue, true );

			if ( bValue )
			{
				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "maps_rbl_domains_enabled" ), kCFBooleanTrue );
			}
			else
			{
				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "maps_rbl_domains_enabled" ), kCFBooleanFalse );
			}

			if ( csValue.GetLength() )
			{
				cfMutableArray = CFArrayCreateMutable( NULL, 0, &cfArrayCallbacks );
				if ( cfMutableArray != NULL )
				{
					cfString = CFStringCreateWithCString( NULL, csValue.GetData(), kCFStringEncodingASCII );
					if ( cfString != NULL )
					{
						CFArrayAppendValue( cfMutableArray, cfString );
						CFRelease( cfString );
					}

					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "maps_rbl_domains" ), cfMutableArray );
					CFRelease( cfMutableArray );
				}
			}

			if ( bValue && csValue.GetLength() )
			{
				csTmp.Sprintf( "maps_rbl_domains=%s", csValue.GetData() );

				if ( (ourPid = vfork()) < 0 )
				{
					// can't fork
				}
				if ( ourPid == 0 )
				{
					execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
					exit( errno );
				}
				else
				{
					waitpid( ourPid, &status, 0 );
				}
			}

			/* get auth methods */
			bValue = false;
			uiValue = 0;
			pPrefsObj->GetOtherSMTPAuthFlag( &bValue, true );
			pPrefsObj->GetSMTP_CRAM_MD5_Value( &uiValue, true );

			if ( uiValue )
			{
				if ( !uiValue )
				{
					csTmp.Set( "smtpd_pw_server_security_options=cram-md5" );
				}
				else
				{
					csTmp.Set( "smtpd_pw_server_security_options=cram-md5,plain,login" );
				}

				if ( (ourPid = vfork()) < 0 )
				{
					// can't fork
				}
				if ( ourPid == 0 )
				{
					execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
					exit( errno );
				}
				else
				{
					waitpid( ourPid, &status, 0 );
				}
			}

			/* get relay server */
			DSHostPrefs *pHostPrefs = DSMgr::GetHostPrefsObj();
			if ( pHostPrefs != nil )
			{
				csValue.Clear();
				dsStatus = pHostPrefs->GetRelaySMTPServer( csValue, true );
				if ( (dsStatus == eDSNoErr) && (csValue.GetLength() != 0) )
				{
					cfString = CFStringCreateWithCString( NULL, csValue.GetData(), kCFStringEncodingASCII );
					if ( cfString != NULL )
					{
						CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "relayhost" ), cfString );
						CFRelease( cfString );
					}
				}

				dsStatus = pHostPrefs->GetRelaySMTPFlag( &bValue, true );
				if ( dsStatus == eDSNoErr )
				{
					if ( (bValue == true) && (csValue.GetLength() != 0) )
					{
						if ( (ourPid = vfork()) < 0 )
						{
							// can't fork
						}
						if ( ourPid == 0 )
						{
							csTmp.Set( "relayhost=" );
							csTmp.Append( csValue.GetData() );
							execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
							exit( errno );
						}
						else
						{
							waitpid( ourPid, &status, 0 );
						}
						CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "relayhost_enabled" ), kCFBooleanTrue );
					}
					else
					{
						CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "relayhost_enabled" ), kCFBooleanFalse );
					}
				}
				else
				{
					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "relayhost_enabled" ), kCFBooleanFalse );
				}
			}

			/* get allowed relay servers */
			dsStatus = pPrefsObj->GetAllowedRelayList( &pIPStrStrList, true );
			if ( (pIPStrStrList != NULL) && (dsStatus == eDSNoErr) )
			{
				cfMutableArray = CFArrayCreateMutable( NULL, 0, &cfArrayCallbacks );
				CFArrayAppendValue( cfMutableArray, CFSTR( "127.0.0.0/8" ) );
				csTmp.Set ("mynetworks=127.0.0.0/8" );

				pStrObj = pIPStrStrList;
				while ( pStrObj != NULL )
				{
					bBad = false;
					p = pStrObj->fString;
					if ( p != NULL )
					{
						/* is it some kind of host name or IP address range */
						while ( *p != NULL )
						{
							if ( isalpha( *p ) || (*p == '*') || (*p == '-') )
							{
								bBad = true;
								break;
							}
							p++;
						}
	
						if ( !bBad && (strcmp( pStrObj->fString, "127.0.0.1" ) != 0) )
						{
							csTmp.Append( "," );
							csTmp.Append( pStrObj->fString );

							cfString = CFStringCreateWithCString( NULL, pStrObj->fString, kCFStringEncodingASCII );
							if ( cfString != NULL )
							{
								CFArrayAppendValue( cfMutableArray, cfString );
								CFRelease( cfString );
							}
						}
					}
					pStrObj = pStrObj->fNext;
				}

				/* Allowed Relay List */
				pPrefsObj->GetUseAllowedRelayListFlag( &bValue, true );
				if ( bValue == true )
				{
					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "mynetworks_enabled" ), kCFBooleanTrue );
	
					if ( (ourPid = vfork()) < 0 )
					{
						// can't fork
					}
					if ( ourPid == 0 )
					{
						execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
						exit( errno );
					}
					else
					{
						waitpid( ourPid, &status, 0 );
					}
				}
				else
				{
					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "mynetworks_enabled" ), kCFBooleanFalse );
				}

				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "mynetworks" ), cfMutableArray );
				CFRelease( cfMutableArray );
				CString::FreeStringList( &pIPStrStrList );
			}

			csTmp.Clear();

			/* SMTP rejection list */
			dsStatus = pPrefsObj->GetSMTPRejectServerList( &pIPStrStrList, true );
			if ( (pIPStrStrList != NULL) && (dsStatus == eDSNoErr) )
			{
				cfMutableArray = CFArrayCreateMutable( NULL, 0, &cfArrayCallbacks );
				csTmp.Clear();

				pStrObj = pIPStrStrList;
				while ( pStrObj != NULL )
				{
					p = pStrObj->fString;
					if ( p != NULL )
					{
						bBad = false;
						/* is it some kind of host name or IP address range */
						while ( *p != NULL )
						{
							if ( isalpha( *p ) || (*p == '*') || (*p == '-') || (*p == '/') )
							{
								bBad = true;
								break;
							}
							p++;
						}
	
						if ( !bBad )
						{
							csTmp.Append( pStrObj->fString );
							csTmp.Append( "         REJECT\n" );

							cfString = CFStringCreateWithCString( NULL, pStrObj->fString, kCFStringEncodingASCII );
							if ( cfString != NULL )
							{
								CFArrayAppendValue( cfMutableArray, cfString );
								CFRelease( cfString );
							}
						}
					}
					pStrObj = pStrObj->fNext;
				}

				pPrefsObj->GetSMTPRejectServerFlag( &bValue, true );
				if ( bValue == true )
				{
					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "smtp_reject_list_enabled" ), kCFBooleanTrue );

					CFile tmpFile( "/etc/postfix/smtpdreject" , k644, true );

					tmpFile.write( csTmp.GetData(), csTmp.GetLength() );

					if ( (ourPid = vfork()) < 0 )
					{
						// can't fork
					}
					if ( ourPid == 0 )
					{
						execlp( POSTMAP_PATH, POSTMAP_PATH, "/etc/postfix/smtpdreject", (char *) 0 );
						exit( errno );
					}
					else
					{
						waitpid( ourPid, &status, 0 );
					}

					if ( (ourPid = vfork()) < 0 )
					{
						// can't fork
					}
					if ( ourPid == 0 )
					{
						csTmp.Set( "smtpd_client_restrictions=hash:/etc/postfix/smtpdreject" );
						execlp( POSTCONF_PATH, POSTCONF_PATH, "-e", csTmp.GetData(), (char *) 0 );
						exit( errno );
					}
					else
					{
						waitpid( ourPid, &status, 0 );
					}
				}
				else
				{
					CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "smtp_reject_list_enabled" ), kCFBooleanFalse );
				}

				CFDictionarySetValue( cfMutableDictRefPostfix, CFSTR( "smtp_reject_list" ), cfMutableArray );
				CFRelease( cfMutableArray );
				CString::FreeStringList( &pIPStrStrList );
			}

			if ( gLocalHostList != NULL )
			{
				pStrObj = gLocalHostList;
				while ( pStrObj != NULL )
				{
					if ( pStrObj->fString != NULL )
					{
						gCSLocalHostList.Append( "," );
						gCSLocalHostList.Append( pStrObj->fString );
					}
					pStrObj = pStrObj->fNext;
				}

				if ( (ourPid = vfork()) < 0 )
				{
					// can't fork
				}
				if ( ourPid == 0 )
				{
					execl( POSTCONF_PATH, POSTCONF_PATH, "-e", gCSLocalHostList.GetData(), (char *) 0 );
					exit( errno );
				}
				else
				{
					waitpid( ourPid, &status, 0 );
				}
			}
		}


		if ( cfMutableDictRefCyrus != NULL )
		{
			bValue = false;
			pPrefsObj->GetIMAP_EnabledFlag( &bValue, true );
			if ( bValue )
			{
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_imap" ), kCFBooleanTrue );
				EnableIMAPKey( "enable_imap", "yes" );
			}
			else
			{
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_imap" ), kCFBooleanFalse );
				EnableIMAPKey( "enable_imap", "no" );
			}

			bValue = false;
			pPrefsObj->GetPOP3_EnabledFlag( &bValue, true );
			if ( bValue )
			{
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_pop" ), kCFBooleanTrue );
				EnableIMAPKey( "enable_pop", "yes" );
			}
			else
			{
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "enable_pop" ), kCFBooleanFalse );
				EnableIMAPKey( "enable_pop", "no" );
			}

			/* Set default value to 1 == kerberos off */
			uiValue = 1;
			pPrefsObj->GetKerberosState( &uiValue, true );
			if ( uiValue == kKerberosStateRequired )
			{
				// Turn off IMAP clear text auth
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_clear" ), kCFBooleanFalse );
				EnableIMAPKey( "imap_auth_clear", "no" );

				// Turn off POP clear text auth
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_clear" ), kCFBooleanFalse );
				EnableIMAPKey( "pop_auth_clear", "no" );

				// Turn off IMAP CRAM-MD5 auth
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_cram_md5" ), kCFBooleanFalse );
				EnableIMAPKey( "imap_auth_cram_md5", "no" );

				// Turn off POP APOP auth
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_apop" ), kCFBooleanFalse );
				EnableIMAPKey( "pop_auth_apop", "no" );

				// Turn on IMAP Kerberos
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_gssapi" ), kCFBooleanTrue );
				EnableIMAPKey( "imap_auth_gssapi", "yes" );

				// Turn on POP Kerberos
				CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_gssapi" ), kCFBooleanTrue );
				EnableIMAPKey( "pop_auth_gssapi", "yes" );
			}
			else
			{
				// Turn kerberos on if allowed
				if ( uiValue == kKerberosStateAllowed )
				{
					// Turn on IMAP Kerberos
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_gssapi" ), kCFBooleanTrue );
					EnableIMAPKey( "imap_auth_gssapi", "yes" );
	
					// Turn on POP Kerberos
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_gssapi" ), kCFBooleanTrue );
					EnableIMAPKey( "pop_auth_gssapi", "yes" );
				}

				bValue = false;
				pPrefsObj->GetRequireCRAM_MD5_IMAP_Flag( &bValue, true );
				if ( bValue )
				{
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_cram_md5" ), kCFBooleanTrue );
					EnableIMAPKey( "imap_auth_cram_md5", "yes" );
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_clear" ), kCFBooleanFalse );
					EnableIMAPKey( "imap_auth_clear", "no" );
				}
				else
				{
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_cram_md5" ), kCFBooleanFalse );
					EnableIMAPKey( "imap_auth_cram_md5", "no" );
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "imap_auth_clear" ), kCFBooleanTrue );
					EnableIMAPKey( "imap_auth_clear", "yes" );
				}
	
				bValue = false;
				pPrefsObj->GetRequireAPOP_Flag( &bValue, true );
				if ( bValue )
				{
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_apop" ), kCFBooleanTrue );
					EnableIMAPKey( "pop_auth_apop", "yes" );
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_clear" ), kCFBooleanFalse );
					EnableIMAPKey( "pop_auth_clear", "no" );
				}
				else
				{
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_apop" ), kCFBooleanFalse );
					EnableIMAPKey( "pop_auth_apop", "no" );
					CFDictionarySetValue( cfMutableDictRefCyrus, CFSTR( "pop_auth_clear" ), kCFBooleanTrue );
					EnableIMAPKey( "pop_auth_clear", "yes" );
				}
			}
		}
	}

	cfDataRef = CFPropertyListCreateXMLData( NULL, cfMutableDictRef );
	if ( cfDataRef != NULL )
	{
		pFile = new CFile( "/etc/MailServicesOther.plist", k644, true );
		if ( pFile != NULL )
		{
			pFile->write(  CFDataGetBytePtr( cfDataRef ), CFDataGetLength( cfDataRef ) );
		}
		delete( pFile );
	}
} // ExportPrefs


// ---------------------------------------------------------------------------
//	* DoExport ()
// ---------------------------------------------------------------------------

void DoExport ( const char *inSrcPath, const char *inDstPath, eOptions inOptions )
{
    pid_t				ourPid		= 0;
    int					status		= 0;
	sInt32				siResult	= kNoErr;
	uInt32				uiDBFlags	= 0;
	bool				canRun		= false;
	bool				done		= false;
	CString				dbPath;
	struct passwd	   *pwd			= nil;

	try
	{
		// Looking for 10.2.x mail database
		if ( (inOptions == kMigrate10_2_Only) || (inOptions == kMigrateDefault) )
		{
			dbPath.Sprintf( "%s/%s", inSrcPath, spMacOSX10_2_MailDBName );
			if ( COSUtils::DoesFileExist( dbPath.GetData() ) == true )
			{
				// We have an older mail DB
				siResult = ValidateMailDB( dbPath.GetData() );
				if ( siResult == kDBSafe )
				{
					canRun = InitializeMainDB( dbPath.GetData(), uiDBFlags );
					if ( canRun )
					{
						CRootObject::Find();
		
						ExportMail( inDstPath );

						done = true;
					}
				}
			}
			else
			{
				::fprintf( stdout, "No mail database found at %s.\n", dbPath.GetData() );
			}
		}

		// Looking for 10.1.x mail database
		if ( (inOptions == kMigrate10_1_Only) || ((inOptions == kMigrateDefault) && !done) )
		{
			dbPath.Sprintf( "%s/%s", inSrcPath, spMacOSX10_1_MailDBName );
			if ( COSUtils::DoesFileExist( dbPath.GetData() ) == true )
			{
				// We have an older mail DB
				siResult = ValidateMailDB_10_1( dbPath.GetData() );
				if ( siResult == kDBSafe_10_1 )
				{
					canRun = InitializeMainDB_10_1( dbPath.GetData(), uiDBFlags );
					if ( canRun )
					{
						CRootObject_10_1::Find();
		
						ExportMail_10_1( inDstPath );

						done = true;
					}
				}
			}
			else
			{
				::fprintf( stdout, "No mail database found at %s.\n", dbPath.GetData() );
			}
		}

		if ( done )
		{
			pwd = ::getpwnam( "cyrus" );
			if ( pwd != nil )
			{
				setegid( pwd->pw_gid );
				seteuid( pwd->pw_uid );
			}
	
			if ( (ourPid = vfork()) < 0 )
			{
				// can't fork
			}
			if ( ourPid == 0 )
			{
				execlp( RECONSTRUCT_PATH, RECONSTRUCT_PATH, "-i", (char *) 0 );
				syslog( LOG_ERR, "reconstruct execlp failed errno = %d\n", errno );
				exit( errno );
			}
			else
			{
				waitpid( ourPid, &status, 0 );
			}
	
			seteuid( 0 );
			setegid( 0 );
		}
	}

	catch ( long err )
	{
		siResult = err;
	}

	catch ( ... )
	{
		siResult = -99;
	}
} // DoExport


// ----------------------------------------------------------------------------------------
//	* ExportMail_10_1 ()
//
// ----------------------------------------------------------------------------------------

void ExportMail_10_1 ( const char *inDestPath )
{
	OSErr 				osErr				= kNoErr;
	int					iTotalMsgs			= 0;
	int					iMailboxMsgs		= 0;
	int					iUserMsgs			= 0;
	int					fileUID				= 1;
	uid_t				uid					= 0;
	gid_t				gid					= 0;
	uInt64				freeBytes			= 0;
	uInt64				bytesNeeded			= 0;
	uInt32				messages			= 0;
	char		 	   *pData				= nil;
	const char		   *pPtr				= nil;
	const char		   *pPath				= nil;
	uInt32				uiDataLen			= 0;
	uInt32				uiFlags				= 0;
	uInt32				uiMsgFlags			= 0;
	uInt32				uiSeenFlag			= 0;
	ObjID				acctObjID			= 0;
	ObjID				spoolObjID			= 0;
	ObjID				envInfoID			= 0;
	ObjID				msgPartID			= 0;
   	ObjID               hostEntryID         = 0;
	sInt32				siMsgParts			= 0;
	CAccount_10_1	   *pAcctObj			= nil;
	CMailSpool_10_1	   *pSpoolObj			= nil;
	CEnvelope_10_1	   *pEnvObj				= nil;
	CEnvelopeInfo_10_1 *pEnvInfoObj			= nil;
	CMessagePart_10_1  *pMsgPartObj			= nil;
	CSmallMsgPart_10_1 *pSmallMsgPartObj	= nil;
   	CHostEntry_10_1    *pHostObj             = nil;
	CMailboxes_10_1	   *pMbox				= nil;
	struct passwd	   *pwd					= nil;
	SDBIterator_10_1	hostIterator;
	SDBIterator_10_1	acctIterator;
	SDBIterator_10_1	spoolIterator;
	SDBIterator_10_1   *pEnvInfoIterator	= nil;
	SDBIterator_10_1	envInfoIterator;
	DSMailUser	 	   *pMailUser			= nil;
	DSMailGroup	 	   *pMailGroup			= nil;
	CFile			   *pFile				= nil;
	CFile			   *pExtra				= nil;
	CString				csPath;
	CString				csFullPath;
	CString				csAcctName;
	CString				csBuffer;
	CString				csUserPath;
	CString				csUnknownUser;
	struct statfs		statbuf;

	try
	{
		// Get the cyrus user
		pwd = ::getpwnam( "cyrus" );
		if ( pwd != nil )
		{
			uid = pwd->pw_uid;
		}
		else
		{
			syslog( LOG_WARNING, "No cyrus user found setting owner to root." );
			::fprintf( stdout, "No cyrus user found setting owner to root." );
		}

		// Get the mail group
		gDSMgr->GetGroupByName( "mail", &pMailGroup, false );
		if ( pMailGroup != nil )
		{
			gid = pMailGroup->GetGroupID();

			delete( pMailGroup );
			pMailGroup = nil;
		}

		COSUtils::CreateAndVerifyPath( inDestPath, uid, gid, 0700 );

		csUserPath.Sprintf( "%s/user", inDestPath );
		csUnknownUser.Sprintf( "%s/unknown_users", inDestPath );

		// Do size preflight check
		// Iterate across all envelope infos
		osErr = gDB_10_1->CreateIterator( kEnvelopeInfoSignature, &envInfoIterator );
		if ( osErr == kNoErr )
		{
			while ( gDB_10_1->NextObject( &envInfoIterator, envInfoID ) == kNoErr )
			{
				pEnvInfoObj = CEnvelopeInfo_10_1::FindByID( envInfoID );
				if ( pEnvInfoObj != NULL )
				{
					if ( sbReset == true )
					{
						pEnvInfoObj->SetMigrationFlag( kReservedConst );
					}

					if ( pEnvInfoObj->GetMigrationFlag() != kMigratedConst )
					{
						bytesNeeded += pEnvInfoObj->GetMsgSize();
						messages++;
					}
				}
				pEnvInfoObj->Done( pEnvInfoObj );
			}

			if ( (siMaxSize != 0) && (bytesNeeded > siMaxSize) )
			{
				syslog( LOG_INFO, "Migration halted. %llu bytes required is greater than %d maximum size requested.", bytesNeeded, siMaxSize );
				::fprintf( stdout, "Migration halted. %llu bytes required is greater than %d maximum size requested.\n", bytesNeeded, siMaxSize );
				exit( 256 );
			}

			::statfs( inDestPath, &statbuf );

			freeBytes = (uInt64)statbuf.f_bavail * (uInt64)statbuf.f_bsize;

			if ( bytesNeeded > freeBytes )
			{
				syslog( LOG_INFO, "Cannot migrate mail database. %llu bytes required but only %llu bytes free.", bytesNeeded, freeBytes );
				::fprintf( stdout, "Cannot migrate mail database.\n" );
				::fprintf( stdout, "  - Destination directory : %s\n", inDestPath );
				::fprintf( stdout, "  - Total bytes needed    : %llu\n", bytesNeeded );
				::fprintf( stdout, "  - Total bytes free      : %llu\n", freeBytes );
				::fprintf( stdout, "                            ----------\n" );
				::fprintf( stdout, "  - Extra bytes needed    : %llu\n", bytesNeeded -freeBytes );
				exit( 128 );
			}
			else
			{
				syslog( LOG_ERR, "Migrating %lu messages requiring %llu bytes.", messages, bytesNeeded );
				::fprintf( stdout, "Migrating %lu messages requiring %llu bytes.\n", messages, bytesNeeded );
			}

			// Force any DB changes to be saved
			gDB_10_1->FlushDatabaseFile( kDBSafe_10_1 );
		}

		// Iterate across all accounts
		osErr = gDB_10_1->CreateIterator( kAccountSignature_10_1, &acctIterator );
		if ( osErr == kNoErr )
		{
			while ( gDB_10_1->NextObject( &acctIterator, acctObjID ) == kNoErr )
			{
				iUserMsgs = 0;
				pPath = csUnknownUser.GetData();
				pAcctObj = CAccount_10_1::FindByID( acctObjID );
				if ( pAcctObj != nil )
				{
					gDSMgr->GetUserByID( pAcctObj->GetMailUserID(), &pMailUser );
					if ( pMailUser != nil )
					{
						if ( pMailUser->GetUserName() != nil )
						{
							csAcctName.Set( pMailUser->GetUserName() );
						}
						else
						{
							csAcctName.Sprintf( "%d", pMailUser->GetUserID() );
						}

						delete( pMailUser );
						pMailUser = nil;

						pPath = csUserPath.GetData();
					}
					else
					{
						if ( pAcctObj->GetAccountName() != nil )
						{
							csAcctName.Set( pAcctObj->GetAccountName() );
						}
						else
						{
							csAcctName.Sprintf( "Unknown_User_ID_%d", pAcctObj->GetMailUserID() );
						}
					}

					// Iterate across all mailboxes for each account
					osErr = gDB_10_1->CreateIterator( kMailSpoolSignature, &spoolIterator, acctObjID );
					if ( osErr == kNoErr )
					{
						while ( gDB_10_1->NextObject( &spoolIterator, spoolObjID ) == kNoErr )
						{
							iMailboxMsgs = 0;
							pSpoolObj = CMailSpool_10_1::FindByID( spoolObjID );
							if ( pSpoolObj != nil )
							{
								pMbox = new CMailboxes_10_1( nil, true, acctObjID );
								if ( pMbox != nil )
								{
									pMbox->GetFullPath( csPath, pSpoolObj );

									// Is this the INBOX
									if ( CUtils::Stricmp( csPath.GetData(), "INBOX" ) == 0 )
									{
										csFullPath.Sprintf( "%s/%s", pPath, csAcctName.GetData() );
									}
									else if ( (CUtils::Strincmp( csPath.GetData(), "INBOX", 5 ) == 0) && (csPath.GetLength() > 5) )
									{
										// Is a sub INBOX mailbox (ie. INBOX/Junk)
										FixPath( &csPath );
										csFullPath.Sprintf( "%s/%s/%s", pPath, csAcctName.GetData(), csPath.GetData() );
									}
									else
									{
										csFullPath.Sprintf( "%s/%s/%s", pPath, csAcctName.GetData(), csPath.GetData() );
									}

									syslog( LOG_INFO,  "Exporting mailbox %s to %s", csPath.GetData(), csFullPath.GetData() );
									::fprintf( stdout, "Exporting mailbox: %s\n", csPath.GetData() );

									COSUtils::CreateAndVerifyPath( csFullPath.GetData(), uid, gid, 0700 );

									fileUID = 1;

									pEnvInfoIterator = pSpoolObj->GetEnvelopeInfoIterator();
									if ( pEnvInfoIterator != nil )
									{
										while ( gDB_10_1->NextObject( pEnvInfoIterator, envInfoID ) == kNoErr )
										{
											pEnvInfoObj = CEnvelopeInfo_10_1::FindByID( envInfoID );
											if ( (pEnvInfoObj != NULL) && (pEnvInfoObj->GetMigrationFlag() != kMigratedConst) )
											{
												pEnvObj = CEnvelope_10_1::FindByID( pEnvInfoObj->GetEnvelopeID() );
												if ( pEnvObj != nil )
												{
													pFile = OpenDataFile( &csFullPath, &fileUID, uid, gid, &pExtra );
													if ( pFile != NULL )
													{
														iTotalMsgs++;
														iMailboxMsgs++;
														iUserMsgs++;

														siMsgParts = pEnvObj->GetMessagePartCount();
	
														msgPartID = pEnvObj->GetMsgPartListHeadID();
														if ( msgPartID != 0 )
														{
															while ( msgPartID != 0 )
															{
																pMsgPartObj = CMessagePart_10_1::FindByID( msgPartID );
																if ( pMsgPartObj != nil )
																{
																	pData = pMsgPartObj->GetPartData();
																	uiDataLen = pMsgPartObj->GetDataLength();
																	if ( (pData != nil) && (uiDataLen > 0) )
																	{
																		pFile->write( pData, uiDataLen );
																	}
																}
																msgPartID = pMsgPartObj->GetNextMessagePartID();
																pMsgPartObj->Done( pMsgPartObj );
															}
														}
														else
														{
															pSmallMsgPartObj = CSmallMsgPart_10_1::FindByID( pEnvObj->GetSmallMsgPartID() );
															if ( pSmallMsgPartObj != NULL )
															{
																// Send the data across
																pData = pSmallMsgPartObj->GetPartData();
																uiDataLen = pSmallMsgPartObj->GetDataLength();
																if ( (pData != nil) && (uiDataLen > 0) )
																{
																	pFile->write( pData, uiDataLen );
																}
																pSmallMsgPartObj->Done( pSmallMsgPartObj );
															}
														}

														pFile->write( "\r\n", 2 );
														delete( pFile );
														pFile = nil;

														if ( pExtra != nil )
														{
															uiFlags = 0;
															uiSeenFlag = 0;
															uiMsgFlags = pEnvInfoObj->GetFlags();
															if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Answered )
															{
																uiFlags |= FLAG_ANSWERED;
															}

															if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Flagged )
															{
																uiFlags |= FLAG_FLAGGED;
															}

															if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Deleted )
															{
																uiFlags |= FLAG_DELETED;
															}

															if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Draft )
															{
																uiFlags |= FLAG_DRAFT;
															}

															if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Seen )
															{
																uiSeenFlag = 1;
															}

															csBuffer.Sprintf(  "%u %u %u\n", pEnvObj->GetCreationTime(), uiFlags, uiSeenFlag );
															pExtra->write( csBuffer.GetData(), csBuffer.GetLength() );

															delete( pExtra );
															pExtra = nil;
														}
													}
													pEnvObj->Done( pEnvObj );
												}
												pEnvInfoObj->SetMigrationFlag( kMigratedConst );
												pEnvInfoObj->Done( pEnvInfoObj );
											}
										}
										pSpoolObj->ReleaseEnvelopeInfoIterator( pEnvInfoIterator );
									}
									delete( pMbox );
									pMbox = nil;
								}

								syslog( LOG_INFO,  "Posted: %d messages, for user: %s, in mailbox: %s", iMailboxMsgs, csAcctName.GetData(), csPath.GetData() );
								::fprintf( stdout, "  - Posted: %d messages for user \"%s\" in mailbox \"%s\"\n", iMailboxMsgs, csAcctName.GetData(), csPath.GetData() );

								pSpoolObj->Done( pSpoolObj );
							}
						}
					}

					::sync();

					syslog( LOG_INFO,  "Posted: %d total messages, for user: %s", iUserMsgs, csAcctName.GetData() );
					::fprintf( stdout, "\nDone posting mail for user: %s\n", csAcctName.GetData() );
					::fprintf( stdout, "  - Posted: %d total messages, for user: %s\n\n", iUserMsgs, csAcctName.GetData() );

					pAcctObj->Done( pAcctObj );
					gDB_10_1->ReleaseIterator( &spoolIterator );
				}
				// Save migration flags
				gDB_10_1->FlushDatabaseFile( kDBSafe_10_1 );
			}
		}

        // Get local host names
        osErr = gDB_10_1->CreateIterator( kHostEntrySignature_10_1, &hostIterator );
        if ( osErr == kNoErr )
        {
            while ( gDB_10_1->NextObject( &hostIterator, hostEntryID ) == kNoErr )
            {
                pHostObj = CHostEntry_10_1::FindByID( hostEntryID );
                if ( pHostObj != nil )
                {
                    // Is it a local host name
                    if ( pHostObj->GetDestClass() == CHostEntry::kAdminClassLocal )
                    {
                        CString::AppendString( &gLocalHostList, pHostObj->GetHostName(), 0 );
                    }
                    pHostObj->Done( pHostObj );
                }
            }
            gDB_10_1->ReleaseIterator( &hostIterator );
        }

		syslog( LOG_INFO,  "Posted: %d total messages for all users", iTotalMsgs );
		::fprintf( stdout, "\nDone posting mail for all users\n" );
		::fprintf( stdout, "  - Posted: %d total messages for all users\n", iTotalMsgs );

		gDB_10_1->ReleaseIterator( &acctIterator );
	}

	catch ( long err )
	{
	}
} // ExportMail_10_1


// ----------------------------------------------------------------------------------------
//	* ExportMail ()
//
// ----------------------------------------------------------------------------------------

void ExportMail ( const char *inDestPath )
{
	OSErr 				osErr				= kNoErr;
	int					iTotalMsgs			= 0;
	int					iMailboxMsgs		= 0;
	int					iUserMsgs			= 0;
	int					fileUID				= 1;
	uInt32				uiCntr				= 0;
	uInt32				uiDataLen			= 0;
	uInt32				uiFlags				= 0;
	uInt32				uiSeenFlag			= 0;
	uInt32				uiMsgFlags			= 0;
	ObjID				acctObjID			= 0;
	ObjID				spoolObjID			= 0;
	ObjID				envInfoID			= 0;
	ObjID				hostObjID			= 0;
   	ObjID               hostEntryID         = 0;
	uid_t				uid					= 0;
	gid_t				gid					= 0;
	uInt64				freeBytes			= 0;
	uInt64				bytesNeeded			= 0;
	uInt32				messages			= 0;
	bool				bIsSubPath			= false;
	char			   *pData				= nil;
	const char		   *pPtr				= nil;
	const char		   *pPath				= nil;
	CAccount		   *pAcctObj			= nil;
	CMailSpool		   *pSpoolObj			= nil;
	CEnvelope		   *pEnvObj				= nil;
	CEnvelopeInfo	   *pEnvInfoObj			= nil;
   	CHostEntry         *pHostObj             = nil;
	CFile			   *pFile				= nil;
	CFile			   *pExtra				= nil;
	CMailboxes		   *pMbox				= nil;
	DSMailUser		   *pMailUser			= nil;
	DSMailGroup		   *pMailGroup			= nil;
	struct passwd	   *pwd					= nil;
	SDBIterator		   *pEnvInfoIterator	= nil;
	SDBIterator			acctIterator;
	SDBIterator			spoolIterator;
	SDBIterator			envInfoIterator;
	SDBIterator			hostIterator;
	CString				csPath;
	CString				csFullPath;
	CString				csAcctName;
	CString				csBuffer;
	CString				csUserPath;
	CString				csUnknownUser;
	struct statfs		statbuf;

	try
	{
		// Get the cyrus user
		pwd = ::getpwnam( "cyrus" );
		if ( pwd != nil )
		{
			uid = pwd->pw_uid;
		}
		else
		{
			syslog( LOG_WARNING, "No cyrus user found setting owner to root." );
			::fprintf( stdout, "No cyrus user found setting owner to root.\n" );
		}

		// Get the mail group
		gDSMgr->GetGroupByName( "mail", &pMailGroup, false );
		if ( pMailGroup != nil )
		{
			gid = pMailGroup->GetGroupID();

			delete( pMailGroup );
			pMailGroup = nil;
		}

		COSUtils::CreateAndVerifyPath( inDestPath, uid, gid, 0700 );

		csUserPath.Sprintf( "%s/user", inDestPath );
		csUnknownUser.Sprintf( "%s/unknown_users", inDestPath );

		// Do size preflight check
		// Iterate across all envelope infos
		osErr = gDB->CreateIterator( kEnvelopeInfoSignature, &envInfoIterator );
		if ( osErr == kNoErr )
		{
			while ( gDB->NextObject( &envInfoIterator, envInfoID ) == kNoErr )
			{
				pEnvInfoObj = CEnvelopeInfo::FindByID( envInfoID );
				if ( pEnvInfoObj != NULL )
				{
					if ( sbReset == true )
					{
						pEnvInfoObj->SetMigrationFlag( kReservedConst );
					}

					if ( pEnvInfoObj->GetMigrationFlag() != kMigratedConst )
					{
						bytesNeeded += pEnvInfoObj->GetMsgSize();
						messages++;
					}
				}
				pEnvInfoObj->Done( pEnvInfoObj );
			}

			if ( (siMaxSize != 0) && (bytesNeeded > siMaxSize) )
			{
				syslog( LOG_INFO, "Migration halted. %llu bytes required is greater than %d maximum size requested.", bytesNeeded, siMaxSize );
				::fprintf( stdout, "Migration halted. %llu bytes required is greater than %d maximum size requested.\n", bytesNeeded, siMaxSize );
				exit( 256 );
			}

			::statfs( inDestPath, &statbuf );

			freeBytes = (uInt64)statbuf.f_bavail * (uInt64)statbuf.f_bsize;

			if ( bytesNeeded > freeBytes )
			{
				syslog( LOG_INFO, "Cannot migrate mail database. %llu bytes required but only %llu bytes free.", bytesNeeded, freeBytes );
				::fprintf( stdout, "Cannot migrate mail database.\n" );
				::fprintf( stdout, "  - Destination directory : %s\n", inDestPath );
				::fprintf( stdout, "  - Total bytes needed    : %llu\n", bytesNeeded );
				::fprintf( stdout, "  - Total bytes free      : %llu\n", freeBytes );
				::fprintf( stdout, "                            ----------\n" );
				::fprintf( stdout, "  - Extra bytes needed    : %llu\n", bytesNeeded -freeBytes );
				exit( 128 );
			}
			else
			{
				syslog( LOG_ERR, "Migrating %lu messages requiring %llu bytes.", messages, bytesNeeded );
				::fprintf( stdout, "Migrating %lu messages requiring %llu bytes.\n", messages, bytesNeeded );
			}

			// Force any DB changes to be saved
			gDB->FlushDatabaseFile( kDBSafe );
		}

		// Iterate across all accounts
		osErr = gDB->CreateIterator( kAccountSignature, &acctIterator );
		if ( osErr == kNoErr )
		{
			while ( gDB->NextObject( &acctIterator, acctObjID ) == kNoErr )
			{
				iUserMsgs = 0;
				pPath = csUnknownUser.GetData();
				pAcctObj = CAccount::FindByID( acctObjID );
				if ( pAcctObj != nil )
				{
					gDSMgr->GetUserByID( pAcctObj->GetMailUserID(), &pMailUser );
					if ( pMailUser != nil )
					{
						if ( pMailUser->GetUserName() != nil )
						{
							csAcctName.Set( pMailUser->GetUserName() );
						}
						else
						{
							csAcctName.Sprintf( "%d", pMailUser->GetUserID() );
						}

						delete( pMailUser );
						pMailUser = nil;

						pPath = csUserPath.GetData();
					}
					else
					{
						if ( pAcctObj->GetAccountName() != nil )
						{
							csAcctName.Set( pAcctObj->GetAccountName() );
						}
						else
						{
							csAcctName.Sprintf( "Unknown_User_ID_%d", pAcctObj->GetMailUserID() );
						}
					}

					// Iterate across all mailboxes for each account
					osErr = gDB->CreateIterator( kMailSpoolSignature, &spoolIterator, acctObjID );
					if ( osErr == kNoErr )
					{
						while ( gDB->NextObject( &spoolIterator, spoolObjID ) == kNoErr )
						{
							iMailboxMsgs = 0;
							pSpoolObj = CMailSpool::FindByID( spoolObjID );
							if ( pSpoolObj != nil )
							{
								pMbox = new CMailboxes( nil, true, acctObjID );
								if ( pMbox != nil )
								{
									pMbox->GetFullPath( csPath, pSpoolObj, kHierDelimStr );

									// Is this the INBOX
									if ( CUtils::Stricmp( csPath.GetData(), "INBOX" ) == 0 )
									{
										csFullPath.Sprintf( "%s/%s", pPath, csAcctName.GetData() );
									}
									else if ( (CUtils::Strincmp( csPath.GetData(), "INBOX", 5 ) == 0) && (csPath.GetLength() > 5) )
									{
										// Is a sub INBOX mailbox (ie. INBOX/Junk)
										FixPath( &csPath );
										csFullPath.Sprintf( "%s/%s/%s", pPath, csAcctName.GetData(), csPath.GetData() );
									}
									else
									{
										csFullPath.Sprintf( "%s/%s/%s", pPath, csAcctName.GetData(), csPath.GetData() );
									}

									syslog( LOG_INFO,  "Exporting mailbox %s to %s", csPath.GetData(), csFullPath.GetData() );
									::fprintf( stdout, "Exporting mailbox: %s\n", csPath.GetData() );

									COSUtils::CreateAndVerifyPath( csFullPath.GetData(), uid, gid, 0700 );

									fileUID = 1;

									pEnvInfoIterator = pSpoolObj->GetEnvelopeInfoIterator();
									if ( pEnvInfoIterator != nil )
									{
										while ( gDB->NextObject( pEnvInfoIterator, envInfoID ) == kNoErr )
										{
											pEnvInfoObj = CEnvelopeInfo::FindByID( envInfoID );
											if ( (pEnvInfoObj != NULL) && (pEnvInfoObj->GetMigrationFlag() != kMigratedConst) )
											{
												pEnvObj = CEnvelope::FindByID( pEnvInfoObj->GetEnvelopeID() );
												if ( pEnvObj != nil )
												{
													pData = pEnvObj->GetData( uiDataLen );
													if ( pData != nil )
													{
														pFile = OpenDataFile( &csFullPath, &fileUID, uid, gid, &pExtra );
														if ( pFile != NULL )
														{
															iTotalMsgs++;
															iMailboxMsgs++;
															iUserMsgs++;

															pFile->write( pData, uiDataLen );

															delete( pFile );
															pFile = nil;

															if ( pExtra != nil )
															{
																uiFlags = 0;
																uiSeenFlag = 0;
																uiMsgFlags = pEnvInfoObj->GetFlags();
																if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Answered )
																{
																	uiFlags |= FLAG_ANSWERED;
																}

																if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Flagged )
																{
																	uiFlags |= FLAG_FLAGGED;
																}

																if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Deleted )
																{
																	uiFlags |= FLAG_DELETED;
																}

																if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Draft )
																{
																	uiFlags |= FLAG_DRAFT;
																}

																if ( uiMsgFlags & CEnvelopeInfo::kIMAP4Seen )
																{
																	uiSeenFlag = 1;
																}

																csBuffer.Sprintf( "%u %u %u\n", pEnvObj->GetCreationTime(), uiFlags, uiSeenFlag );
																pExtra->write( csBuffer.GetData(), csBuffer.GetLength() );

																delete( pExtra );
																pExtra = nil;
															}
														}
														free( pData );
														pData = nil;
													}
													pEnvObj->Done( pEnvObj );
												}
												pEnvInfoObj->SetMigrationFlag( kMigratedConst );
												pEnvInfoObj->Done( pEnvInfoObj );
											}

										}
										pSpoolObj->ReleaseEnvelopeInfoIterator( pEnvInfoIterator );
									}
									delete( pMbox );
									pMbox = nil;
								}

								syslog( LOG_INFO,  "Posted: %d messages, for user: %s, in mailbox: %s", iMailboxMsgs, csAcctName.GetData(), csPath.GetData() );
								::fprintf( stdout, "  - Posted: %d messages for user \"%s\" in mailbox \"%s\"\n", iMailboxMsgs, csAcctName.GetData(), csPath.GetData() );

								pSpoolObj->Done( pSpoolObj );
							}
						}
					}

					::sync();

					syslog( LOG_INFO,  "Posted: %d total messages, for user: %s", iUserMsgs, csAcctName.GetData() );
					::fprintf( stdout, "\nDone posting mail for user: %s\n", csAcctName.GetData() );
					::fprintf( stdout, "  - Posted: %d total messages, for user: %s\n\n", iUserMsgs, csAcctName.GetData() );

					pAcctObj->Done( pAcctObj );
					gDB->ReleaseIterator( &spoolIterator );
				}
				// Save migration flags
				gDB->FlushDatabaseFile( kDBSafe );
			}
		}

        // Get local host names
        osErr = gDB->CreateIterator( kHostEntrySignature_10_1, &hostIterator );
        if ( osErr == kNoErr )
        {
            while ( gDB->NextObject( &hostIterator, hostEntryID ) == kNoErr )
            {
                pHostObj = CHostEntry::FindByID( hostEntryID );
                if ( pHostObj != nil )
                {
                    // Is it a local host name
                    if ( pHostObj->GetDestClass() == CHostEntry::kAdminClassLocal )
                    {
                        CString::AppendString( &gLocalHostList, pHostObj->GetHostName(), 0 );
                    }
                    pHostObj->Done( pHostObj );
                }
            }
            gDB->ReleaseIterator( &hostIterator );
        }

		syslog( LOG_INFO,  "Posted: %d total messages for all users", iTotalMsgs );
		::fprintf( stdout, "\nDone posting mail for all users\n" );
		::fprintf( stdout, "  - Posted: %d total messages for all users\n", iTotalMsgs );

		gDB->ReleaseIterator( &acctIterator );
	}

	catch ( long err )
	{
	}
} // DoExport


// ----------------------------------------------------------------------------------------
//	* FixPath ()
//
// ----------------------------------------------------------------------------------------

void FixPath ( CString *inStr )
{
	char *p	= NULL;
	
	if ( inStr != NULL )
	{
		p = inStr->GetData();

		while ( *p != NULL )
		{
			if ( *p == '/' )
			{
				*p = '^';
			}
			p++;
		}
	}
} // FixPath

// ----------------------------------------------------------------------------------------
//	* OpenDataFile ()
//
// ----------------------------------------------------------------------------------------

CFile * OpenDataFile ( CString *inStr, int *inOutFileUID, uid_t inUID, gid_t inGID, CFile **inOutExtra )
{
	int				iResult		= 0;
	int				fileUID		= 0;
	CFile		   *pFile		= NULL;
	CString			csFilePath;
	CString			csExtraFilePath;
	struct stat		statbuf;

	fileUID = *inOutFileUID;

	while ( iResult == 0 )
	{
		csFilePath.Sprintf( "%s/%d.", inStr->GetData(), fileUID++ );

		iResult = ::stat( csFilePath.GetData(), &statbuf );
	}

	*inOutFileUID = fileUID;
	pFile = new CFile( csFilePath.GetData(), inUID, inGID, k600 );

	if ( pFile != nil )
	{
		csExtraFilePath.Set( csFilePath.GetData() );
		csExtraFilePath.Append( "ams_extra_data" );

		*inOutExtra = new CFile( csExtraFilePath.GetData(), inUID, inGID, k600 );
	}

	return( pFile );

} // OpenDataFile


// ---------------------------------------------------------------------------
//	* sIsRunning ()
//
// ---------------------------------------------------------------------------

pid_t sIsRunning ( const char *inProcName )
{
	register size_t		i ;
	register pid_t 		pidLast		= -1 ;
	int					mib[]		= { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
	size_t				ulSize		= 0;

	// Allocate space for complete process list
	if ( 0 > sysctl( mib, 4, NULL, &ulSize, NULL, 0) )
	{
		return( pidLast );
	}

	i = ulSize / sizeof( struct kinfo_proc );
	struct kinfo_proc	*kpspArray = new kinfo_proc[ i ];
	if ( !kpspArray )
	{
		return( pidLast );
	}

	// Get the proc list
	ulSize = i * sizeof( struct kinfo_proc );
	if ( 0 > sysctl( mib, 4, kpspArray, &ulSize, NULL, 0 ) )
	{
		delete [] kpspArray;
		return( pidLast );
	}

	register struct kinfo_proc	*kpsp = kpspArray;
	register pid_t 				pidParent = -1, pidProcGroup = -1;

	for ( ; i-- ; kpsp++ )
	{
		// Skip names that don't match
		if ( CUtils::Strcmp( kpsp->kp_proc.p_comm, inProcName ) != 0 )
		{
			continue;
		}

		// Skip our id
		if ( kpsp->kp_proc.p_pid == ::getpid() )
		{
			continue;
		}

		// If it's not us, is it a zombie
		if ( kpsp->kp_proc.p_stat == SZOMB )
		{
			continue;
		}

		// If the name doesn't match, continue
		if ( CUtils::Strcmp( kpsp->kp_proc.p_comm, inProcName ) == 0 )
		{
			pidLast = 0;
			break;
		}
	}

	delete [] kpspArray;

	return( pidLast );

} // sIsRunning


//-----------------------------------------------------------------------------
//	* sHelp ()
//
//-----------------------------------------------------------------------------

void sHelp ( FILE *inFile )
{
	sVersion( inFile );
} // sHelp


//-----------------------------------------------------------------------------
//	* sVersion ()
//
//-----------------------------------------------------------------------------

void sVersion ( FILE *inFile )
{
	CString		csVer( spAppName );

	csVer.Append( spVersion );
	csVer.Append( "\n" );

	::fprintf( inFile, csVer.GetData() );
} // sVersion


//-----------------------------------------------------------------------------
//	* sAppleVersion ()
//
//-----------------------------------------------------------------------------

void sAppleVersion ( FILE *inFile )
{
	CString		csVer( spAppName );

	csVer.Append( spVersion );
	csVer.Append( spBuild );
	csVer.Append( "\n" );

	::fprintf( inFile, csVer.GetData() );

} // sAppleVersion


//-----------------------------------------------------------------------------
//	* sUsage ()
//
//-----------------------------------------------------------------------------

void sUsage ( FILE *inFile, const char *inArgv0, int inErr, const char *inStr )
{
	static const char * const	szpNotRoot		= "%s: must be run by root\n";
	static const char * const	szpNoSrc		= "%s: missing <source>\n";
	static const char * const	szpBadSrcFile	= "%s: source database \"%s\" does not exist\n";
	static const char * const	szpBadSrc		= "%s: source path \"%s\" does not exist\n";
	static const char * const	szpNoDst		= "%s: missing <destination>\n";
	static const char * const	szpBadDest		= "%s: destination path \"%s\" does not exist\n";
	static const char * const	szpTooManyArgs	= "%s: too many arguments: %s\n";
	static const char * const	szpIllegalArg	= "%s: illegal argument: %s\n";
	static const char * const	szpNoMaxsize	= "%s: missing <maxsize>\n";
	static const char * const	szpBadMaxsize	= "%s: bad maxsize value: %s\n";
	static const char * const	szpUsage =
		"\n"
		"usage: amsmailtool [-source <path>] [-destination <path>] [-maxsize <size>]\n"
		"       amsmailtool -migrate.10.1 [-source <path>] [-destination <path>]\n"
		"       amsmailtool -migrate.10.2 [-source <path>] [-destination <path>]\n"
		"\n"
		"    By default the amsmailtool will attemtp to migrate a 10.2.x mail\n"
		"       database or a 10.1.x if the previous was not found. \n"
		"\n"
		"    -migrate.10.1\n"
		"      Migrate a 10.1.x (MacOSXMailDB) AMS Mail Database\n"
		"    -migrate.10.2\n"
		"      Migrate 10.2.x (MacOSXMailDatabase) AMS Mail Database\n"
		"    -source <AMS database source path>\n"
		"        Use alternate mail database source location.\n"
		"        Default: /Library/AppleMailServer\n"
		"    -destination <destination path>\n"
		"        Use alternate mail destination.\n"
		"        Default: /var/spool/imap\n"
		"    -maxsize <size>\n"
		"        This option is used to check the destination for adequate\n"
		"        disk space to contain the newly migrated mail.  The migration\n"
		"        tool will exit if mail data to be migrated exceeds this max value\n"
		"    -ver\n"
		"        Display version.\n"
		"    -help\n"
		"        Detailed help.\n"
		"";

	switch ( inErr )
	{
		case kNotRoot:
			::fprintf( inFile, szpNotRoot, inArgv0 );
			break;

		case kMissingSrcPath:
			::fprintf( inFile, szpNoSrc, inArgv0 );
			::fprintf( inFile, szpUsage );
			break;

		case kBadSrcPath:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpBadSrc, inArgv0, inStr );
			}
			::fprintf( inFile, szpUsage );
			break;

		case kBadSrcFile:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpBadSrcFile, "Warning", inStr );
			}
			break;

		case kMissingDestPath:
			::fprintf( inFile, szpNoDst, inArgv0 );
			::fprintf( inFile, szpUsage );
			break;

		case kBadDestPath:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpBadDest, inArgv0, inStr );
			}
			::fprintf( inFile, szpUsage );
			break;

		case kTooManyArgs:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpTooManyArgs, inArgv0, inStr );
			}
			::fprintf( inFile, szpUsage );
			break;

		case kIllegalArg:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpIllegalArg, inArgv0, inStr );
			}
			::fprintf( inFile, szpUsage );
			break;

		case kMissingSize:
			::fprintf( inFile, szpNoMaxsize, inArgv0 );
			::fprintf( inFile, szpUsage );
			break;

		case kBadSizeArg:
			if( inStr != NULL )
			{
				::fprintf( inFile, szpBadMaxsize, inArgv0, inStr );
			}
			::fprintf( inFile, szpUsage );
			break;

		default:
			::fprintf( inFile, szpUsage );
	}
} // sUsage


// ----------------------------------------------------------------------------------------
//	* sInitDirectory ()
//
// ----------------------------------------------------------------------------------------

eAppError sInitDirectory ( void )
{
	eAppError	siResult	= kAppNoErr;

	try
	{
		gDSMgr = new DSMgr;
		if ( gDSMgr != nil )
		{
			gDSMgrRouter = gDSMgr;

			siResult = (eAppError)gDSMgr->Initialize();
			if ( siResult == kAppNoErr )
			{
				gDSMgr->InitServerLogPrefs();
			}
	 	}
		else
		{
			siResult = kAppMemAllocError;
		}
	}

	catch ( long err )
	{
		siResult = (eAppError)err;
	}

	catch ( ... )
	{
		siResult = kAppUnknownErr;
	}

	return( siResult );

} // sInitDirectory
