/*
**	termTransfer.c
**
**	File transfer routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The action strings to display. */

STATIC STRPTR	SendQuery[3],
		ReceiveQuery[3],
		TransferTypes[3];

	/* ReplaceName(STRPTR Original):
	 *
	 *	Replace a filename with a mangled version for
	 *	file uploads.
	 */

STATIC STRPTR __regargs
ReplaceName(STRPTR Source)
{
	if(Config -> TransferConfig -> MangleFileNames)
	{
		UBYTE LocalName[MAX_FILENAME_LENGTH],*Char;

		strcpy(OriginalName,Source);

		ShrinkName(FilePart(Source),LocalName,12,TRUE);

		strcpy(ShrunkenName,Source);

		Char = PathPart(ShrunkenName);

		*Char = 0;

		AddPart(ShrunkenName,LocalName,MAX_FILENAME_LENGTH);

		return(ShrunkenName);
	}
	else
	{
		OriginalName[0] = 0;

		return(Source);
	}
}

	/* FreeFileTransferInfo(struct FileTransferInfo *Info):
	 *
	 *	Free a file transfer info list as allocated
	 *	by AllocFileTransferInfo() and AddFileTransferNode().
	 */

VOID __regargs
FreeFileTransferInfo(struct FileTransferInfo *Info)
{
	if(Info)
	{
		struct FileTransferNode	*Node,
					*Next;

		Node = (struct FileTransferNode *)Info -> FileList . mlh_Head;

		while(Next = (struct FileTransferNode *)Node -> Node . mln_Succ)
		{
			FreeVecPooled(Node);

			Node = Next;
		}

		FreeVecPooled(Info);
	}
}

	/* AllocFileTransferInfo():
	 *
	 *	Allocate a FileTransferInfo structure for use with
	 *	AddFileTransferNode().
	 */

struct FileTransferInfo *
AllocFileTransferInfo()
{
	struct FileTransferInfo *Info;

	if(Info = (struct FileTransferInfo *)AllocVecPooled(sizeof(struct FileTransferInfo),MEMF_ANY | MEMF_CLEAR))
	{
		NewList((struct List *)&Info -> FileList);

		return(Info);
	}

	return(NULL);
}

	/* AddFileTransferNode(struct FileTransferInfo *Info,STRPTR Name,ULONG Size):
	 *
	 *	Allocate a file transfer information node.
	 */

