/*
	$Id: CUtils.cpp,v 1.1 2003/04/20 23:29:40 dasenbro Exp $

	File:	CUtils.cpp

	Contains: Implementation of the various OS Utilities

	Version:	Apple Mail Server - Mac OS X :  $Revision: 1.1 $

	Written by: Michael Dasenbrock

	Copyright:	© 1996-2001 by Apple Computer, Inc., all rights reserved.

 	NOT_FOR_OPEN_SOURCE <to be reevaluated at a later time>

	Change History:

		$Log: CUtils.cpp,v $
		Revision 1.1  2003/04/20 23:29:40  dasenbro
		Initial check-in.
		
		Revision 1.17  2002/06/07 22:43:23  dasenbro
		Now call GetDataLength() instead of GetLength() on blocks that may contain imbedded NULL's.
		
		Revision 1.16  2002/05/30 16:53:09  dasenbro
		Added CalcCRC().
		
		Revision 1.15  2002/05/09 16:59:04  dasenbro
		Changed all str... calls to CUtils::Str... to be NULL safe.
		
		Revision 1.14  2002/04/15 21:47:22  dasenbro
		Synatx change in CUtils::Strstr() for clarity.
		
		Revision 1.13  2002/03/21 16:41:47  dasenbro
		Updated file version information.
		
		Revision 1.12  2002/02/20 20:31:47  dasenbro
		Syntax changes.
		
		Revision 1.11  2002/01/14 17:04:09  dasenbro
		Initial S4 updates.
		
		Revision 1.10  2001/06/21 20:51:11  dasenbro
		Updated file header info.
		
		Revision 1.9  2001/06/21 20:08:24  dasenbro
		Added Change History.
		

	Projector History:

		<12+>	10/13/98	ANC		Made changes to work on Rhapsody
		<12>	 9/28/98	MED		Added MemiStr() to do searches in blocks of memory.
		<11>	 9/22/98	MED		Fixed TimeFromInetTime().
		<10>	 9/21/98	MED		Added TimeFromInetTime().
		 <9>	 9/14/98	MED		Added StripQuotes().
		 <8>	 8/21/98	DOR		Fix the double-zero printing bug
		 <7>	 5/19/98	DOR		Fix a time calculation bug...image that...in the future if you
									can...
		 <6>	  5/5/98	DOR		Change the CalcElapsed time routines so that they don't throw.
		 <5>	  5/4/98	DOR		Fix some string formatting code.
		 <4>	  4/8/98	DOR		Add implementation of strncpy.
		 <3>	 3/24/98	MED		Added AppendNumToString to append a number to the end of a
									string.
		 <2>	03/17/98	DOR		Add a utility function to produce a correct format string based
									on elasped time.
		 <1>	  2/3/98	MED		first checked in

	To Do:
		Need locking around "localtime" since it returns static var. If there are multiple copies of
		CUtils may have race condition.
 */


#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <UException.h>

#include "CUtils.h"
#include "CMailDatabase.h"
#include "CRCtable.h"

// Internet standard days of the week and months.
// DO NOT LOCALIZE! These are placed in message headers.
static const char *const statDays[] = {
	"Sun",	"Mon",	"Tue",
	"Wed",	"Thu",	"Fri",
	"Sat"
};

static const char *const statMonths[] = {
	"Jan",	"Feb",	"Mar",
	"Apr",	"May",	"Jun",
	"Jul",	"Aug",	"Sep",
	"Oct",	"Nov",	"Dec"
};

char	CUtils::fTimeBuf[ 34 ];						// output time variable

// ---------------------------------------------------------------------------
//	* TimeFromInetTime
//
// ---------------------------------------------------------------------------

