/* Copyright (c) 2017, Computer History Museum 
All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to 
the limitations in the disclaimer below) provided that the following conditions are met: 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
   disclaimer in the documentation and/or other materials provided with the distribution. 
 * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products 
   derived from this software without specific prior written permission. 
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE 
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE. */

#define FILE_NUM 131
/* Copyright (c) 1999 by QUALCOMM Incorporated */

#include "regcode_v2.h"
#include "regcode_charsets.h"

//	Prototypes
void MakeJumpPath(StringPtr s,short hostResID);
Handle GenerateAdwareURLStr (UserStateType state, StringPtr host, short action, UInt32 query, long refcon);

extern ModalFilterUPP DlgFilterUPP;

//
//	OpenAdwareURL
//
//	
//		Host is resource
void OpenAdwareURL (UserStateType state, short hostResID, short action, UInt32 query, long refcon)

{
	Str255	host;
	MakeJumpPath(host,hostResID);	
	OpenAdwareURLStr (state,host,action,query,refcon);
}

//		Host is string
OSErr OpenAdwareURLStr (UserStateType state, StringPtr host, short action, UInt32 query, long refcon)

{
	Handle	url;
	Str255	protocol;
	short		length;
	OSErr	err;

	if (url = GenerateAdwareURLStr (state, host, action, query, refcon)) {
		length = GetHandleSize (url);
		if (length && (*url)[length]==0) length--;
		if (!ParseProtocolFromURLPtr (LDRef (url), length, protocol))
			err = OpenOtherURLPtr (protocol, *url, length);
		ZapHandle (url);
	}
	return err;
}

//
//	GenerateAdwareURL
//
//		'state'			-	The state to be used when generating the URL (used by registration)
//		'hostResID'	- The host stuff... www.eudora.com
//		'action'		- The action to be taken.  Pass in one of the constants from URLAction in StrnDefs
//		'query'			- A bit mask identifying the fields to be included in the query.  Use the 'urlQueryParts' constants in adutil.h
//		'refcon'		- Paramater for special use of certain actions and query types.  This mechanism is not entirely
//									swift since multiple query parts _could_ each require different refcons.  This would break.  Badly.
//									Don't do this.
//
//		Host is resource
Handle GenerateAdwareURL (UserStateType state, short hostResID, short action, UInt32 query, long refcon)

{
	Str255	host;	
	MakeJumpPath(host,hostResID);
	return GenerateAdwareURLStr (state,host,action,query,refcon);
}

//		Host is string
Handle GenerateAdwareURLStr (UserStateType state, StringPtr host, short action, UInt32 query, long refcon)

{
	Accumulator			urlAccumulator;
	AdURLStringsPtr	urlStringPtrs;
	Str255					attribute,
									value;
	OSErr						theError;

	// Initialize the accumulator into which we'll build the URL
	theError = AccuInit (&urlAccumulator);
	
	// Add the URL path, normally "http://www.eudora.com/jump.cgi"
	if (!theError)
		theError = AccuAddStr(&urlAccumulator, host);
	
	// Gimme a '?'
	if (!theError)
		theError = AccuAddChar (&urlAccumulator, '?');
	
	// The query is built from the bit mask passed in 'query'.  Each bit represents a attribute/value
	// pair that will be included in the query portion of the URL.  See 'urlQueryParts' in adutil.h
	// for those parts that have been defined.  See also 'URLQueryPartStrn' for the
	// attribute names
	
	if (!theError && (query & actionQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryAction), GetRString (value, URLActionStrn + action));
	
	if (!theError && (query & realNameQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRealname), GetDominantPref (PREF_REALNAME, value));

#ifdef I_HATE_THE_BOX	
	if (!theError && (query & regFirstQueryPart) && !BoxUser (state))
#else
	if (!theError && (query & regFirstQueryPart))
#endif
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegFirst), GetSomeHunkOfReg (state, value, action, GetRegFirst));
	
#ifdef I_HATE_THE_BOX	
	if (!theError && (query & regLastQueryPart) && !BoxUser (state))