BYTE __regargs
AddFileTransferNode(struct FileTransferInfo *Info,STRPTR Name,ULONG Size)
{
	struct FileTransferNode	*Node;
	WORD			 Len = strlen(Name) + 1;

	if(Node = (struct FileTransferNode *)AllocVecPooled(sizeof(struct FileTransferNode) + Len,MEMF_ANY))
	{
		Node -> Size	= Size;
		Node -> Name	= (STRPTR)(Node + 1);

		strcpy(Node -> Name,Name);

		AddTail((struct List *)&Info -> FileList,(struct Node *)Node);

		Info -> TotalSize += Size;

		Info -> TotalFiles++;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* Compare(struct FileTransferNode **A,struct FileTransferNode **B):
	 *
	 *	Local subroutine required by qsort().
	 */

STATIC int __stdargs
Compare(struct FileTransferNode **A,struct FileTransferNode **B)
{
	return(Stricmp(FilePart((*A) -> Name),FilePart((*B) -> Name)));
}

	/* SortFileTransferInfo(struct FileTransferInfo *Info):
	 *
	 *	Sorts the file transfer information list in ascending
	 *	order, but makes sure that the files are sorted
	 *	in descending order (i.e. the larger files come
	 *	first and files of equal size are sorted
	 *	lexically).
	 */

VOID __regargs
SortFileTransferInfo(struct FileTransferInfo *Info)
{
	if(Info -> TotalFiles > 1)
	{
		struct FileTransferNode **NodeList;

		if(NodeList = (struct FileTransferNode **)AllocVecPooled(sizeof(struct FileTransferNode *) * Info -> TotalFiles,MEMF_ANY))
		{
			struct FileTransferNode	*Node,
						*Next;
			LONG			 i = 0;

			Node = (struct FileTransferNode *)Info -> FileList . mlh_Head;

			while(Next = (struct FileTransferNode *)Node -> Node . mln_Succ)
			{
				NodeList[i++] = Node;

				Node = Next;
			}

			qsort((APTR)NodeList,Info -> TotalFiles,sizeof(struct FileTransferNode *),Compare);

			NewList((struct List *)&Info -> FileList);

			for(i = 0 ; i < Info -> TotalFiles ; i++)
				AddTail((struct List *)&Info -> FileList,(struct Node *)&NodeList[i] -> Node);

			FreeVecPooled(NodeList);
		}
	}

	Info -> DoneSize	= 0;
	Info -> DoneFiles	= 0;

	Info -> CurrentFile	= (struct FileTransferNode *)Info -> FileList . mlh_Head;
	Info -> CurrentSize	= Info -> CurrentFile -> Size;
}

	/* BuildFileTransferInfo(struct FileRequester *FileRequester):
	 *
	 *	Build a file transfer information list from
	 *	information provided by a FileRequester structure.
	 */

struct FileTransferInfo * __regargs
BuildFileTransferInfo(struct FileRequester *FileRequester)
{
	struct FileTransferInfo	*Info;
	LONG			 FilesFound = 0;

	if(Info = AllocFileTransferInfo())
	{
		BYTE	Success = FALSE;
		BPTR	NewLock,
			OldLock;

		if(NewLock = Lock(FileRequester -> rf_Dir,ACCESS_READ))
		{
			OldLock = CurrentDir(NewLock);

			if(FileRequester -> rf_NumArgs)
			{
				struct FileInfoBlock *FileInfo;

				if(FileInfo = (struct FileInfoBlock *)AllocDosObject(DOS_FIB,TAG_DONE))
				{
					BPTR		 FileLock;
					struct WBArg	*ArgList = FileRequester -> rf_ArgList;
					LONG		 i;

					Success = TRUE;

					for(i = 0 ; Success && i < FileRequester -> rf_NumArgs ; i++)
					{
						if(ArgList[i] . wa_Name)
						{
							if(ArgList[i] . wa_Lock)
							{
								CurrentDir(ArgList[i] . wa_Lock);

								if(FileLock = Lock(ArgList[i] . wa_Name,ACCESS_READ))
								{
									if(Examine(FileLock,FileInfo))
									{
										if(FileInfo -> fib_DirEntryType < 0)
										{
											if(NameFromLock(FileLock,SharedBuffer,512))
											{
												if(!AddFileTransferNode(Info,SharedBuffer,FileInfo -> fib_Size))
													Success = FALSE;
												else
													FilesFound++;
											}
										}
									}

									UnLock(FileLock);
								}

								CurrentDir(NewLock);
							}
							else
							{
								if(FileLock = Lock(ArgList[i] . wa_Name,ACCESS_READ))
								{
									if(Examine(FileLock,FileInfo))
									{
										if(FileInfo -> fib_DirEntryType < 0)
										{
											if(NameFromLock(FileLock,SharedBuffer,512))
											{
												if(!AddFileTransferNode(Info,SharedBuffer,FileInfo -> fib_Size))
													Success = FALSE;
												else
													FilesFound++;
											}
										}
									}

									UnLock(FileLock);
								}
							}
						}
					}
				}

				FreeDosObject(DOS_FIB,FileInfo);
			}
			else
			{
				struct AnchorPath *Anchor;

				STRPTR FileName;

				if(FileRequester -> rf_NumArgs > 1 && FileRequester -> rf_ArgList)
					FileName = FileRequester -> rf_ArgList -> wa_Name;
				else
					FileName = FileRequester -> rf_File;

				if(Anchor = (struct AnchorPath *)AllocVecPooled(sizeof(struct AnchorPath),MEMF_ANY | MEMF_CLEAR))
				{
					if(!MatchFirst(FileName,Anchor))
					{
						Success = TRUE;

						if(Anchor -> ap_Info . fib_DirEntryType < 0)
						{
							if(NameFromLock(NewLock,SharedBuffer,512))
							{
								if(AddPart(SharedBuffer,Anchor -> ap_Info . fib_FileName,512))
								{
									if(!AddFileTransferNode(Info,SharedBuffer,Anchor -> ap_Info . fib_Size))
										Success = FALSE;
									else
										FilesFound++;
								}
							}
						}

						if(Success)
						{
							while(!MatchNext(Anchor))
							{
								if(NameFromLock(NewLock,SharedBuffer,512))
								{
									if(AddPart(SharedBuffer,Anchor -> ap_Info . fib_FileName,512))
									{
										if(!AddFileTransferNode(Info,SharedBuffer,Anchor -> ap_Info . fib_Size))
										{
											Success = FALSE;

											break;
										}
										else
											FilesFound++;
									}
								}
							}
						}

						if(IoErr() != ERROR_NO_MORE_ENTRIES)
							Success = FALSE;
					}

					MatchEnd(Anchor);

					FreeVecPooled(Anchor);
				}
			}

			CurrentDir(OldLock);

			UnLock(NewLock);
		}

		if(Success && FilesFound)
		{
			SortFileTransferInfo(Info);

			return(Info);
		}
		else
			FreeFileTransferInfo(Info);
	}

	return(NULL);
}

	/* SendTextFile(STRPTR TheFile):
	 *
	 *	Send a single text file via xpr.
	 */

VOID __regargs
SendTextFile(BYTE Type,STRPTR TheFile)
{
	TheFile = ReplaceName(TheFile);

	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> InternalASCIIUpload)
		InternalASCIIUpload(TheFile,TRUE);
	else
	{
		BYTE OldStatus = Status;
		BPTR NewDir,OldDir;

		if(Type == TRANSFER_ASCII)
			NewDir = Lock(Config -> PathConfig -> ASCIIUploadPath,ACCESS_READ);
		else
			NewDir = Lock(Config -> PathConfig -> TextUploadPath,ACCESS_READ);

		if(NewDir)
			OldDir = CurrentDir(NewDir);
		else
			OldDir = NULL;

		Uploading = TRUE;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

			XprIO -> xpr_filename = TheFile;

			if(TransferPanel(SendQuery[TRANSFER_TEXT]))
			{
				Status = STATUS_UPLOAD;

				ClearSerial();

				LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[TRANSFER_TEXT]);

				if(ReadRequest && WriteRequest)
				{
					if(XProtocolSend(XprIO))
						TransferFailed = FALSE;
					else
						TransferFailed = TRUE;
				}
				else
					TransferFailed = TRUE;

				if(TransferFailed || TransferAborted)
					Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
				else
					Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

				if(TransferFailed || TransferError)
				{
					WakeUp(TransferWindow,SOUND_BADTRANSFER);

					DeleteTransferPanel(TRUE);
				}
				else
				{
					if(TransferWindow)
					{
						WakeUp(TransferWindow,SOUND_GOODTRANSFER);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

				RestartSerial(FALSE);
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		Uploading = FALSE;

		if(Config -> CommandConfig -> UploadMacro[0])
			SerialCommand(Config -> CommandConfig -> UploadMacro);

		DidTransfer = FALSE;
	}
}

	/* StartXprReceive():
	 *
	 *	Receive files via xpr.
	 */

VOID __regargs
StartXprReceive(BYTE Type,STRPTR Name,BYTE WaitForIt)
{
	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> InternalASCIIDownload)
		InternalASCIIDownload(Name,WaitForIt);
	else
	{
		struct FileRequester	*FileRequest;
		UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
		BYTE			 OldStatus = Status;
		BPTR			 NewDir,OldDir;

		ClearGenericList(GenericListTable[GLIST_DOWNLOAD]);

		LocalizeString(ReceiveQuery,MSG_TERMTRANSFER_DOWNLOAD_FILE_TXT,MSG_TERMTRANSFER_DOWNLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* Select the download path. */

		switch(Type)
		{
			case TRANSFER_BINARY:

				DownloadPath = Config -> PathConfig -> BinaryDownloadPath;
				break;

			case TRANSFER_TEXT:

				DownloadPath = Config -> PathConfig -> TextDownloadPath;
				break;

			case TRANSFER_ASCII:

				DownloadPath = Config -> PathConfig -> ASCIIDownloadPath;
				break;
		}

		if(DownloadPath[0])
		{
			if(NewDir = Lock(DownloadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* Set up the library if necessary. */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Do we need to ask the user for
				 * the destination file name?
				 */

			if(TransferBits & XPRS_NORECREQ)
			{
					/* Obviously not, let's open
					 * the transfer info window as
					 * usual and download the file(s).
					 */

				if(TransferPanel(ReceiveQuery[Type]))
				{
					Status = STATUS_DOWNLOAD;

					ClearSerial();

					LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_DOWNLOAD_TXT),TransferTypes[Type]);

						/* Receive the data. */

					if(ReadRequest && WriteRequest)
					{
						if(XProtocolReceive(XprIO))
							TransferFailed = FALSE;
						else
							TransferFailed = TRUE;
					}
					else
						TransferFailed = TRUE;

						/* In case the transfer has been aborted,
						 * flush the input buffer of dirty data.
						 */

					if(TransferAborted)
						xpr_sflush();

					if(TransferAborted || TransferFailed)
						Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
					else
						Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

					if(TransferFailed || TransferError)
					{
						WakeUp(TransferWindow,SOUND_BADTRANSFER);

						DeleteTransferPanel(WaitForIt);
					}
					else
					{
						if(TransferWindow)
						{
							WakeUp(TransferWindow,SOUND_GOODTRANSFER);

							WaitTime(2,0);
						}

						DeleteTransferPanel(FALSE);
					}

					Status = OldStatus;

						/* Queue another read request. */

					RestartSerial(FALSE);
				}
			}
			else
			{
				if(!Name)
				{
					if(FileRequest = GetFile(Window,ReceiveQuery[Type],DownloadPath,"",DummyBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_TERMTRANSFER_RECEIVE_TXT),TRUE))
					{
							/* Save the download path. */

						strcpy(DownloadPath,FileRequest -> rf_Dir);

						ConfigChanged = TRUE;

							/* Install the name of the file to receive. */

						XprIO -> xpr_filename = DummyBuffer;

						FreeAslRequest(FileRequest);

						Name = DummyBuffer;
					}
				}
				else
				{
					STRPTR Index;

					strcpy(DownloadPath,Name);

					ConfigChanged = TRUE;

					Index = PathPart(DownloadPath);

					*Index = 0;

					XprIO -> xpr_filename = Name;
				}

					/* Download the file(s). */

				if(Name)
				{
						/* Open the transfer panel. */

					if(TransferPanel(ReceiveQuery[Type]))
					{
						Status = STATUS_DOWNLOAD;

						ClearSerial();

						LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_DOWNLOAD_TXT),TransferTypes[Type]);

							/* Receive the file. */

						if(ReadRequest && WriteRequest)
						{
							if(XProtocolReceive(XprIO))
								TransferFailed = FALSE;
							else
								TransferFailed = TRUE;
						}
						else
							TransferFailed = TRUE;

							/* In case the transfer has been aborted,
							 * flush the input buffer of dirty data.
							 */

						if(TransferAborted)
							xpr_sflush();

						if(TransferAborted || TransferFailed)
							Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
						else
							Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

						if(TransferFailed || TransferError)
						{
							WakeUp(TransferWindow,SOUND_BADTRANSFER);

							DeleteTransferPanel(WaitForIt);
						}
						else
						{
							if(TransferWindow)
							{
								WakeUp(TransferWindow,SOUND_GOODTRANSFER);

								WaitTime(2,0);
							}

							DeleteTransferPanel(FALSE);
						}

						Status = OldStatus;

							/* Queue another read
							 * request.
							 */

						RestartSerial(FALSE);
					}
				}
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		DownloadPath = NULL;

		DidTransfer = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> DownloadMacro[0])
				SerialCommand(Config -> CommandConfig -> DownloadMacro);
		}
	}
}

	/* StartXprSend():
	 *
	 *	Send files via xpr.
	 */

