/******************************************************************************
 GPOPRetriever.cc

	<Description>

	BASE CLASS = virtual public JBroadcaster

	Copyright © 1998 by Glenn W. Bach.  All rights reserved.

 *****************************************************************************/

// includes

//Class Header
#include <GPOPRetriever.h>
#include <GPrefsMgr.h>
#include "GNetDebugDir.h"
#include "md5.h"

#include <JXGetPasswordDialog.h>
#include <JXTimerTask.h>

#include <GMGlobals.h>

#include <JProcess.h>
#include <JRegex.h>
#include <JOutPipeStream.h>
#include <JPtrArray-JString.h>

#include <jFileUtil.h>
#include <jStreamUtil.h>
#include <jTime.h>

#include <ace/Connector.h>
#include <ace/INET_Addr.h>
#include <ace/UNIX_Addr.h>

#include <ace/SOCK_Connector.h>
#include <ace/SOCK_Stream.h>

#include <fstream.h>
#include <strstream.h>
#include <iostream.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jAssert.h>

#undef new
#undef delete

typedef JMessageProtocol<ACE_SOCK_STREAM>	InetLink;
typedef ACE_Connector<InetLink, ACE_SOCK_CONNECTOR>	INETConnector;

enum
{
	kStartup = 1,
	kAPop,
	kUser,
	kPasswd,
	kList,
	kRetr,
	kDelete,
	kQuit
};

/******************************************************************************
 Constructor

 *****************************************************************************/

GPOPRetriever::GPOPRetriever()
	:
   JBroadcaster()
{
	itsLink 			= NULL;
	itsDialog			= NULL;
	itsReadUIDList		= NULL;
	itsUIDList			= NULL;
	itsNewUIDIndexes	= NULL;
	itsTimerTask		= NULL;
	itsDeleteTask 		= NULL;
	itsProcmailProcess	= NULL;
	itsMessageBuffer 	= NULL;
	itsCurrentMode 		= kStartup;
	itsCurrentIndex 	= 1;
	itsMessageCount 	= 0;
	itsListLinesCount	= 0;

// These relate to the retrieval of individual messages.
// When a "retr" command is set, "itsMessageStarting" is set to kTrue
// because we need to pull out the response (ie. +OK) string before
// saving the message.

	itsMessageStarting	= kTrue;
	itsMessageFinished 	= kFalse;

	itsListStarting		= kTrue;

	if (GGetPrefsMgr()->RememberPasswd())
		{
		itsPasswd = GGetPrefsMgr()->GetPopPasswd();
		}

	itsBuffers = new JPtrArray<JString>;
	assert(itsBuffers != NULL);

	if (GGetPrefsMgr()->LeaveOnServer())
		{
		itsReadUIDList = new JPtrArray<JString>;
		assert(itsReadUIDList != NULL);
		itsReadUIDList->SetCompareFunction(JCompareStringsCaseSensitive);
		GGetPrefsMgr()->GetUIDList(*itsReadUIDList);

		itsUIDList = new JPtrArray<JString>;
		assert(itsUIDList != NULL);

		itsNewUIDIndexes = new JArray<JIndex>;
		assert(itsNewUIDIndexes != NULL);
		}

	if (GGetPrefsMgr()->AutoCheckingMail())
		{
		JInteger delay = GGetPrefsMgr()->CheckMailDelay();
		itsTimerTask = new JXTimerTask(delay * 60 * 1000);
		assert(itsTimerTask != NULL);
		JXGetApplication()->InstallIdleTask(itsTimerTask);
		ListenTo(itsTimerTask);
		}

	ListenTo(GGetPrefsMgr());
}

/******************************************************************************
 Destructor

 *****************************************************************************/