#else
	if (!theError && (query & regLastQueryPart))
#endif
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegLast), GetSomeHunkOfReg (state, value, action, GetRegLast));
	
#ifdef I_HATE_THE_BOX	
	if (!theError && (query & regCodeQueryPart) && BoxUser (state) && action == actionRegister50box)
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegCode), GetDespisedBoxRegCode (state, value));
	else
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegCode), GetSomeHunkOfReg (state, value, action, GetRegCode));
#else
	if (!theError && (query & regCodeQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegCode), GetSomeHunkOfReg (state, value, action, GetRegCode));
#endif
	
	if (!theError && (query & regLevelQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryRegLevel), GetSomeHunkOfReg (state, value, action, GetRegLevel));
	
	if (!theError && (query & oldRegQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryOldReg), GetOldRegCode (state, value));
	
	if (!theError && (query & emailQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryEmail), GetShortReturnAddr (value));
	
	if (!theError && (query & profileQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryProfile), GetProfileID (value));
	
	if (!theError && (query & destinationQueryPart))
		if (urlStringPtrs = (AdURLStringsPtr) refcon)
			if (urlStringPtrs->destination)
				theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryDestination), urlStringPtrs->destination);
	
	if (!theError && (query & adIDQueryPart))
		if (urlStringPtrs = (AdURLStringsPtr) refcon)
			if (urlStringPtrs->adID)
				theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryAdID), urlStringPtrs->adID);
	
	if (!theError && (query & platformQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryPlatform), GetRString (value, REG_PLATFORM));
	
	if (!theError && (query & productQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryProduct), GetRString (value, REG_PRODUCT));
	
	if (!theError && (query & versionQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryVersion), GetVersionString (value));
	
	if (!theError && (query & distributorIDQueryPart))
			theError = AccuAttributeValuePairHandle (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryDistributorID), GetDistributorID ());
	
	if (!theError && (query & modeQueryPart))
			theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryMode), GetModeString (value));

	if (!theError && (query & topicQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryTopic), GetRString (value, URLSupportTopicStrn + refcon));

	if (!theError && (query & langQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryLang), GetRString (value, LOCALIZED_VERSION_LANG));

	if (!theError && (query & queryQueryPart))
		theError = AccuAttributeValuePair (&urlAccumulator, GetRString (attribute, URLQueryPartStrn + queryQuery), (PStr)refcon);

	if (!theError) {
		// If any query part was present, strip the '& off the end of the accumulator
		if (query & fullQuery)
			AccuStrip (&urlAccumulator, 1);
		// Tack a null at the end of the handle since DownloadURL expects a C string
		theError = AccuAddChar (&urlAccumulator, 0);
	}
	if (!theError)
		AccuTrim (&urlAccumulator);
	else
		AccuZap (urlAccumulator);
	return (urlAccumulator.data);
}

//	MakeJumpPath - make path for jump URL
void MakeJumpPath(StringPtr s,short hostResID)
{
	// Get host
	GetRString(s,hostResID);

	// Slide in a slash '/'
	PCatC(s,'/');

	// Tack on the command portion  "jump.cgi"
	PCatR(s,URL_JUMP_COMMAND);
}


OSErr AccuAttributeValuePair (AccuPtr a, PStr attribute, PStr value)

{
	OSErr	theError;
	Str255 localValue;
	
	PCopy(localValue,value);
	TransLitRes(localValue+1,*localValue,ktFlatten);
	TransLitRes(localValue+1,*localValue,ktMacISO);
	URLQueryEscape(localValue);
	
	theError = noErr;
	if (attribute[0] && value[0]) {
		theError = AccuAddStr (a, attribute);
		if (!theError)
			theError = AccuAddChar (a, '=');
		if (!theError && value[0])
			theError = AccuAddStr (a, localValue);
		if (!theError)
			theError = AccuAddChar (a, '&');
	}
	return (theError);
}


OSErr AccuAttributeValuePairHandle (AccuPtr a, PStr attribute, Handle value)