BYTE __regargs
StartXprSend(BYTE Type,BYTE WaitForIt)
{
	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> InternalASCIIUpload)
		return(InternalASCIIUpload(NULL,WaitForIt));
	else
	{
		struct FileRequester	*FileRequest;
		UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
		BYTE			 OldStatus = Status;
		STRPTR			 UploadPath;
		BYTE			 DidSend = TRUE;
		BPTR			 NewDir,OldDir;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* We are uploading data. */

		Uploading = TRUE;

			/* Select the upload path. */

		switch(Type)
		{
			case TRANSFER_BINARY:

				UploadPath = Config -> PathConfig -> BinaryUploadPath;
				break;

			case TRANSFER_TEXT:

				UploadPath = Config -> PathConfig -> TextUploadPath;
				break;

			case TRANSFER_ASCII:

				UploadPath = Config -> PathConfig -> ASCIIUploadPath;
				break;
		}

		if(UploadPath[0])
		{
			if(NewDir = Lock(UploadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(SelectProtocol(LastXprLibrary,Window))
			{
				if(ProtocolSetup(FALSE))
				{
					SaveProtocolOpts();

					strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Do we need to use our own file requester or
				 * will xpr handle this job for us?
				 */

			if(TransferBits & XPRS_NOSNDREQ)
			{
					/* Open the transfer info window. */

				if(TransferPanel(SendQuery[Type]))
				{
					Status = STATUS_UPLOAD;

						/* Shut up the serial line. */

					ClearSerial();

					LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

						/* Perform upload. */

					if(ReadRequest && WriteRequest)
					{
						if(XProtocolSend(XprIO))
							TransferFailed = FALSE;
						else
							TransferFailed = TRUE;
					}
					else
						TransferFailed = TRUE;

					if(TransferFailed || TransferAborted)
						Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
					else
						Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

					if(TransferFailed || TransferError)
					{
						WakeUp(TransferWindow,SOUND_BADTRANSFER);

						DeleteTransferPanel(WaitForIt);
					}
					else
					{
						if(TransferWindow)
						{
							WakeUp(TransferWindow,SOUND_GOODTRANSFER);

							WaitTime(2,0);
						}

						DeleteTransferPanel(FALSE);
					}

					Status = OldStatus;

						/* And request another character. */

					RestartSerial(FALSE);
				}
			}
			else
			{
					/* We will need the file requester to find
					 * out which file(s) are to be transferred.
					 * Multiple files and wildcards are
					 * supported as well as plain file names.
					 */

				if(FileRequest = GetFile(Window,SendQuery[Type],UploadPath,"",DummyBuffer,"",FALSE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT),TRUE))
				{
					strcpy(UploadPath,FileRequest -> rf_Dir);

					ConfigChanged = TRUE;

					if(FileTransferInfo = BuildFileTransferInfo(FileRequest))
					{
							/* Make sure that at least the
							 * first file gets transferred
							 * in case the protocol does
							 * no support batch upload.
							 */

						XprIO -> xpr_filename = ReplaceName(FileTransferInfo -> CurrentFile -> Name);

						if(TransferPanel(SendQuery[Type]))
						{
							Status = STATUS_UPLOAD;

							ClearSerial();

							LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

							if(ReadRequest && WriteRequest)
							{
								if(XProtocolSend(XprIO))
									TransferFailed = FALSE;
								else
									TransferFailed = TRUE;
							}
							else
								TransferFailed = TRUE;

							if(TransferFailed || TransferAborted)
								Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
							else
								Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

							if(TransferFailed || TransferError)
							{
								WakeUp(TransferWindow,SOUND_BADTRANSFER);

								DeleteTransferPanel(WaitForIt);
							}
							else
							{
								if(TransferWindow)
								{
									WakeUp(TransferWindow,SOUND_GOODTRANSFER);

									WaitTime(2,0);
								}

								DeleteTransferPanel(FALSE);
							}

							Status = OldStatus;

							RestartSerial(FALSE);
						}
						else
							MyEasyRequest(Window,LocaleString(MSG_TERMTRANSFER_FAILED_TO_LOCATE_DIRECTORY_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),FileRequest -> rf_Dir);

						if(FileTransferInfo)
						{
							FreeFileTransferInfo(FileTransferInfo);

							FileTransferInfo = NULL;
						}
					}

					FreeAslRequest(FileRequest);
				}
				else
					DidSend = FALSE;
			}
		}
		else
			SetTransferMenu(FALSE);

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		Uploading = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> UploadMacro[0])
				SerialCommand(Config -> CommandConfig -> UploadMacro);
		}

		DidTransfer = FALSE;

		return(DidSend);
	}
}

	/* StartXprSendFromList():
	 *
	 *	Send files via xpr.
	 */