GPOPRetriever::~GPOPRetriever()
{
	if (GGetPrefsMgr()->LeaveOnServer())
		{
		if (itsReadUIDList != NULL)
			{
			GGetPrefsMgr()->SetUIDList(*itsReadUIDList);
			}
		}
	delete itsLink;
	if (itsReadUIDList != NULL)
		{
		itsReadUIDList->DeleteAll();
		delete itsReadUIDList;
		}
	if (itsUIDList != NULL)
		{
		itsUIDList->DeleteAll();
		delete itsUIDList;
		}
	if (itsNewUIDIndexes != NULL)
		{
		delete itsNewUIDIndexes;
		}
	itsBuffers->DeleteAll();
	delete itsBuffers;
	if (itsMessageBuffer != NULL)
		{
		delete itsMessageBuffer;
		itsMessageBuffer = NULL;
		}
}

/******************************************************************************
 Receive

 *****************************************************************************/

void
GPOPRetriever::Receive
	(
	JBroadcaster* 					sender,
	const JBroadcaster::Message& 	message
	)
{
	if (sender == itsLink && message.Is(JMessageProtocolT::kMessageReady))
		{
		ReadReturnValue();
		}
	else if (sender == itsDialog && message.Is(JXDialogDirector::kDeactivated))
		{
		const JXDialogDirector::Deactivated* info = 
			dynamic_cast(const JXDialogDirector::Deactivated*, &message);
		assert(info != NULL);
		if (info->Successful())
			{
			itsPasswd = itsDialog->GetPassword();
			if (!itsPasswd.IsEmpty())
				{
				if (GGetPrefsMgr()->RememberPasswd())
					{
					GGetPrefsMgr()->SetPopPasswd(itsPasswd);
					}
				ConnectToServer();
				}
			}
		itsDialog = NULL;
		}
	else if (sender == GGetPrefsMgr() && message.Is(GPrefsMgr::kPopChanged))
		{
		if (GGetPrefsMgr()->LeaveOnServer() && (itsReadUIDList == NULL))
			{
			itsReadUIDList = new JPtrArray<JString>;
			assert(itsReadUIDList != NULL);
			itsReadUIDList->SetCompareFunction(JCompareStringsCaseSensitive);
			GGetPrefsMgr()->GetUIDList(*itsReadUIDList);

			itsUIDList = new JPtrArray<JString>;
			assert(itsUIDList != NULL);

			itsNewUIDIndexes = new JArray<JIndex>;
			assert(itsNewUIDIndexes != NULL);
			}
		if (GGetPrefsMgr()->AutoCheckingMail())
			{
			JInteger delay = GGetPrefsMgr()->CheckMailDelay();
			if (itsTimerTask == NULL)
				{
				itsTimerTask = new JXTimerTask(delay * 60 * 1000);
				assert(itsTimerTask != NULL);
				JXGetApplication()->InstallIdleTask(itsTimerTask);
				ListenTo(itsTimerTask);
				}
			else
				{
				itsTimerTask->SetPeriod(delay * 60 * 1000);
				}
			}
		else
			{
			if (itsTimerTask != NULL)
				{
				StopListening(itsTimerTask);
				delete itsTimerTask;
				itsTimerTask = NULL;
				}
			}
		}
	else if (message.Is(JXTimerTask::kTimerWentOff))
		{
		if (sender == itsTimerTask)
			{
			CheckMail();
			}
		else if (sender == itsDeleteTask)
			{
			delete itsLink;
			itsLink = NULL;
			itsDeleteTask = NULL;
			}
		}
	else if (sender == itsProcmailProcess && message.Is(JProcess::kFinished))
		{
		StopListening(itsProcmailProcess);
		delete itsProcmailProcess;
		itsProcmailProcess = NULL;
		SaveMessage();
		}
}

/******************************************************************************
 CheckMail

 *****************************************************************************/