{
	Str255	scratch;
	OSErr		theError;
	UPtr		from,
					end;
	
	theError = noErr;
	if (attribute[0] && value) {
		theError = AccuAddStr (a, attribute);
		if (!theError)
			theError = AccuAddChar (a, '=');
		if (!theError && value) {
// Old unescaped way...
//			theError = AccuAddHandle (a, value);
			end = *value + GetHandleSize (value);
			for (from = *value; !theError && from < end; from++)
			switch (*from) {
				case '%':
				case '&':
				case '?':
				case ':':
				case ' ':
					theError = AccuAddChar (a, '%');
					if (!theError)
						theError = AccuAddPtr (a, Bytes2Hex (from, 1, scratch), 1);
					break;
				default:
					if (*from & 0x80)
					{
						theError = AccuAddChar (a, '%');
						if (!theError)
							theError = AccuAddPtr (a, Bytes2Hex (from, 1, scratch), 1);
					}
					else
						theError = AccuAddChar (a, *from);
					break;
			}
		}
		if (!theError)
			theError = AccuAddChar (a, '&');
	}
	return (theError);
}


//
//	ChooseInitialUserState
//
//		Choose the initial state for a user very, very carefully...  We need to be
//		able to recognize the difference between a new user, an old user and a box
//		user.
//

UserStateType ChooseInitialUserState (UserStateType state, Boolean foundRegFile, Boolean needsRegistration, int pnPolicyCode, InitNagResultType *result)

{
	int			regMonth;
	Boolean	regValid;
	
	*result	 = noDialogPending;
	
	// If we've found a more recent RegCode file, set the state to that indicated by the policy code
	if (foundRegFile)
		state = PolicyCodeToRegisteredNagState (pnPolicyCode);

	// Don't trust anything or anyone... Now that we think we have a state, make darn sure
	if (!ValidUser (state)) {
		ComposeStdAlert (Note, STATE_INVALID_AT_STARTUP);
		*result = paymentRegPending;
		state = adwareUser;
	}

	// Do we suddenly have an invalid registration code?
	regValid = ValidRegCode (state, &pnPolicyCode, &regMonth);
	if (RegisteredUser (state) && !regValid) {
		if (PayUser (state) && foundRegFile) {
			ComposeStdAlert (Note, REG_INVALID_AT_STARTUP);
			*result = codeEntryPending;
		}
		state = PolicyCodeToUnregisteredNagState (pnPolicyCode);
	}

#ifdef I_HATE_THE_BOX
	// If the user has a valid regcode and is in Paid mode -- check the policy code
	// to verify that this is actually a regcode that applies to a box build
	if (regValid && PayUser (state) && !PolicyCheck (pnPolicyCode, regMonth))
		state = boxUser;
#else
	// If the user had previously been changed to a repay user, we make them sponsored here because they have relaunched
	if (RepayUser (state)) {
		*result = gracelessRepayPending;
		return (state = RegisteredUser (paidUser) ? regAdwareUser : adwareUser);
	}
	
	// If the registration code is valid, but the user is not entitled to this update, give them the Repay dialog
	if (IsPayUser () && *result == noDialogPending && regValid && !PolicyCheck (pnPolicyCode, regMonth))
		*result = repayPending;
#endif

	return (state);
}

Boolean PolicyCheck (int pnPolicyCode, int regMonth)

{
	Boolean	entitledToFreeUpgrade;
	
	entitledToFreeUpgrade = false;
	switch (pnPolicyCode) {
		case REG_EUD_AD_WARE:
			break;
		case REG_EUD_LIGHT:
			break;
		case REG_EUD_PAID:
#ifndef I_HATE_THE_BOX
			// Never honor REG_EUD_PAID when using the box.  The user is expected to
			// re-register and recieve back a policy code of REG_EUD_50_PAID_BOX_ESD (or higher)
			// when using the 5.0 box.
			entitledToFreeUpgrade = (regMonth < REG_EUD_CLIENT_34_DEFUNCT_MONTH) &&
															(regMonth + 12 >= CLIENT_BUILD_MONTH);
#endif
			break;
		case REG_EUD_50_PAID_TRIMODE:
		case REG_EUD_50_PAID_BOX_ESD:
		case REG_EUD_50_PAID_37_RSRV:
		case REG_EUD_50_PAID_38_RSRV:
		case REG_EUD_50_PAID_39_RSRV:
		case REG_EUD_50_PAID_40_RSRV:
		case REG_EUD_50_PAID_EN_ONLY:
		case REG_EUD_50_PAID_EN_NOT_X1:
#ifdef DONT_CHECK_REGMONTH
			return true;
#endif
			entitledToFreeUpgrade = (regMonth + 12 >= CLIENT_BUILD_MONTH);
			break;
	}

	return (entitledToFreeUpgrade);
}