BYTE __regargs
StartXprSendFromList(BYTE Type,BYTE WaitForIt)
{
	if(Type == TRANSFER_ASCII && Config -> TransferConfig -> InternalASCIIUpload)
		return(InternalASCIIUpload(NULL,WaitForIt));
	else
	{
		BYTE	OldStatus	= Status,
			DidSend		= TRUE;
		BPTR	NewDir,
			OldDir;
		STRPTR	UploadPath;

		LocalizeString(SendQuery,MSG_TERMTRANSFER_UPLOAD_FILE_TXT,MSG_TERMTRANSFER_UPLOAD_ASCII_TXT);
		LocalizeString(TransferTypes,MSG_TERMTRANSFER_BINARY_TXT,MSG_TERMTRANSFER_ASCII_TXT);

			/* We are uploading data. */

		Uploading = TRUE;

		switch(Type)
		{
			case TRANSFER_BINARY:

				UploadPath = Config -> PathConfig -> BinaryUploadPath;
				break;

			case TRANSFER_TEXT:

				UploadPath = Config -> PathConfig -> TextUploadPath;
				break;

			case TRANSFER_ASCII:

				UploadPath = Config -> PathConfig -> ASCIIUploadPath;
				break;
		}

		if(UploadPath[0])
		{
			if(NewDir = Lock(UploadPath,ACCESS_READ))
				OldDir = CurrentDir(NewDir);
			else
				OldDir = NULL;
		}
		else
			NewDir = OldDir = NULL;

		BlockWindows();

			/* If not initialized, try to set up a new
			 * external transfer protocol.
			 */

		if(!XProtocolBase)
		{
			if(WaitForIt)
			{
				if(SelectProtocol(LastXprLibrary,Window))
				{
					if(ProtocolSetup(FALSE))
					{
						SaveProtocolOpts();

						strcpy(Config -> FileConfig -> ProtocolFileName,LastXprLibrary);
					}
				}
			}
		}

		if(XProtocolBase)
		{
			SetTransferMenu(TRUE);

				/* Make sure that at least the
				 * first file gets transferred
				 * in case the protocol does
				 * no support batch upload.
				 */

			XprIO -> xpr_filename = ReplaceName(FileTransferInfo -> CurrentFile -> Name);

			if(TransferPanel(SendQuery[Type]))
			{
				Status = STATUS_UPLOAD;

				ClearSerial();

				LogAction(LocaleString(MSG_TERMTRANSFER_LOGMSG_INITIATE_UPLOAD_TXT),TransferTypes[Type]);

				if(ReadRequest && WriteRequest)
				{
					if(XProtocolSend(XprIO))
						TransferFailed = FALSE;
					else
						TransferFailed = TRUE;
				}
				else
					TransferFailed = TRUE;

				if(TransferFailed || TransferAborted)
					Say(LocaleString(MSG_GLOBAL_TRANSFER_FAILED_OR_ABORTED_TXT));
				else
					Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

				if(TransferFailed || TransferError)
				{
					WakeUp(TransferWindow,SOUND_BADTRANSFER);

					DeleteTransferPanel(WaitForIt);
				}
				else
				{
					if(TransferWindow)
					{
						WakeUp(TransferWindow,SOUND_GOODTRANSFER);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

				RestartSerial(FALSE);
			}

			if(FileTransferInfo)
			{
				FreeFileTransferInfo(FileTransferInfo);

				FileTransferInfo = NULL;
			}
		}
		else
		{
			SetTransferMenu(FALSE);

			DidSend = FALSE;
		}

		if(OldDir)
			CurrentDir(OldDir);

		if(NewDir)
			UnLock(NewDir);

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		ReleaseWindows();

		Uploading = FALSE;

		if(WaitForIt)
		{
			if(Config -> CommandConfig -> UploadMacro[0])
				SerialCommand(Config -> CommandConfig -> UploadMacro);
		}

		DidTransfer = FALSE;

		return(DidSend);
	}
}

	/* ChangeProtocol(STRPTR ProtocolName):
	 *
	 *	Select a different file transfer protocol.
	 */

BYTE __regargs
ChangeProtocol(STRPTR ProtocolName)
{
	UBYTE NameBuffer[40],i;

	if(!ProtocolName || !ProtocolName[0])
		ProtocolName = Config -> TransferConfig -> DefaultLibrary;

	if(!Stricmp(ProtocolName,LastXprLibrary) && XProtocolBase)
		return(TRUE);

		/* Close the old library if still open. */

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;

		SetTransferMenu(FALSE);
	}

		/* Clear the XPR interface buffer. */

	memset(XprIO,0,sizeof(struct XPR_IO));

		/* Copy the name of the library. */

	strcpy(NameBuffer,FilePart(ProtocolName));

		/* Extract the name itself (strip the `.library'). */

	for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
	{
		if(NameBuffer[i] == '.')
		{
			NameBuffer[i] = 0;

			break;
		}
	}

		/* Check if the transfer protocol is a sort of ZModem. */

	UsesZModem = FALSE;

	for(i = 0 ; i <= strlen(NameBuffer) - 6 ; i++)
	{
		if(!Stricmp(&NameBuffer[i],"zmodem"))
			UsesZModem = TRUE;
	}

		/* Reset the scanner. */

	FlowInit(TRUE);

		/* Obtain the protocol default settings. */

	if(!GetEnvDOS(NameBuffer,ProtocolOptsBuffer))
		ProtocolOptsBuffer[0] = 0;

		/* Initialize the interface structure. */

	XprIO -> xpr_filename	= ProtocolOptsBuffer;
	XprIO -> xpr_fopen	= xpr_fopen;
	XprIO -> xpr_fclose	= xpr_fclose;
	XprIO -> xpr_fread	= xpr_fread;
	XprIO -> xpr_fwrite	= xpr_fwrite;
	XprIO -> xpr_sread	= xpr_sread;
	XprIO -> xpr_swrite	= xpr_swrite;
	XprIO -> xpr_sflush	= xpr_sflush;
	XprIO -> xpr_update	= xpr_update;
	XprIO -> xpr_chkabort	= xpr_chkabort;
	XprIO -> xpr_gets	= xpr_gets;
	XprIO -> xpr_setserial	= xpr_setserial;
	XprIO -> xpr_ffirst	= xpr_ffirst;
	XprIO -> xpr_fnext	= xpr_fnext;
	XprIO -> xpr_finfo	= xpr_finfo;
	XprIO -> xpr_fseek	= xpr_fseek;
	XprIO -> xpr_extension	= 4;
	XprIO -> xpr_options	= xpr_options;
	XprIO -> xpr_unlink	= xpr_unlink;
	XprIO -> xpr_squery	= xpr_squery;
	XprIO -> xpr_getptr	= xpr_getptr;

		/* Try to open the library. */

	if(XProtocolBase = (struct Library *)OpenLibrary(ProtocolName,0))
	{
			/* Set up the library. */

		ClearSerial();

		TransferBits = XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(TRUE);

			/* Successful initialization? */

		if(TransferBits & XPRS_SUCCESS)
		{
			SetTransferMenu(TRUE);

			strcpy(LastXprLibrary,ProtocolName);

			if(TransferBits & XPRS_HOSTMON)
				ConTransfer = ConTransferHost;
			else
				ConTransfer = ConProcess;

			if(TransferBits & XPRS_HOSTNOWAIT)
			{
				if(!HostReadBuffer)
					HostReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY);
			}
			else
			{
				if(HostReadBuffer)
				{
					FreeVecPooled(HostReadBuffer);

					HostReadBuffer = NULL;
				}
			}

			return(TRUE);
		}
		else
		{
			MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),ProtocolName);

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			SetTransferMenu(FALSE);

			LastXprLibrary[0] = 0;

			TransferBits = 0;

			ConTransfer = ConProcess;
		}
	}
	else
	{
		ConTransfer = ConProcess;

		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),ProtocolName);
	}

	return(FALSE);
}

	/* ResetProtocol():
	 *
	 *	Return to the previously selected file
	 *	transfer protocol.
	 */