void
GPOPRetriever::CheckMail()
{	
	
	if ((itsDialog != NULL) || (itsLink != NULL))
		{
		return;
		}

	JString inbox = GGetPrefsMgr()->GetDefaultInbox();
	JString lockfile = JString(inbox) + ".lock";
	if (JFileExists(lockfile))
		{
		JString report = "Your inbox \"" + inbox + "\" is locked."
						" No mail can be retrieved until this lock is removed."
						" If another program is currently using this file"
						" it will remove the lock when it is finished."
						" Do you want to forcibly remove the lock?";

		JBoolean ok = JGetUserNotification()->AskUserYes(report);
		if (ok)
			{
			GUnlockFile(inbox);
			}
		else
			{
			return;
			}
		}
//	ofstream os(lockfile);
//	if (!os.good())
//		{
//		JString report = "I was unable to lock your inbox \""
//						+ inbox + "\", so your mail can not be"
//						" retrieved at this time.";
//		JGetUserNotification()->ReportError(report);
//		return;
//		}
	if (itsPasswd.IsEmpty())
		{
		assert(itsDialog == NULL);
		itsDialog = new JXGetPasswordDialog(JXGetApplication());
		assert(itsDialog != NULL);
		ListenTo(itsDialog);
		itsDialog->BeginDialog();
		}
	else
		{
		ConnectToServer();
		}
}

/******************************************************************************
 ConnectToServer

 If the necessary settings are available in the preferences (ie. pop server
 and user name), a connection is made here to the pop socket (110) of the
 pop server.

 *****************************************************************************/

void
GPOPRetriever::ConnectToServer()
{
	JBoolean POPClicked		= GGetPrefsMgr()->UsingPopServer();
	JBoolean serverEmpty	= GGetPrefsMgr()->GetPopServer().IsEmpty();
	JBoolean nameEmpty 		= GGetPrefsMgr()->GetPopUserName().IsEmpty();
	JBoolean needPrefs 		= JConvertToBoolean(serverEmpty || nameEmpty || !POPClicked);
	JString report;
	if (needPrefs)
		{
		if (!POPClicked)
			{
			report = "In order to check mail, you must configure POP in the "
					 "Mail Preferences dialog. Would you like to do that now?";
			}
		else if (serverEmpty && nameEmpty)
			{
			report = "In order to check mail, I need a valid mail server"
					 " and user name. Would you like to specify them now?";
			}
		else if (serverEmpty)
			{
			report = "In order to check mail, I need a valid mail server."
					 "Would you like to specify it now?";
			}
		else
			{
			report = "In order to check mail, I need a valid  user name."
					 " Would you like to specify it now?";
			}
		if (JGetUserNotification()->AskUserYes(report))
			{
			GGetPrefsMgr()->EditPrefs();
			}
		return;
		}
	JString addrStr = GGetPrefsMgr()->GetPopServer() + ":110";
	ACE_INET_Addr addr(addrStr);
	
	itsLink = new InetLink;
	assert(itsLink != NULL);
	
	INETConnector* connector = new INETConnector;
	assert( connector != NULL );

	if (connector->connect(itsLink, addr) == -1)
		{
		JString notice = "No response from the POP server.";
		JGetUserNotification()->ReportError(notice);
		itsLink = NULL;
		}
	else
		{
		ListenTo(itsLink);
		}
}

/******************************************************************************
 ReadReturnValue

 	If using APOP, the timestamp returned from the server must be retreived.
 	That's what the regex: <[[:digit:]]+[-.][[:digit:]]+@[^>]+> is for. If
 	the response does not match this regex, I assume that it does not support
 	APOP.

 *****************************************************************************/