void CUtils::TimeFromInetTime ( char *inDateString, uInt32 *outTime )
{
	sInt32		result	= kNoErr;
	char	   *p		= inDateString;
	char		month[4];
	tm			date;

	Try_
	{
		*outTime = 0;
		::memset( &date, 0, sizeof( tm ) );

		// *ĘSkip the day of the week

		// Must be 3 consecative characters
		ThrowIfNot_( ::isalpha( *p ) );
		p++;

		ThrowIfNot_( ::isalpha( *p ) );
		p++;

		ThrowIfNot_( ::isalpha( *p ) );
		p++;

		// must be a ','
		ThrowIf_( *p != ',' );
		p++;

		// This had better be a space
		ThrowIf_( *p != ' ' );
		p++;

		// *ĘGet the day of month

		// This had better be a number
		ThrowIfNot_( ::isdigit( *p ) );

		date.tm_mday = *p - '0';
		p++;

		// Check for double digit date
		if ( ::isdigit( *p ) )
		{
			date.tm_mday = date.tm_mday * 10 + *p - '0';
			p++;
		}

		// Must be a ' ' separator
		ThrowIf_( *p != ' ' );
		p++;

		// * Get the month

		// Must be 3 consecative characters
		ThrowIfNot_( ::isalpha( *p ) );
		month[0] = *p;
		p++;

		ThrowIfNot_( ::isalpha( *p ) );
		month[1] = *p;
		p++;

		ThrowIfNot_( ::isalpha( *p ) );
		month[2] = *p;
		p++;

		month[ 3 ] = '\0';

		for ( date.tm_mon = 0; date.tm_mon < 12; date.tm_mon++ )
		{
			if ( CUtils::Stricmp( month, statMonths[ date.tm_mon ] ) == 0 )
			{
				break;
			}
		}

		ThrowIf_( date.tm_mon >= 12 );

		// Must be a ' ' separator
		ThrowIf_( *p != ' ' );
		p++;


		// *ĘGet the year
		ThrowIfNot_( ::isdigit( *p ) );

		date.tm_year = *p - '0';
		p++;

		ThrowIfNot_( ::isdigit( *p ) );

		date.tm_year = date.tm_year * 10 + *p - '0';
		p++;

		// Check for 4 vs 2 digit year (gotta love 822, NOT!)
		if ( ::isdigit( *p ) )
		{
			ThrowIf_( date.tm_year < 19 );

			date.tm_year -= 19;
			date.tm_year = date.tm_year * 10 + *p - '0';
			p++;

			ThrowIfNot_( ::isdigit( *p ) );

			date.tm_year = date.tm_year * 10 + *p - '0';
			p++;
		}

		date.tm_isdst = -1;

		// Set the beginning of the day
		*outTime = ::mktime( &date );
	}

	Catch_( err )
	{
	}

} // TimeFromInetTime


// ---------------------------------------------------------------------------
//	* InetTime ()
//
//		- return a time string formatted to RFC 822 specs
//
// ---------------------------------------------------------------------------

char* CUtils::InetTime ( Boolean inTwoDigitYear )
{
	time_t t = ::time( nil );
	struct tm *tms_static;
	struct tm tmStruct;
	struct tm *tms = &tmStruct;

	tms_static = ::localtime( &t );
	// since localtime return a static var, make a copy of it and use the copy
	::memcpy( tms, tms_static, sizeof( struct tm ) );
	long gmtDelta = tms->tm_gmtoff;		// the GMT offset in seconds

	// West or East of GMT?
	char zdir = '+';

	if ( gmtDelta < 0 )
	{
		zdir = '-';
		gmtDelta = -gmtDelta;
	}

	// Hours
	int gmtDeltaH = gmtDelta / 3600;
	gmtDelta -= ( gmtDeltaH * 3600 );

	// minutes
	int gmtDeltaM = gmtDelta / 60;

	// Think about the year 2000 and beyond...
	int year = tms->tm_year;

	if ( inTwoDigitYear == true )
	{
		// two digit year
		if ( year > 100 )
		{
			year -= 100;
		}

		::sprintf( fTimeBuf, "%s, %02d %s %02d %02d:%02d:%02d %c%02d%02d",
					statDays[tms->tm_wday],
					tms->tm_mday, statMonths[tms->tm_mon], year,
					tms->tm_hour, tms->tm_min, tms->tm_sec,
					zdir, gmtDeltaH, gmtDeltaM );
	}
	else
	{
		// four digit year
		if ( year < 1900 )
		{
			year += 1900;
		}

		::sprintf( fTimeBuf, "%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d",
					statDays[tms->tm_wday],
					tms->tm_mday, statMonths[tms->tm_mon], year,
					tms->tm_hour, tms->tm_min, tms->tm_sec,
					zdir, gmtDeltaH, gmtDeltaM );

	}

	return( fTimeBuf );
}