//
//	CheckForRegCodeFile
//
//		Checks to see if there is a RegCode file (with valid data, hopefully), returning
//		true or false.  We pass in the date of the last known use of the RegCode file and
//		reparse the file if the current RegCode file is newer than the old one.
//

Boolean CheckForRegCodeFile (Boolean *needsRegistration, uLong *regDate, int *pnPolicyCode)

{
	FSSpec	spec;
	Str31		folderName;
	uLong		curRegDate;

	*pnPolicyCode = regInvalidPolicyCode;	
	if (!GetFileByRef (AppResFile, &spec))
		if (!FSMakeFSSpec (spec.vRefNum, spec.parID, GetRString (folderName, STUFF_FOLDER), &spec)) {
			IsAlias (&spec, &spec);
			spec.parID = SpecDirId (&spec);
			GetRString (spec.name, REG_CODE_FILE);
			if ((curRegDate = FSpModDate (&spec)) > *regDate)
				if (!ParseRegFile (&spec, parseSilent, needsRegistration, pnPolicyCode)) {
					*regDate = curRegDate;
					return (true);
				}
		}
	return (false);
}


UserStateType PolicyCodeToRegisteredNagState (int policyCode)

{
	switch (policyCode) {
		case REG_EUD_PAID:
		case REG_EUD_50_PAID_TRIMODE:
		case REG_EUD_50_PAID_BOX_ESD:
		case REG_EUD_50_PAID_37_RSRV:
		case REG_EUD_50_PAID_38_RSRV:
		case REG_EUD_50_PAID_39_RSRV:
		case REG_EUD_50_PAID_40_RSRV:
		case REG_EUD_50_PAID_EN_ONLY:
		case REG_EUD_50_PAID_EN_NOT_X1:
			return (paidUser);
			break;
		case REG_EUD_LIGHT:
			return (regFreeUser);
			break;
		case REG_EUD_AD_WARE:
			return (regAdwareUser);
			break;
	}
	return (regAdwareUser);
}


UserStateType PolicyCodeToUnregisteredNagState (int policyCode)

{
	switch (policyCode) {
		case REG_EUD_PAID:
		case REG_EUD_50_PAID_TRIMODE:
		case REG_EUD_50_PAID_BOX_ESD:
		case REG_EUD_50_PAID_37_RSRV:
		case REG_EUD_50_PAID_38_RSRV:
		case REG_EUD_50_PAID_39_RSRV:
		case REG_EUD_50_PAID_40_RSRV:
		case REG_EUD_50_PAID_EN_ONLY:
		case REG_EUD_50_PAID_EN_NOT_X1:
			return (adwareUser);
			break;
		case REG_EUD_LIGHT:
			return (freeUser);
			break;
		case REG_EUD_AD_WARE:
			return (adwareUser);
			break;
	}
	return (adwareUser);
}

PStr GetDominantPref (short prefNumber, PStr string)

{
	PushPers (PersList);
	GetPref (string, prefNumber);
	PopPers ();
	return (string);
}


PStr GetSomeHunkOfReg (UserStateType state, PStr string, short action, GetSomeHunkOfRegProcPtr getRegProc)

{
	if (action == actionUpdate || action == actionArchived || action == actionPay) {
		(*getRegProc) (paidUser, string);
		if (!*string && state != paidUser)
			(*getRegProc) (state, string);
	}
	else
		(*getRegProc) (state, string);
	return (string);
}