void
GPOPRetriever::ReadReturnValue()
{
	JString line;
	JBoolean ok = itsLink->GetNextMessage(&line);
	assert(ok);
	if (((itsCurrentMode != kRetr) && (itsCurrentMode != kList)) || 
		((itsCurrentMode == kRetr) && itsMessageStarting) ||
		((itsCurrentMode == kList) && itsListStarting))
		{
		JIndex findex;
		ok = line.LocateSubstring(" ", &findex);
		JString response;
		if (ok)
			{
			assert(findex > 1);
			response = line.GetSubstring(1, findex - 1);
			}
		else
			{
			response = line;
			}
		if (!response.BeginsWith("+OK"))
			{
			itsDeleteTask = new JXTimerTask(10,kTrue);
			assert(itsDeleteTask != NULL);
			JXGetApplication()->InstallIdleTask(itsDeleteTask);
			ListenTo(itsDeleteTask);
			StopListening(itsLink);
			JGetUserNotification()->ReportError(line);
			if ((itsCurrentMode == kPasswd) || (itsCurrentMode == kAPop))
				{
				itsPasswd = "";
				if (GGetPrefsMgr()->RememberPasswd())
					{
					GGetPrefsMgr()->SetPopPasswd(itsPasswd);
					}
				assert(itsDialog == NULL);
				itsDialog = new JXGetPasswordDialog(JXGetApplication());
				assert(itsDialog != NULL);
				ListenTo(itsDialog);
				itsDialog->BeginDialog();
				}
			itsCurrentMode 		= kStartup;
			itsCurrentIndex 	= 1;
			itsMessageStarting 	= kTrue;
			itsListStarting		= kTrue;
			if (itsMessageBuffer != NULL)
				{
				delete itsMessageBuffer;
				itsMessageBuffer = NULL;
				}
			return;
			}
		}

	if (itsCurrentMode == kStartup)
		{
		GMGetPOPDebugDir()->AddText(line);
		if (GGetPrefsMgr()->UsingAPop())
			{
			itsCurrentMode = kAPop;

		// Get the time stamp
			JRegex regex;
			JBoolean matched;
			JArray<JIndexRange>* subList = new JArray<JIndexRange>;
			assert(subList != NULL);
			JError err = regex.SetPattern("<[[:digit:]]+[-.][[:digit:]]+@[^>]+>");
			assert(err.OK());
			matched = regex.Match(line, subList);
			if (matched)
				{
				JIndexRange sRange = subList->GetElement(1);
				itsTimeStamp = line.GetSubstring(sRange);
				}
			else
				{
				itsDeleteTask = new JXTimerTask(10,kTrue);
				assert(itsDeleteTask != NULL);
				JXGetApplication()->InstallIdleTask(itsDeleteTask);
				ListenTo(itsDeleteTask);
				JString report = "This server does not support APOP";
				JGetUserNotification()->ReportError(report);
				itsCurrentMode 		= kStartup;
				itsCurrentIndex 	= 1;
				itsMessageStarting 	= kTrue;
				itsListStarting		= kTrue;
				if (itsMessageBuffer != NULL)
					{
					delete itsMessageBuffer;
					itsMessageBuffer = NULL;
					}
				return;
				}
			}
		else
			{
			itsCurrentMode = kUser;
			}
		}
	else if (itsCurrentMode == kAPop)
		{
		GMGetPOPDebugDir()->AddText(line);
		itsCurrentMode = kList;
		}
	else if (itsCurrentMode == kList)
		{
		GMGetPOPDebugDir()->AddText(line);
		if (!itsListStarting)
			{
			if ((line == ".") || (line == ".\r"))
				{
				if (GGetPrefsMgr()->LeaveOnServer())
					{
					HandleUIDL();
					}
				else
					{
					itsMessageCount = itsListLinesCount;
					}
				if (itsMessageCount == 0)
					{
					itsCurrentMode = kQuit;
					}
				else
					{
					itsCurrentMode++;
					}
				if (GGetPrefsMgr()->LeaveOnServer())
					{
					itsUIDList->DeleteAll();
					}
				itsListStarting = kTrue;
				}
			else
				{
				if (GGetPrefsMgr()->LeaveOnServer())
					{
					JString* str = new JString(line);
					assert(str != NULL);
					itsUIDList->Append(str);
					}
				itsListLinesCount++;
				}
			}
		else 
			{
			itsListStarting 	= kFalse;
			itsListLinesCount	= 0;
			}
		}
	else if (itsCurrentMode < kRetr)
		{
		GMGetPOPDebugDir()->AddText(line);
		itsCurrentMode++;
		}
	else if (itsCurrentMode == kRetr)
		{
		if (!itsMessageStarting)
			{
			if ((line == ".") || (line == ".\r"))
				{
				itsBuffers->Append(itsMessageBuffer);
				itsMessageBuffer = NULL;
				JBoolean ok = SaveMessage();
				if (!ok)
					{
					itsDeleteTask = new JXTimerTask(10,kTrue);
					assert(itsDeleteTask != NULL);
					JXGetApplication()->InstallIdleTask(itsDeleteTask);
					ListenTo(itsDeleteTask);
					StopListening(itsLink);
					JString report("Arrow needs the procmail program"
									" to download mail, but was unable"
									" to run it. Please make sure procmail"
									" is installed on your system.");
					JGetUserNotification()->ReportError(report);
					itsCurrentMode 		= kStartup;
					itsCurrentIndex 	= 1;
					itsMessageStarting 	= kTrue;
					itsListStarting		= kTrue;
					return;
					}
				itsMessageFinished = kTrue;
				itsMessageStarting = kTrue;
				if (itsCurrentIndex <= (JIndex)itsMessageCount)
					{
					itsCurrentIndex++;
					}
				}
			else
				{
				if (!line.IsEmpty() && line.GetLastCharacter() == '\r')
					{
					line.RemoveSubstring(line.GetLength(),line.GetLength());
					}
				if (itsMessageBuffer == NULL)
					{
					itsMessageBuffer = new JString();
					assert(itsMessageBuffer != NULL);
					}
				*itsMessageBuffer += line;
				*itsMessageBuffer += "\n";
				}
			}
		else 
			{
			itsMessageStarting = kFalse;
			GMGetPOPDebugDir()->AddText("Data not shown...");
			}
		if ((itsCurrentIndex > (JIndex)itsMessageCount) && itsMessageFinished)
			{
			itsCurrentIndex = 1;
			if (GGetPrefsMgr()->LeaveOnServer())
				{
				itsNewUIDIndexes->RemoveAll();
				itsCurrentMode = kQuit;
				}
			else
				{
				itsCurrentMode = kDelete;
				}
			}
		}
	else if (itsCurrentMode == kDelete)
		{
		GMGetPOPDebugDir()->AddText(line);
		if (itsCurrentIndex < (JIndex)itsMessageCount)
			{
			itsCurrentIndex++;
			}
		else
			{
			itsCurrentMode++;
			}
		}
	else if (itsCurrentMode == kQuit)
		{
		GMGetPOPDebugDir()->AddText(line);
		itsCurrentMode 	= kStartup;
		itsMessageStarting = kTrue;
		itsCurrentIndex = 1;
		itsDeleteTask = new JXTimerTask(10,kTrue);
		assert(itsDeleteTask != NULL);
		JXGetApplication()->InstallIdleTask(itsDeleteTask);
		ListenTo(itsDeleteTask);
		if (itsMessageBuffer != NULL)
			{
			delete itsMessageBuffer;
			itsMessageBuffer = NULL;
			}
		}

	SendNextData();
}