void CUtils::FormatDayHourMinSec ( const uInt32 inSeconds, CString	&theOutputString )
{
	uInt32		aTime		= inSeconds;
	uInt32		timeDays	= 0;
	uInt32		timeHours	= 0;
	uInt32		timeMin		= 0;
	uInt32		timeSec		= 0;
	char		timeSepChar	= ':';
	CString		tempBuffer(64);

	timeDays = aTime / (60 * 60 * 24);
	aTime = aTime - (aTime / (60 * 60 * 24));

	timeHours = aTime / (60 * 60);
	aTime = aTime - (timeHours * (60 * 60));

	timeMin = aTime / 60;
	aTime = aTime - (timeMin * 60);

	timeSec = aTime;

	::sprintf(tempBuffer.GetData(), "%ld %02ld%c%02ld%c%02ld", timeDays, timeHours, timeSepChar, timeMin, timeSepChar, timeSec);
	theOutputString.Set(tempBuffer.GetData());
}


void CUtils::FormatHourMinSec ( const uInt32 inSeconds, CString	&theOutputString )
{
	uInt32	aTime = inSeconds;
	uInt32	timeHours = 0;
	uInt32	timeMin = 0;
	uInt32	timeSec = 0;
	char	timeSepChar = ':';
	CString	tempBuffer(64);

	timeHours = aTime / (60*60);
	aTime = aTime - (timeHours * (60*60));
	timeMin = aTime / 60;
	timeSec = aTime - (timeMin * 60);

	if (timeHours < 10)
	{
		::sprintf(tempBuffer.GetData(), "%02ld%c%02ld%c%02ld", timeHours, timeSepChar, timeMin, timeSepChar, timeSec);
	}
	else
	{
		::sprintf(tempBuffer.GetData(), "%ld%c%02ld%c%02ld", timeHours, timeSepChar, timeMin, timeSepChar, timeSec);
	}

	theOutputString.Set(tempBuffer.GetData());
}

void CUtils::FormatMinSec ( const uInt32 inSeconds, CString	&theOutputString )
{
	uInt32	aTime = inSeconds;
	uInt32	timeMin = 0;
	uInt32	timeSec = 0;
	char	timeSepChar = ':';
	CString	tempBuffer(64);

	timeMin = aTime / 60;
	timeSec = aTime - (timeMin * 60);

	if (timeMin < 10)
	{
		::sprintf(tempBuffer.GetData(), "%02ld%c%02ld", timeMin, timeSepChar, timeSec);
	}
	else
	{
		::sprintf(tempBuffer.GetData(), "%ld%c%02ld", timeMin, timeSepChar, timeSec);
	}

	theOutputString.Set(tempBuffer.GetData());
}

void CUtils::FormatElapsedTimeFromSecs ( const uInt32 inSeconds, CString &theOutputString )
{
	uInt32	aTime		= inSeconds;
	uInt32	timeDays	= 0;
	uInt32	timeHours	= 0;

	timeDays = aTime / (60*60*24);
	if (timeDays > 0)
	{
		CUtils::FormatDayHourMinSec(inSeconds, theOutputString );
		return;
	}

	aTime = aTime - (timeDays * (60*60*24));
	timeHours = aTime / (60*60);
	if (timeHours > 0)
	{
		CUtils::FormatHourMinSec(inSeconds, theOutputString );
		return;
	}

	CUtils::FormatMinSec(inSeconds, theOutputString );
}


// ---------------------------------------------------------------------------
//	* Strlen ()
//
// ---------------------------------------------------------------------------

int CUtils::Strlen ( const char *inStr )
{
	int result = 0;

	if ( inStr != nil )
	{
        result = ::strlen( inStr );
	}

	return( result );

} // Strlen


// ---------------------------------------------------------------------------
//	* Strcpy ()
//
// ---------------------------------------------------------------------------

char * CUtils::Strcpy ( char *inDestStr, const char *inSrcStr )
{
	char	*pResult	= inDestStr;

	if ( (inDestStr != nil) && (inSrcStr != nil) )
	{
        pResult = ::strcpy( inDestStr, inSrcStr );
	}

	return( pResult );

} // Strcpy


// ---------------------------------------------------------------------------
//	* Strncpy ()
//
// ---------------------------------------------------------------------------

void CUtils::Strncpy ( char *inDestStr, const char *inSrcStr, const uInt32 inMaxLength )
{
	if ( (inDestStr != NULL) && (inSrcStr != NULL) && (inMaxLength > 0) )
	{
		register uInt32 sourceLength = CUtils::Strlen( inSrcStr );

		if ( sourceLength > inMaxLength )
		{
			sourceLength = inMaxLength;
			::strncpy( inDestStr, inSrcStr, sourceLength );
			inDestStr[ inMaxLength ] = 0x00;
		}
		else
		{
			::strcpy( inDestStr, inSrcStr );
		}
	}
} // Strncpy