VOID
ResetProtocol()
{
	ChangeProtocol(NULL);
}

	/* ProtocolSetup(BYTE IgnoreOptions):
	 *
	 *	Set up the library and options for the external protocol.
	 */

BYTE __regargs
ProtocolSetup(BYTE IgnoreOptions)
{
	UBYTE NameBuffer[40],i;

		/* Close the old library if still open. */

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;

		SetTransferMenu(FALSE);
	}

		/* Clear the XPR interface buffer. */

	memset(XprIO,0,sizeof(struct XPR_IO));

		/* Copy the name of the library. */

	strcpy(NameBuffer,FilePart(LastXprLibrary));

		/* Extract the name itself (strip the `.library'). */

	for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
	{
		if(NameBuffer[i] == '.')
		{
			NameBuffer[i] = 0;
			break;
		}
	}

		/* Check if the transfer protocol is a sort of ZModem. */

	UsesZModem = FALSE;

	for(i = 0 ; i <= strlen(NameBuffer) - 6 ; i++)
	{
		if(!Stricmp(&NameBuffer[i],"zmodem"))
			UsesZModem = TRUE;
	}

		/* Reset the scanner. */

	FlowInit(TRUE);

		/* Obtain the protocol default settings. */

	if(!IgnoreOptions)
	{
		if(!GetEnvDOS(NameBuffer,ProtocolOptsBuffer))
			ProtocolOptsBuffer[0] = 0;
	}

		/* Initialize the interface structure. */

	XprIO -> xpr_filename	= ProtocolOptsBuffer;
	XprIO -> xpr_fopen	= xpr_fopen;
	XprIO -> xpr_fclose	= xpr_fclose;
	XprIO -> xpr_fread	= xpr_fread;
	XprIO -> xpr_fwrite	= xpr_fwrite;
	XprIO -> xpr_sread	= xpr_sread;
	XprIO -> xpr_swrite	= xpr_swrite;
	XprIO -> xpr_sflush	= xpr_sflush;
	XprIO -> xpr_update	= xpr_update;
	XprIO -> xpr_chkabort	= xpr_chkabort;
	XprIO -> xpr_gets	= xpr_gets;
	XprIO -> xpr_setserial	= xpr_setserial;
	XprIO -> xpr_ffirst	= xpr_ffirst;
	XprIO -> xpr_fnext	= xpr_fnext;
	XprIO -> xpr_finfo	= xpr_finfo;
	XprIO -> xpr_fseek	= xpr_fseek;
	XprIO -> xpr_extension	= 4;
	XprIO -> xpr_options	= xpr_options;
	XprIO -> xpr_unlink	= xpr_unlink;
	XprIO -> xpr_squery	= xpr_squery;
	XprIO -> xpr_getptr	= xpr_getptr;

		/* Try to open the library. */

	if(XProtocolBase = (struct Library *)OpenLibrary(LastXprLibrary,0))
	{
			/* Set up the library. */

		ClearSerial();

		TransferBits = XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(IgnoreOptions != TRUE);

			/* Successful initialization? */

		if(TransferBits & XPRS_SUCCESS)
		{
			SetTransferMenu(TRUE);

			if(TransferBits & XPRS_HOSTMON)
				ConTransfer = ConTransferHost;
			else
				ConTransfer = ConProcess;

			if(TransferBits & XPRS_HOSTNOWAIT)
			{
				if(!HostReadBuffer)
					HostReadBuffer = AllocVecPooled(Config -> SerialConfig -> SerialBufferSize,MEMF_ANY);
			}
			else
			{
				if(HostReadBuffer)
				{
					FreeVecPooled(HostReadBuffer);

					HostReadBuffer = NULL;
				}
			}

			return(TRUE);
		}
		else
		{
			MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LastXprLibrary);

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			LastXprLibrary[0] = 0;

			TransferBits = 0;

			ConTransfer = ConProcess;

			SetTransferMenu(FALSE);
		}
	}
	else
	{
		ConTransfer = ConProcess;

		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),LastXprLibrary);
	}

	return(FALSE);
}

	/* SaveProtocolOpts():
	 *
	 *	Save the current protocol settings to an environment variable.
	 */