/******************************************************************************
 SendNextData

 *****************************************************************************/

void
GPOPRetriever::SendNextData()
{
	if (itsCurrentMode == kAPop)
		{
		JString senddata = "apop " + GGetPrefsMgr()->GetPopUserName();
		senddata += " " + EncryptedTimeAndPass();
		SendLine(senddata);
		}
	else if (itsCurrentMode == kUser)
		{
		JString senddata = "user " + GGetPrefsMgr()->GetPopUserName();
		SendLine(senddata);
		}
	else if (itsCurrentMode == kPasswd)
		{
		JString senddata = "pass " + itsPasswd;
		SendLine(senddata);
		}
	else if (itsCurrentMode == kList)
		{
		if (itsListStarting)
			{
			if (GGetPrefsMgr()->LeaveOnServer())
				{
				SendLine("uidl");
				}
			else
				{
				SendLine("list");
				}
			}
		}
	else if ((itsCurrentMode == kRetr) && itsMessageStarting)
		{
		assert(itsCurrentIndex <= (JIndex)itsMessageCount);
		JString senddata = "retr ";
		if (GGetPrefsMgr()->LeaveOnServer())
			{
			senddata += JString(itsNewUIDIndexes->GetElement(itsCurrentIndex));
			}
		else
			{
			senddata += JString(itsCurrentIndex);
			}
		SendLine(senddata);
		}
	else if (itsCurrentMode == kDelete)
		{
		assert(itsCurrentIndex <= (JIndex)itsMessageCount);
		JString senddata = "dele " + JString(itsCurrentIndex);
		SendLine(senddata);
		}
	else if (itsCurrentMode == kQuit)
		{
		JString senddata = "quit";
		SendLine(senddata);
		}
}