// ---------------------------------------------------------------------------
//	* Strstr ()
//
// ---------------------------------------------------------------------------

char * CUtils::Strstr ( const char *inStr1, const char *inStr2 )
{
	char	*result = nil;

	if ( (inStr1 != nil) && (inStr2 != nil) )
	{
		result = ::strstr( inStr1, inStr2 );
	}

	return( result );

} // Strstr


// ---------------------------------------------------------------------------
//	* Stristr ()
//
//		- Case insensitive C-string substring hunt
//
// ---------------------------------------------------------------------------

char* CUtils::Stristr ( const char *inString, const char *inSubStr )
{
	const	char   *ep			= nil;
	const	char   *p			= nil;
			bool	bChecking	= false;

	if ( (inString != nil) && (inSubStr != nil) )
	{
		ep = inSubStr;
		p = inString;

		while ( *p )
		{
			char c1 = ( ::isupper(*p) ) ? ::tolower(*p) : *p;
			char c2 = ( ::isupper(*ep) ) ? ::tolower(*ep) : *ep;
			p++;
	
			if ( c1 == c2 )
			{
				bChecking = true;
				ep++;
				if ( !*ep )
				{
					return( (char *) p - CUtils::Strlen( inSubStr ) );
				}
			}
			else if ( bChecking )
			{
				bChecking = false;
				ep = inSubStr;
			}
		}
	}

	return( nil );

} // Stristr


// ---------------------------------------------------------------------------
//	* Strcmp ()
//
//		- Case sensitive NULL safe C-string comparison...
//
// ---------------------------------------------------------------------------

int CUtils::Strcmp ( const char* str1, const char* str2 )
{
	if ( (str1 == NULL) && (str2 == NULL) )
	{
		return( 0 );
	}

	if ( (str1 == NULL) && (str2 != NULL) )
	{
		return( -1 );
	}

	if ( (str1 != NULL) && (str2 == NULL) )
	{
		return( 1 );
	}

	if ( !(*str1) && !(*str2) )
	{
		return( 0 );
	}

	if ( !(*str1) )
	{
		return( -1 );
	}

	if ( !(*str2) )
	{
		return( 1 );
	}

	return( ::strcmp( str1, str2 ) );

} // Strcmp


// ---------------------------------------------------------------------------
//	* Stricmp ()
//
//		- Case insensitive C-string comparison...
//
// ---------------------------------------------------------------------------

int CUtils::Stricmp ( const char* str1, const char* str2 )
{
	if ( (str1 == NULL) && (str2 == NULL) )
	{
		return( 0 );
	}

	if ( (str1 == NULL) && (str2 != NULL) )
	{
		return( -1 );
	}

	if ( (str1 != NULL) && (str2 == NULL) )
	{
		return( 1 );
	}

	if ( !(*str1) && !(*str2) )
	{
		return( 0 );
	}

	if ( !(*str1) )
	{
		return( -1 );
	}

	if ( !(*str2) )
	{
		return( 1 );
	}

	const char *p1 = str1;
	const char *p2 = str2;

	for ( ; (*p1 && *p2); p1++, p2++ )
	{
		char c1 = ( ::isupper(*p1) ) ? ::tolower(*p1) : *p1;
		char c2 = ( ::isupper(*p2) ) ? ::tolower(*p2) : *p2;
		int diff = c1 - c2;

		if ( diff )
		{
			return( diff );
		}
	}

	// anything left?
	if ( *p1 )
	{
		return( 1 );
	}

	if ( *p2 )
	{
		return( -1 );
	}

	// nope!
	return( 0 );
}

// ---------------------------------------------------------------------------
//	* Strincmp
//
//		- Case insensitive C-string comparison.  Compare the first "n"
//			characters of the two strings.
//
// ---------------------------------------------------------------------------

int CUtils::Strincmp ( const char *inStr1, const char *inStr2, size_t inLen )
{
	sInt16	i = 0;

	if ( !(*inStr1) && !(*inStr2) )
	{
		return( 0 );
	}

	if ( !(*inStr1) )
	{
		return -1;
	}

	if ( !(*inStr2) )
	{
		return 1;
	}

	const char *p1 = inStr1;
	const char *p2 = inStr2;

	for ( i = 0; (*p1 && *p2 && i < inLen); p1++, p2++, i++ )
	{
		char c1 = ( ::isupper(*p1) ) ? ::tolower(*p1) : *p1;
		char c2 = ( ::isupper(*p2) ) ? ::tolower(*p2) : *p2;
		int diff = c1 - c2;

		if ( diff )
		{
			return( diff );
		}
	}

	if ( i < inLen )
	{
		return( -1 );
	}

	return( 0 );

} // Strincmp