PStr GetRegFirst (UserStateType state, PStr string)

{
	Handle	regInfo;
	short		resID;
	
	resID = PayMode (state) ? REG_PREF_PAID : (AdwareMode (state) ? REG_PREF_SPONSORED : REG_PREF_LIGHT);
	regInfo = GetResourceFromFile (REG_PREF_TYPE, resID, SettingsRefN);
	if(regInfo == nil || GetHandleSize (regInfo) < 3)
		string[0] = 0;
	else
		CtoPPtrCpy(string,*regInfo);
	if (regInfo)
		ReleaseResource (regInfo);
	return (string);
}



PStr GetRegLast (UserStateType state, PStr string)

{
	Handle	regInfo;
	short		resID;
	
	resID = PayMode (state) ? REG_PREF_PAID : (AdwareMode (state) ? REG_PREF_SPONSORED : REG_PREF_LIGHT);
	regInfo = GetResourceFromFile (REG_PREF_TYPE, resID, SettingsRefN);
	if(regInfo == nil || GetHandleSize (regInfo) < 3)
		string[0] = 0;
	else {
		Ptr c = *regInfo;
		
		c += strlen(c) + 1;
		CtoPPtrCpy(string,c);
	}
	if (regInfo)
		ReleaseResource (regInfo);
	return (string);
}


PStr GetRegCode (UserStateType state, PStr string)

{
	Handle	regInfo;
	short		resID;
	
	resID = PayMode (state) ? REG_PREF_PAID : (AdwareMode (state) ? REG_PREF_SPONSORED : REG_PREF_LIGHT);
	regInfo = GetResourceFromFile (REG_PREF_TYPE, resID, SettingsRefN);
	if(regInfo == nil || GetHandleSize (regInfo) < 3)
		string[0] = 0;
	else {
		Ptr c = *regInfo;
		
		c += strlen(c) + 1;
		c += strlen(c) + 1;
		CtoPPtrCpy(string,c);
	}
	if (regInfo)
		ReleaseResource (regInfo);
	return (string);
}


#ifdef I_HATE_THE_BOX
//
//	GetDespisedBoxRegCode
//
//		Generate a registration code for box users, using:
//
//			PolicyCode = 41
//					 Month = current minute
//					  Name = Ihate Thebox
//
PStr GetDespisedBoxRegCode (UserStateType state, PStr string)

{
	DateTimeRec	dateTime;
	Str255			szName,
							szResult;
	
	GetRString (szName, THE_DAVE_AND_CHUCK_LOVE_CONNECTION);
	szName[++szName[0]] = 0;
	
	SecondsToDate (GMTDateTime(), &dateTime);
	
	*string = 0;
	if (!regcode_v2_encode (&szName[1], dateTime.minute, REG_EUD_50_TEMP_BOX_ESD, Random (), szResult))
		CtoPPtrCpy (string, szResult);
	return (string);
}
#endif


PStr GetRegLevel (UserStateType state, PStr string)
{
	Str255 regName, regCode;
	int policyCode,
			regMonth;
	
	// grab name
	GetRegFirst(state,regName);
	GetRegLast(state,regCode);
	// reformat
	PSCatC(regName,' ');
	PSCat(regName,regCode);
	
	// grab code
	GetRegCode(state,regCode);
	
	*regName = MIN(*regName,254);
	*regCode = MIN(*regCode,254);
	regName[*regName+1] = 0;
	regCode[*regCode+1] = 0;
	
	// Grab month by verifying code
	if (RegCodeVerifies(regCode+1,regName+1,&policyCode,&regMonth))
		NumToString(19*(((((Random()&0xff)<<8)|policyCode)<<8)|regMonth),string);
	else
#ifdef I_HATE_THE_BOX
	{
		DateTimeRec	dateTime;
		SecondsToDate (GMTDateTime(), &dateTime);
		NumToString(19*(((((Random()&0xff)<<8)|REG_EUD_50_TEMP_BOX_ESD)<<8)|dateTime.minute),string);
	}
#else
		*string = 0;
#endif	
	return string;
}