/******************************************************************************
 SaveMessage

 The individual messages are delivered here via procmail. Since pop strips
 the From line from each message, we need to use "procmail -f -" which adds
 the From line. The nice thing about having procmail do this, is that the
 user automatically gets filtering via the procmailrc file:

 	$HOME/.procmailrc

 The environment variable "DEFAULT" tells procmail where to deliver mail by
 default.

 The Create functions forks, and then execs procmail. The stdin of procmail
 is redirected (via dup2) to "toFD", so that it will read from our pipe.
 procmail reads until it detects an end-of-file on the pipe, which happens
 when we close the pipe. JOutPipeStream closes the pipe when we delete it,
 since we passed kTrue as the second argument to its constructor.

 *****************************************************************************/

JBoolean
GPOPRetriever::SaveMessage()
{
	if ((itsProcmailProcess == NULL) && (itsBuffers->GetElementCount() > 0))
		{
		int toFD;

		JString envStr = "DEFAULT=" + GGetPrefsMgr()->GetDefaultInbox();
		JString execStr = "procmail -p -f - " + envStr;

		JError err = JProcess::Create(&itsProcmailProcess, execStr,
										kJCreatePipe, &toFD,
										kJIgnoreConnection, NULL,
										kJTossOutput, NULL);

		if (err.OK())
			{
			ListenTo(itsProcmailProcess);
			
			JOutPipeStream* outP = new JOutPipeStream(toFD, kTrue);
			assert(outP != NULL);
			
			JString* buffer = itsBuffers->NthElement(1);
			buffer->Print(*outP);
			itsBuffers->DeleteElement(1);
			delete outP;

			return kTrue;
			}
		return kFalse;
		}
	return kTrue;
}

/******************************************************************************
 HandleUIDL

 In this function, I have to merge two id lists. One list (itsReadUIDList),
 that is saved in the prefs file, contains the list of unique id's (uids
 from uidl) that have been read. The other list (itsUIDList) is the set of
 ids that are currently on the pop server. 

 First, I loop through each of the ids in itsUIDList and check if they are
 already in itsReadUIDList. If the are, I set the corresponding entry in
 stillPresent, otherwise I add the index of this id into itsNewUIDIndexes.

 Next, I remove any ids in itsReadUIDList that are no longer on the server
 by looping through the stillPresent array.

 Finally, I loop through itsNewUIDIndexes, and add the corresponding id
 from itsUIDList to itsReadUIDList.

 Note: The ids in itsUIDList have an index number prepended to them. This
 needs to be removed before searching or inserting.

 *****************************************************************************/

void
GPOPRetriever::HandleUIDL()
{
	const JSize readCount = itsReadUIDList->GetElementCount();
//cout << "Read count: " << readCount << endl;
	JArray<JBoolean> stillPresent;
	for (JSize i = 1; i <= readCount; i++)
		{
		stillPresent.AppendElement(kFalse);
		}
		
	const JSize count = itsUIDList->GetElementCount();
	for (JSize i = 1; i <= count; i++)
		{
		JString id = *(itsUIDList->NthElement(i));
		JIndex findex;
		JBoolean ok = id.LocateSubstring(" ", &findex);
		assert(ok);
		assert(findex > 1);
		id.RemoveSubstring(1, findex);
		id.TrimWhitespace();
	//cout << "ID: #" << i << ": " << id << endl;
		if (itsReadUIDList->SearchSorted(&id, JOrderedSetT::kAnyMatch, &findex))
			{
			stillPresent.SetElement(i, kTrue);
			}
		else
			{
			itsNewUIDIndexes->AppendElement(i);
			}
		}

	for (JSize i = readCount; i >= 1; i--)
		{
		if (!stillPresent.GetElement(i))
			{
			itsReadUIDList->DeleteElement(i);
			}
		}

	const JSize indexCount = itsNewUIDIndexes->GetElementCount();
	for (JSize i = 1; i <= indexCount; i++)
		{
		JIndex newIndex = itsNewUIDIndexes->GetElement(i);
		//cout << "Index: " << newIndex << endl;
		JString* id = new JString(*(itsUIDList->NthElement(newIndex)));
		assert(id != NULL);
		JIndex findex;
		JBoolean ok = id->LocateSubstring(" ", &findex);
		assert(ok);
		assert(findex > 1);
		id->RemoveSubstring(1, findex);
		id->TrimWhitespace();
		itsReadUIDList->InsertSorted(id, kFalse);
		}

	itsMessageCount = indexCount;
}