// ---------------------------------------------------------------------------
//	* MemiStr
//
// ---------------------------------------------------------------------------

char* CUtils::MemiStr ( const char* inBuff, const char* inSubStr, uInt32 inBuffLen )
{
	bool		checking	= false;
	bool		done		= false;
	const char *p			= inBuff;
	const char *s			= inSubStr;
	uInt32		i			= 0;

	while ( (!done) && (i < inBuffLen) )
	{
		char c1 = ( ::isupper(*p) ) ? ::tolower( *p ) : *p;
		char c2 = ( ::isupper(*s) ) ? ::tolower( *s ) : *s;
		p++;
		i++;

		if ( c1 == c2 )
		{
			checking = true;
			s++;
			if ( !*s )
			{
				return( (char *)p - CUtils::Strlen(inSubStr) );
			}
		}
		else if ( checking == true )
		{
			checking = false;
			s = inSubStr;
		}
	}

	return( nil );

} // MemiStr



// ---------------------------------------------------------------------------
//	* AppendNumToString
//
// ---------------------------------------------------------------------------

void CUtils::AppendNumToString ( CString &inString, uInt32 inNum )
{
	char		numStr[16];
//	Str32		string;
	char	   *str		= inString.GetData();
	sInt16		len1	= CUtils::Strlen( str );
	sInt16		len2	= 0;

	::sprintf( numStr, "%ld", inNum );

	if ( (len1 > len2) && (::strncmp( str + (len1 - len2), numStr, len2 ) != 0) )
	{
		inString.Append( " " );
		inString.Append( numStr );
	}

} // AppendNumToString



//--------------------------------------------------------------------------------------------------
//	* StripQuotes
//
//		- Strip the quotes, if any, for the incomming string
//
//--------------------------------------------------------------------------------------------------

void CUtils::StripQuotes ( CString &inString )
{
	sInt16		dataLen = inString.GetLength();
	char	   *tmpPtr = inString.GetData();
	CString		outStr( dataLen );

	// Only strip if the string is quoted at both ends
	if ( (*tmpPtr == '"') && (tmpPtr[dataLen - 1] == '"') )
	{
		// Init a character counter less the 2 bytes for the quotes
		sInt16	cntr = dataLen - 2;

		// Skip past the first quote
		*tmpPtr++;
		while ( cntr-- > 0 )
		{
			outStr.Append( tmpPtr, 1 );
			*tmpPtr++;
		}
		inString.Set( outStr.GetData() );
	}
} // StripQuotes



//--------------------------------------------------------------------------------------------------
//	* ProcessString
//
//--------------------------------------------------------------------------------------------------

void CUtils::ProcessString ( 	CString &inString,
								uInt32	&outCharactersScanned )
{
	char	*tmpPtr = inString.GetData();
	char	termChar = ' ';
	CString	outStr(inString.GetLength());

	outCharactersScanned = 0;

	if (tmpPtr == NULL)
	{
		return;
	}

	// 1st skip over any leading white space...
	while ((*tmpPtr != 0x00) && (*tmpPtr == ' '))
	{
		tmpPtr++;
	}

	// now let's see if we have a leading quote character, if we do
	// then our termination is a matching '"' character, otherwise our
	// termination sequence is a "space" character.
	if (*tmpPtr == '"')
	{
		tmpPtr++;
		termChar = '"';
	}
	else
	{
		termChar = ' ';
	}

	outStr.Clear();
	while ((*tmpPtr != 0x00) && (*tmpPtr != termChar))
	{
		if (*tmpPtr == '\\')
		{
			tmpPtr++;	// skip the backslash character
		}

		outStr.Append(tmpPtr, 1);
		tmpPtr++;
	}

	// if the last character we're viewing is the termination character, make sure
	// we skip over it so that it is "consumed" for the skipped character count...
	if (*tmpPtr == termChar)
	{
		tmpPtr++;
	}

	outCharactersScanned = tmpPtr - inString.GetData();
	inString.Set(outStr.GetData());
} // ProcessString

#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])

static char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????";

static char index_64[ 128 ] =
{
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
};

