//array of random offsets used to encode/decode data
kArraySize=40;
short magicArray[kArraySize];

//constants for encrypting or decrypting
const kDoEncrypt=1;
const kDoDecrypt=-1;

//these are used for the password filter procedure
TEHandle pMirrorEditText;
Str255 password;

//these are used for the progress dialog
long fileSize;
Rect progressRect;
DialogPtr progressBox;

//Updates the progress box on the screen
void UpdateProgress( long i)
{
	Rect z;
	short x=progressRect.left;
	short y=progressRect.top;
	SetRect(&z,x,y,x+(int)((progressRect.right-x)*(i/fileSize)),progressRect.bottom);
	ForeColor(greenColor);
	PaintRect(&z);
	ForeColor(blackColor);
	FrameRect(&z);
}

//sets up the progress window, gets the rectangle, sets static text
void InitProgress(const short mode,const Str255 name)
{
	Handle h;
	short x;
	Rect r;
	Str255 text;
	
	progressBox=GetNewDialog(129,0L,(WindowPtr)-1);
	SetPort(progressBox);
	
	GetDItem(progressBox,1,&x,&h,&r);
	if (mode==kDoEncrypt)
		SetIText(h,"\pEncrypting File:");
	else
		SetIText(h,"\pDecrypting File:");
	GetDItem(progressBox,3,&x,&h,&r);
	SetIText(h,name);
	GetDItem(progressBox,2,&x,&h,&progressRect);
}

//gets the size in bytes of a file
long GetFileSize(const Str255 fName,short vRefNum)
{
	OSErr err;
	short fileRef;
	long size;
	
	err=FSOpen(fName,vRefNum,&fileRef);
	err=GetEOF(fileRef,&size);
	err=FSClose(fileRef);
	return size;
}

//this is the actual encoding/decoding procedure
//the names of the source file and crypt file are determined
//by the CheckEncrypt procedure and passed to this one
//the procedure loops through each character in the file and
//adds to it's ascii value a value in the random array or
//subtracts to decrypt
void CodeIt(const Str255 sourceFilePath, const Str255 cryptFilePath, short mode)
{
	FILE *source, *crypt;
	int c;
	
	source = fopen(PtoCstr(sourceFilePath), "rb" );
	crypt = fopen(PtoCstr(cryptFilePath), "wb" );
	
	long i=0;
	while( (c=fgetc(source)) != EOF)
	{
		short y=c+mode*magicArray[i%kArraySize];
		if (y>255) y-=256;
		if (y<0) y+=256;
		fputc(y,crypt);
		i++;
		UpdateProgress(i);
	}
	
	fclose((char*)source);
	fclose((char*)crypt);
	
	//these lines kept my program from crashing!
	CtoPstr((char*)sourceFilePath);
	CtoPstr((char*)cryptFilePath);
}

//the password filter procedure
//it displays the bullets as you type and still gets
//the password in
pascal Boolean PasswordProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
{
	short result;
	long oldA5;
	Rect dummyRect;
	TEHandle dialogTE;
	char key;
	
	oldA5=SetCurrentA5();
	result=0;
	if (pMirrorEditText==0L)
	{
		SetRect(&dummyRect,0,0,0,0);
		pMirrorEditText=TENew(&dummyRect,&dummyRect);
	}
	if (theEvent->what==keyDown || theEvent->what==autoKey)
	{
		key=(theEvent->message & charCodeMask);
		if (key==13 || key==3)
		{
			if (pMirrorEditText!=0L)
			{
				TEDispose(pMirrorEditText);
				pMirrorEditText=0L;
			}
			*itemHit=1;
			result=1;
		}
		else
		{
			if (key>=' ')
			{
				theEvent->message=(theEvent->message & -charCodeMask);
				theEvent->message=(theEvent->message | '¥');
			}
			dialogTE=(*(DialogPeek)theDialog).textH;
			(**pMirrorEditText).selStart=(**dialogTE).selStart;
			(**pMirrorEditText).selEnd=(**dialogTE).selEnd;
			TEKey(key,pMirrorEditText);
			password[0]=(**pMirrorEditText).teLength;
			BlockMove((*(**pMirrorEditText).hText),&password[1],password[0]);
		}
	}
	oldA5=SetA5(oldA5);
	return result;				
}