VOID
SaveProtocolOpts()
{
		/* It's time to save the altered options. */

	if(NewOptions && XProtocolBase)
	{
		UBYTE NameBuffer[40],i;

			/* Strip the `.library' part. */

		strcpy(NameBuffer,FilePart(LastXprLibrary));

		for(i = strlen(NameBuffer) - 1 ; i >= 0 ; i--)
		{
			if(NameBuffer[i] == '.')
			{
				NameBuffer[i] = 0;
				break;
			}
		}

			/* Cause the xpr.library to prompt for
			 * input. We expect the library to fill
			 * the prompt string with the default
			 * settings. The resulting string is
			 * intercepted by xpr_stealopts, saved
			 * to an environment variable and will
			 * serve as a reinitialization string
			 * later.
			 */

		XprIO -> xpr_filename	= NULL;
		XprIO -> xpr_gets	= xpr_stealopts;
		XprIO -> xpr_extension	= 0;
		XprIO -> xpr_options	= NULL;

		ClearSerial();

		XProtocolSetup(XprIO);

		DeleteTransferPanel(FALSE);

			/* Save the options in case anything goes
			 * wrong.
			 */

		NewOptions = FALSE;

		SetEnvDOS(NameBuffer,ProtocolOptsBuffer);

			/* Reinitialize the library. */

		XprIO -> xpr_filename	= ProtocolOptsBuffer;
		XprIO -> xpr_gets	= xpr_gets;
		XprIO -> xpr_extension	= 4;
		XprIO -> xpr_options	= xpr_options;

		XProtocolSetup(XprIO);

		RestartSerial(FALSE);

		DeleteTransferPanel(FALSE);
	}
}

	/* SelectProtocol(STRPTR Name,struct Window *ParentWindow):
	 *
	 *	Select a different transfer protocol library using
	 *	the asl.library file requester.
	 */