//--------------------------------------------------------------------------------------------------
//	* EncodeBase64
//
//--------------------------------------------------------------------------------------------------

sInt32 CUtils::EncodeBase64 ( CString &inString, CString &outString )
{
	sInt32			siResult	= kNoErr;
	const uInt8	   *pStr		= nil;
	uInt32			uiInLen		= 0;
	uInt8			ucVal		= 0;

	if ( inString.GetDataLength() > 0 )
	{
		pStr = (const uInt8 *)inString.GetData();
		uiInLen = inString.GetDataLength();

		outString.Clear();

		while ( uiInLen >= 3 )
		{
			outString.Append( basis_64[ pStr[ 0 ] >> 2 ] );
			outString.Append( basis_64[ ((pStr[ 0 ] << 4) & 0x30) | (pStr[ 1 ] >> 4) ] );
			outString.Append( basis_64[ ((pStr[ 1 ] << 2) & 0x3c) | (pStr[ 2 ] >> 6) ] );
			outString.Append( basis_64[ pStr[ 2 ] & 0x3f ] );

			pStr += 3;
			uiInLen -= 3;
		}

		if ( uiInLen > 0 )
		{
			outString.Append( basis_64[ pStr[0] >> 2] );
			ucVal = (pStr[0] << 4) & 0x30;
			if ( uiInLen > 1 )
			{
				ucVal |= pStr[1] >> 4;
			}
			outString.Append( basis_64[ ucVal ] );
			outString.Append( (uiInLen < 2) ? '=' : basis_64[ (pStr[ 1 ] << 2) & 0x3c ] );
			outString.Append( '=' );
		}

	}
	else
	{
		siResult = -1;
	}

	return( siResult );

} // EncodeBase64


//--------------------------------------------------------------------------------------------------
//	* DecodeBase64 ()
//
//--------------------------------------------------------------------------------------------------

sInt32 CUtils::DecodeBase64 ( CString &inString, CString &outString )
{
	sInt32		siResult	= kNoErr;
	uInt32		i			= 0;
	uInt32		uiInLen		= 0;
	uInt32		uiLen		= 0;
	uInt32		c1			= 0;
	uInt32		c2			= 0;
	uInt32		c3			= 0;
	uInt32		c4			= 0;
	const char *pStr		= nil;

	uiInLen = inString.GetDataLength();
	if ( uiInLen > 0 )
	{
		pStr = inString.GetData();

		// Skip past the '+ '
		if ( (pStr[ 0 ] == '+') && (pStr[ 1 ] == ' ') )
		{
			pStr += 2;
		}
		if ( *pStr == '\r')
		{
			siResult = -1;
		}
		else
		{
			outString.Clear();

			for ( i = 0; i < uiInLen / 4; i++ )
			{
				c1 = pStr[ 0 ];
				if ( CHAR64( c1 ) == -1 )
				{
					siResult = -1;
					break;
				}

				c2 = pStr[ 1 ];
				if ( CHAR64( c2 ) == -1 )
				{
					siResult = -1;
					break;
				}

				c3 = pStr[ 2 ];
				if ( (c3 != '=') && (CHAR64( c3 ) == -1) )
				{
					siResult = -1;
					break;
				}

				c4 = pStr[ 3 ];
				if (c4 != '=' && CHAR64( c4 ) == -1)
				{
					siResult = -1;
					break;
				}

				pStr += 4;

				outString.Append( (CHAR64(c1) << 2) | (CHAR64(c2) >> 4) );
				++uiLen;
				if ( c3 != '=' )
				{
					outString.Append( ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2) );
					++uiLen;
					if ( c4 != '=' )
					{
						outString.Append( ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4) );
						++uiLen;
					}
				}
			}
		}
	}
	else
	{
		siResult = -1;
	}

	return( siResult );

} // DecodeBase64

// ---------------------------------------------------------------------------
//	* CalcCRC ()
//
// ---------------------------------------------------------------------------

uInt32 CUtils::CalcCRC ( const char *inStr )
{
	const char 	   *p			= inStr;
	sInt32			siI			= 0;
	sInt32			siStrLen	= 0;
	uInt32			uiCRC		= 0xFFFFFFFF;

	if ( inStr != nil )
	{
		siStrLen = ::strlen( inStr );

		for ( siI = 0; siI < siStrLen; ++siI )
		{
			uiCRC = UPDC32( *p, uiCRC );
			p++;
		}
	}

	return( uiCRC );

} // CalcCRC

// End --- CUtils.cpp ---