/******************************************************************************
 EncryptedTimeAndPass

 This function is needed for authenticated pop (APOP).

 When the pop3 session is started, a timestamp is sent from the server to the
 client. The timestamp has the form "<pid.time@hostname>", ie:

 	<6977.900359467@dummy.caltech.edu>

 To use apop the user must have a pop password on the server. This password
 is added via popauth. Instead of using user/pass for authentication (which
 sends the password in plaintext), apop is used in the form:

 	apop username authentication-string

 where the authentication-string is formed by appending the user's pop
 password to the timestamp shown above, and performing an md5 sum on the
 resulting string. This value is then converted to a hex text string.

 *****************************************************************************/

JString
GPOPRetriever::EncryptedTimeAndPass()
{
	MD5_CTX mdContext;
	unsigned char digest[16];
	
	JString buffer = itsTimeStamp + itsPasswd;
	char* charbuffer = strdup(buffer.GetCString());
	
	MD5Init(&mdContext);	
	MD5Update(&mdContext, (unsigned char *)charbuffer, strlen(charbuffer));
	MD5Final(digest, &mdContext);

	free(charbuffer);

	JString encrypted;
	char charchunk[5];
	for (int i = 0 ; i < 16; i++)
		{
		sprintf(charchunk, "%02x", digest[i]);
		encrypted += charchunk;
		}

	return encrypted;
}

/******************************************************************************
 SendLine (private)

 ******************************************************************************/

void
GPOPRetriever::SendLine
	(
	const JCharacter* line
	)
{
	JString sLine(line);
	SendLine(sLine);
}

void
GPOPRetriever::SendLine
	(
	const JString& line
	)
{
	JString sLine(line);
	sLine += "\r";
	itsLink->SendMessage(sLine);
	if (itsCurrentMode == kPasswd)
		{
		GMGetPOPDebugDir()->AddText("pass xxxxxxxx");
		}
	else
		{
		GMGetPOPDebugDir()->AddText(line);
		}
}

/******************************************************************************
 Required templates

	These are defined because the linker asked for them.


#define JTemplateName ACE_Connector
#define JTemplateType InetLink, ACE_SOCK_Connector, ACE_INET_Addr
#include <instantiate_template.h>
#undef JTemplateName
#undef JTemplateType

#define JTemplateName ACE_Map_Manager
#define JTemplateType ACE_HANDLE, ACE_Svc_Tuple<InetLink>*, ACE_SYNCH_RW_MUTEX
#include <instantiate_template.h>
#undef JTemplateName
#undef JTemplateType

#define JTemplateName ACE_Map_Iterator
#define JTemplateType ACE_HANDLE, ACE_Svc_Tuple<InetLink>*, ACE_SYNCH_RW_MUTEX
#include <instantiate_template.h>
#undef JTemplateName
#undef JTemplateType

#define JTemplateName ACE_Map_Entry
#define JTemplateType ACE_HANDLE, ACE_Svc_Tuple<InetLink>*
#include <instantiate_template.h>
#undef JTemplateName
#undef JTemplateType

#define JTemplateName ACE_Svc_Tuple
#define JTemplateType InetLink
#include <instantiate_template.h>
#undef JTemplateName
#undef JTemplateType
******************************************************************************/