BYTE __regargs
SelectProtocol(STRPTR Name,struct Window *ParentWindow)
{
	strcpy(SharedBuffer,LastXprLibrary);

	if(PickFile(Window,"Libs:","xpr#?.library",LocaleString(MSG_TERMXPR_SELECT_TRANSFER_PROTOCOL_TXT),SharedBuffer,NT_LIBRARY))
	{
		if(Stricmp(SharedBuffer,LastXprLibrary))
		{
			strcpy(LastXprLibrary,SharedBuffer);

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* TransferCleanup():
	 *
	 *	We did a file transfer (auto-download?) and
	 *	will need to close the transfer window.
	 */

VOID
TransferCleanup()
{
	if(DidTransfer)
	{
		if(TransferFailed || TransferError)
		{
			WakeUp(TransferWindow,SOUND_BADTRANSFER);

			DeleteTransferPanel(TRUE);
		}
		else
		{
			if(TransferWindow)
			{
				WakeUp(TransferWindow,SOUND_GOODTRANSFER);

				WaitTime(2,0);
			}

			DeleteTransferPanel(FALSE);
		}

		if(SendAbort && UsesZModem)
			SerWrite(ZModemCancel,20);

		SendAbort = FALSE;

		if(Config -> CommandConfig -> DownloadMacro[0])
			SerialCommand(Config -> CommandConfig -> DownloadMacro);

		Say(LocaleString(MSG_GLOBAL_TRANSFER_COMPLETED_TXT));

		DidTransfer = FALSE;
	}
	else
	{
		if(TransferFailed || TransferError)
		{
			WakeUp(TransferWindow,SOUND_BADTRANSFER);

			DeleteTransferPanel(TRUE);
		}
		else
		{
			if(TransferWindow)
			{
				WakeUp(TransferWindow,SOUND_GOODTRANSFER);

				WaitTime(2,0);
			}

			DeleteTransferPanel(FALSE);
		}
	}

	BinaryTransfer = TRUE;

	Status = STATUS_READY;

	ReleaseWindows();
}

	/* RemoveUploadListItem(STRPTR Name):
	 *
	 *	Remove a named item from the upload list.
	 */

VOID __regargs
RemoveUploadListItem(STRPTR Name)
{
	BPTR NameLock;

	if(NameLock = Lock(Name,ACCESS_READ))
	{
		struct GenericList	*List = GenericListTable[GLIST_UPLOAD];
		struct Node		*Node;
		STRPTR			 Base = FilePart(Name);

		ObtainSemaphore(&List -> ListSemaphore);

		Node = (struct Node *)List -> ListHeader . mlh_Head;

		while(Node -> ln_Succ)
		{
			if(!Stricmp(Base,FilePart(Node -> ln_Name)))
			{
				BPTR ListLock;

				if(ListLock = Lock(Node -> ln_Name,ACCESS_READ))
				{
					if(SameLock(ListLock,NameLock) == LOCK_SAME)
					{
						Forbid();

						ReleaseSemaphore(&List -> ListSemaphore);

						DeleteGenericListNode(List,Node);

						Permit();

						UnLock(ListLock);

						UnLock(NameLock);

						return;
					}

					UnLock(ListLock);
				}
			}

			Node = Node -> ln_Succ;
		}

		ReleaseSemaphore(&List -> ListSemaphore);

		UnLock(NameLock);
	}
}