PStr GetOldRegCode (UserStateType state, PStr string)

{
	return (GetPref (string, PREF_SITE));
}


PStr GetVersionString (PStr string)

{
	VersRecHndl	versInfo;

	string[0] = 0;
	if (versInfo = (VersRecHndl) GetResourceFromFile ('vers', 1, AppResFile)) {
		ComposeRString (string, REG_VERSION_FORMAT, (*versInfo)->numericVersion.majorRev, ((*versInfo)->numericVersion.minorAndBugRev & 0xF0) >> 4, (*versInfo)->numericVersion.minorAndBugRev & 0x0F, (*versInfo)->numericVersion.nonRelRev);
		ReleaseResource (versInfo);
	}
	return (string);
}


PStr GetModeString (PStr string)

{
	return (GetRString (string, URLModeStrn + (IsPayMode () ? modePaid : (IsAdwareMode () ? modeAd : modeFree))));
}


Handle GetDistributorID (void)

{
	return (GetTextFromAppFolderFile (STUFF_FOLDER, DIST_ID));
}


PStr GetProfileID (PStr string)

{
	Handle	profileID;
	
	string[0] = 0;
	if (profileID = GetProfileData ()) {
		string[0] = MIN (255, GetHandleSize (profileID));
		BMD (*profileID, &string[1], string[0]);
		ReleaseResource (profileID);
	}
	return (string);
}


Handle GetProfileData (void)
{
	Handle theData = GetResourceFromFile (USER_PROFILE_TYPE, USER_PROFILE_ID, SettingsRefN);
	
	if (!theData)
	{
		TOCType inTOC, outTOC;
		FSSpec spec;
		Str63 s;
		
		// Try to fetch from In/Out toc's
		if (!FSMakeFSSpec(MailRoot.vRef,MailRoot.dirId,GetRString(s,IN),&spec))
		if (!PeekTOC(&spec,&inTOC))
		if (!FSMakeFSSpec(MailRoot.vRef,MailRoot.dirId,GetRString(s,OUT),&spec))
		if (!PeekTOC(&spec,&outTOC))
		if (outTOC.profile[0] || outTOC.profile[1] || inTOC.profile[0] || inTOC.profile[1])
		{
			// Got something.  Format & save
			ComposeString(s,"\p%8x%8x%8x%8x",inTOC.profile[0],inTOC.profile[1],outTOC.profile[0],outTOC.profile[1]);
			SettingsPtr(USER_PROFILE_TYPE, nil, USER_PROFILE_ID, s+1, *s);
			// refetch
			theData = GetResourceFromFile (USER_PROFILE_TYPE, USER_PROFILE_ID, SettingsRefN);
		}
	}
	return theData;
}



OSErr SetProfileID(PStr profile)
{
	TOCHandle inTOCH, outTOCH;
	long profileAsLongs[4];
	OSErr err = noErr;
	
	if (*profile)
		err = SettingsPtr(USER_PROFILE_TYPE, nil, USER_PROFILE_ID, profile+1, *profile);
	else
		ZapResource(USER_PROFILE_TYPE, USER_PROFILE_ID);
	
	ASSERT(*profile==0 || *profile==32);
	if (*profile) Hex2Bytes(profile+1,32,(Ptr)profileAsLongs);
	else WriteZero(profileAsLongs,16);
	
	if (!err)
	{
		err = memFullErr;
		if (inTOCH=GetSpecialTOC(IN))
		if (outTOCH=GetSpecialTOC(OUT))
		{
			(*inTOCH)->profile[0] = profileAsLongs[0];
			(*inTOCH)->profile[1] = profileAsLongs[1];
			(*outTOCH)->profile[0] = profileAsLongs[2];
			(*outTOCH)->profile[1] = profileAsLongs[3];
			(*inTOCH)->reallyDirty = true;
			(*outTOCH)->reallyDirty = true;
			err = noErr;
		}
	}
	return err;
}

Handle GetTextFromEudoraFolderFile (short fileResID)