//asks the user for a password and converts the word into a long integer
//this is then used as a seed value for the random number generator
//and then generates the array of offsets
short MakeKey()
{
	short itemhit=0;
	DialogPtr p=GetNewDialog(130, 0L, (WindowPtr)-1);
	SelectWindow(p);
	SetPort(p);
	pMirrorEditText=0L;
	
	for (;;)
	{
		ModalDialog(PasswordProc,&itemhit);
		switch (itemhit)
		{
			case 1:
				long magicnum=0;
				for (short i=0;i//checks a file passed to it and sees if it needs to be encrypted
//or decrypted.  If it's creator matches the creator of this app
//it is decrypted.  This procedure also determines the name of the
//crypt file and sets it's creator to the appropriate type.
void CheckEncrypt(const Str255 whatFile, short volRef)
{
	FInfo finderInfo;
	OSType cryptFileCreator;
	OSErr err;
	Str255 cryptFilePath;
	short temp;
	
	err=SetVol(0L,volRef);
	err=GetFInfo(whatFile, volRef, &finderInfo);
	fileSize=GetFileSize(whatFile,volRef);
	
	for (short i=0;i<=whatFile[0];i++)
		cryptFilePath[i]=whatFile[i];
	
	if (finderInfo.fdCreator == '^*@#')		//if file is already encrypted
	{
		cryptFilePath[0]-=1;				//delete the * from end of file name
		cryptFileCreator = 'R*ch';			//change the new file's creator
											//decrypt the file
		InitProgress(kDoDecrypt,whatFile);
		CodeIt(whatFile,cryptFilePath,kDoDecrypt);
	}
	else									//else we are going to encrypt it
	{
		cryptFilePath[0]+=1;				//append a * to the file name
		cryptFilePath[cryptFilePath[0]]='*';
		cryptFileCreator = '^*@#';			//change the new file's creator
											//encrypt the file
		InitProgress(kDoEncrypt,whatFile);	
		CodeIt(whatFile,cryptFilePath,kDoEncrypt);
	}

	//set the new file creator for the new file
	err = GetFInfo(cryptFilePath, volRef, &finderInfo);
	if (err==0)
	{
		finderInfo.fdCreator = cryptFileCreator;
		finderInfo.fdType = 'TEXT';
		err= SetFInfo(cryptFilePath, volRef, &finderInfo);
	}
}

//processes apple events.  If a text file is dragged onto the app,
//the program will encrypt the file if it's creator is anything except
//the creator of the app.  If an encrypted file is double clicked on,
//this program will execute and the file will be decrypted because that
//file's creator must match the creator this app in order to launch
void DragnDropOpen()
{
	short i,fileCount,whoCares;
	AppFile fileStuff;
	SFReply reply;
	SFTypeList typeList;
	Point where;
	
	CountAppFiles(&whoCares,&fileCount);
	if (fileCount>=1)
	{
		for (i=1;i<=fileCount;i++)
		{
			GetAppFiles(i,&fileStuff);
			if (MakeKey())
				CheckEncrypt(fileStuff.fName,fileStuff.vRefNum);
			ClrAppFiles(i);
		}
	}
	else
	{
		typeList[0]='TEXT';
		SetPt(&where,-1,-1);
		SFGetFile(where,"\pSelect a text file to Encrypt",0L,1,typeList,0L,&reply);
		if (reply.good)
			if (MakeKey())
				CheckEncrypt(reply.fName,reply.vRefNum);
 	}
 }


void main()
{	
	DragnDropOpen();
	if (progressBox!=0L) DisposeDialog(progressBox);
}


//array of random offsets used to encode/decode data

//constants for encrypting or decrypting
const kDoEncrypt=1;
const kDoDecrypt=-1;

//these are used for the password filter procedure
TEHandle pMirrorEditText;
Str255 password;

//these are used for the progress dialog
long fileSize;
Rect progressRect;
DialogPtr progressBox;

//Updates the progress box on the screen
void UpdateProgress( long i)
{
  Rect z;
  short x=progressRect.left;
  short y=progressRect.top;
  SetRect(&z,x,y,x+(int)((progressRect.right-x)*(i/fileSize)),progressRect.bottom);
  ForeColor(greenColor);
  PaintRect(&z);
  ForeColor(blackColor);
  FrameRect(&z);
}

//sets up the progress window, gets the rectangle, sets static text
void InitProgress(const short mode,const Str255 name)
{
  Handle h;
  short x;
  Rect r;
  Str255 text;
  
  progressBox=GetNewDialog(129,0L,(WindowPtr)-1);
  SetPort(progressBox);
  
  GetDItem(progressBox,1,&x,&h,&r);
  if (mode==kDoEncrypt)
    SetIText(h,"\pEncrypting File:");
  else
    SetIText(h,"\pDecrypting File:");
  GetDItem(progressBox,3,&x,&h,&r);
  SetIText(h,name);
  GetDItem(progressBox,2,&x,&h,&progressRect);
}

//gets the size in bytes of a file
long GetFileSize(const Str255 fName,short vRefNum)
{
  OSErr err;
  short fileRef;
  long size;
  
  err=FSOpen(fName,vRefNum,&fileRef);
  err=GetEOF(fileRef,&size);
  err=FSClose(fileRef);
  return size;
}

//this is the actual encoding/decoding procedure
//the names of the source file and crypt file are determined
//by the CheckEncrypt procedure and passed to this one
//the procedure loops through each character in the file and
//adds to it's ascii value a value in the random array or
//subtracts to decrypt
void CodeIt(const Str255 sourceFilePath, const Str255 cryptFilePath, short mode)
{
  FILE *source, *crypt;
  int c;
  
  source = fopen(PtoCstr(sourceFilePath), "rb" );
  crypt = fopen(PtoCstr(cryptFilePath), "wb" );
  
  long i=0;
  while( (c=fgetc(source)) != EOF)
  {
    short y=c+mode*magicArray[i%kArraySize];
    if (y>255) y-=256;
    if (y<0) y+=256;
    fputc(y,crypt);
    i++;
    UpdateProgress(i);
  }
  
  fclose((char*)source);
  fclose((char*)crypt);
  
  //these lines kept my program from crashing!
  CtoPstr((char*)sourceFilePath);
  CtoPstr((char*)cryptFilePath);
}

//the password filter procedure
//it displays the bullets as you type and still gets
//the password in
pascal Boolean PasswordProc(DialogPtr theDialog, EventRecord *theEvent, short *itemHit)
{
  short result;
  long oldA5;
  Rect dummyRect;
  TEHandle dialogTE;
  char key;
  
  oldA5=SetCurrentA5();
  result=0;
  if (pMirrorEditText==0L)
  {
    SetRect(&dummyRect,0,0,0,0);
    pMirrorEditText=TENew(&dummyRect,&dummyRect);
  }
  if (theEvent->what==keyDown || theEvent->what==autoKey)
  {
    key=(theEvent->message & charCodeMask);
    if (key==13 || key==3)
    {
      if (pMirrorEditText!=0L)
      {
        TEDispose(pMirrorEditText);
        pMirrorEditText=0L;
      }
      *itemHit=1;
      result=1;
    }
    else
    {
      if (key>=' ')
      {
        theEvent->message=(theEvent->message & -charCodeMask);
        theEvent->message=(theEvent->message | '¥');
      }
      dialogTE=(*(DialogPeek)theDialog).textH;
      (**pMirrorEditText).selStart=(**dialogTE).selStart;
      (**pMirrorEditText).selEnd=(**dialogTE).selEnd;
      TEKey(key,pMirrorEditText);
      password[0]=(**pMirrorEditText).teLength;
      BlockMove((*(**pMirrorEditText).hText),&password[1],password[0]);
    }
  }
  oldA5=SetA5(oldA5);
  return result;        
}

//asks the user for a password and converts the word into a long integer
//this is then used as a seed value for the random number generator
//and then generates the array of offsets
short MakeKey()
{
  short itemhit=0;
  DialogPtr p=GetNewDialog(130, 0L, (WindowPtr)-1);
  SelectWindow(p);
  SetPort(p);
  pMirrorEditText=0L;
  
  for (;;)
  {
    ModalDialog(PasswordProc,&itemhit);
    switch (itemhit)
    {
      case 1:
        long magicnum=0;
        for (short i=0;i//checks a file passed to it and sees if it needs to be encrypted
//or decrypted.  If it's creator matches the creator of this app
//it is decrypted.  This procedure also determines the name of the
//crypt file and sets it's creator to the appropriate type.
void CheckEncrypt(const Str255 whatFile, short volRef)
{
  FInfo finderInfo;
  OSType cryptFileCreator;
  OSErr err;
  Str255 cryptFilePath;
  short temp;
  
  err=SetVol(0L,volRef);
  err=GetFInfo(whatFile, volRef, &finderInfo);
  fileSize=GetFileSize(whatFile,volRef);
  
  for (short i=0;i<=whatFile[0];i++)
    cryptFilePath[i]=whatFile[i];
  
  if (finderInfo.fdCreator == '^*@#')   //if file is already encrypted
  {
    cryptFilePath[0]-=1;        //delete the * from end of file name
    cryptFileCreator = 'R*ch';      //change the new file's creator
                      //decrypt the file
    InitProgress(kDoDecrypt,whatFile);
    CodeIt(whatFile,cryptFilePath,kDoDecrypt);
  }
  else                  //else we are going to encrypt it
  {
    cryptFilePath[0]+=1;        //append a * to the file name
    cryptFilePath[cryptFilePath[0]]='*';
    cryptFileCreator = '^*@#';      //change the new file's creator
                      //encrypt the file
    InitProgress(kDoEncrypt,whatFile);  
    CodeIt(whatFile,cryptFilePath,kDoEncrypt);
  }

  //set the new file creator for the new file
  err = GetFInfo(cryptFilePath, volRef, &finderInfo);
  if (err==0)
  {
    finderInfo.fdCreator = cryptFileCreator;
    finderInfo.fdType = 'TEXT';
    err= SetFInfo(cryptFilePath, volRef, &finderInfo);
  }
}

//processes apple events.  If a text file is dragged onto the app,
//the program will encrypt the file if it's creator is anything except
//the creator of the app.  If an encrypted file is double clicked on,
//this program will execute and the file will be decrypted because that
//file's creator must match the creator this app in order to launch
void DragnDropOpen()
{
  short i,fileCount,whoCares;
  AppFile fileStuff;
  SFReply reply;
  SFTypeList typeList;
  Point where;
  
  CountAppFiles(&whoCares,&fileCount);
  if (fileCount>=1)
  {
    for (i=1;i<=fileCount;i++)
    {
      GetAppFiles(i,&fileStuff);
      if (MakeKey())
        CheckEncrypt(fileStuff.fName,fileStuff.vRefNum);
      ClrAppFiles(i);
    }
  }
  else
  {
    typeList[0]='TEXT';
    SetPt(&where,-1,-1);
    SFGetFile(where,"\pSelect a text file to Encrypt",0L,1,typeList,0L,&reply);
    if (reply.good)
      if (MakeKey())
        CheckEncrypt(reply.fName,reply.vRefNum);
  }
 }


void main()
{ 
  DragnDropOpen();
  if (progressBox!=0L) DisposeDialog(progressBox);
}