{
	FSSpec	spec;
	Str255	fileName;
	Handle	text;
	OSErr		theError;

	text = nil;
	theError = FSMakeFSSpec (Root.vRef, Root.dirId, GetRString (fileName, fileResID), &spec);
	if (!theError) {
		IsAlias (&spec, &spec);
		theError = Snarf (&spec, &text, 0);
	}
	if (theError)
		ZapHandle (text);
	return (text);
}


Handle GetTextFromAppFolderFile (short folderResID, short fileResID)

{
	FSSpec	spec;
	Str255	folderName;
	Handle	text;
	OSErr		theError;

	text = nil;
	theError = GetFileByRef (AppResFile, &spec);
	if (!theError)
		theError = FSMakeFSSpec (spec.vRefNum, spec.parID, GetRString (folderName, folderResID), &spec);
	if (!theError) {
		IsAlias (&spec, &spec);
		spec.parID = SpecDirId (&spec);
		GetRString (spec.name, fileResID);
		theError = Snarf (&spec, &text, 0);
	}
	if (theError)
		ZapHandle (text);
	return (text);
}


//
//	GetResourceFromFile
//
//		Get a resource, but make sure we're doing so only from a specifc
//		file.  We do this for the registration resources because we want
//		to make sure that certain information comes from the app and
//		certain information comes from the settings file -- without
//		interference from resource plug-ins
//

Handle GetResourceFromFile (ResType theType, short theID, short refNum)

{
	Handle	resHandle;
	OSErr		theError;
	short		oldResFile;
	
	oldResFile = CurResFile ();
	UseResFile (refNum);
	resHandle = Get1Resource (theType, theID);
	theError = ResError ();
	UseResFile (oldResFile);
	if (theError == resNotFound)	// Uh oh... a resource isn't where it was supposed to be.  :(
		;
	return (resHandle);
}


//
//	ValidRegCode
//

Boolean ValidRegCode (UserStateType state, int *policyCode, int *regMonth)

{
	Str255	regCode,
					regName;
	short		len;
	
	GetRegCode (state, regCode);
	*regCode = MIN(*regCode,254);
	regCode[regCode[0] + 1] = 0;
	
	GetRegFirst (state, regName);
	GetRegLast (state, &regName[regName[0] + 1]);
	len = regName[regName[0] + 1];
	regName[regName[0] + 1] = ' ';
	regName[0] += (len + 1);
	*regName = MIN(*regName,254);
	regName[regName[0] + 1] = 0;
	return RegCodeVerifies (&regCode[1], &regName[1], policyCode, regMonth);
}

Boolean RegCodeVerifies(const char *szCode, const char *szName, int *outProduct, int *outMonth)
{
	int			pnMonth;
	int			pnProduct;
	Boolean		verified = true;
//char		mapName[REG_CODE_MAX_NAME];
// So... University of California, Berkeley is one pretty darn long name...  Reg codes have been generated
// on the server and from the installer without imposing the 20 character limit originally intended for these
// things.  When validating, we're overflowing the buffer and crunching the stack when a really long name
// is input.  Truncation won't help since we'd be validating a different key. 
	Str255	mapName;

	// So, some idiot thinks that the Code Entry dialog is a "security risk" because he's able to type 255 characters
	// into the first name field and crash the reg code verifier.  He further believes that this is a "risk" because
	// Eudora has the capability of automatically processing registration information -- never mind that the only
	// regcodes we'll be automatically processing are those we've generated ourselves...
	//
	// We'll combat such stupidity by not even calling the verifier and making sure that the policy code fails.
	if (strlen (szCode) > REG_CODE_LEN - 1 || strlen (szName) > sizeof (mapName) - 1) {
		if (outProduct)
			*outProduct = regInvalidPolicyCode;
		return (false);
	}
	
	regcode_map_macroman(szName, mapName);
	verified = !regcode_v2_verify(szCode, mapName, &pnMonth, &pnProduct);
	if(outProduct)
	{
		*outProduct = verified ? pnProduct : regInvalidPolicyCode;
	}
	if(outMonth)
	{
		*outMonth = verified ? pnMonth : 0;
	}
	return verified;
}
