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

#include "termGlobal.h"

	/* The action strings to display. */

STATIC STRPTR SendQuery[3] =
{
	NULL,
	NULL,
	NULL
};

STATIC STRPTR ReceiveQuery[3] =
{
	NULL,
	NULL,
	NULL
};

	/* The transfer types to display. */

STATIC STRPTR TransferTypes[3] =
{
	NULL,
	NULL,
	NULL
};

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

VOID
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)
		{
			FreeVec(Node);

			Node = Next;
		}

		FreeVec(Info);
	}
}

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

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

	if(Info = (struct FileTransferInfo *)AllocVec(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
AddFileTransferNode(struct FileTransferInfo *Info,STRPTR Name,ULONG Size)
{
	struct FileTransferNode	*Node;
	WORD			 Len = strlen(Name) + 1;

	if(Node = (struct FileTransferNode *)AllocVec(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
SortFileTransferInfo(struct FileTransferInfo *Info)
{
	if(Info -> TotalFiles > 1)
	{
		struct FileTransferNode **NodeList;

		if(NodeList = (struct FileTransferNode **)AllocVec(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);

			FreeVec(NodeList);
		}
	}
}

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

struct FileTransferInfo *
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;

				if(Anchor = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath),MEMF_ANY | MEMF_CLEAR))
				{
					if(!MatchFirst(FileRequester -> rf_File,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);

					FreeVec(Anchor);
				}
			}

			CurrentDir(OldLock);

			UnLock(NewLock);
		}

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

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

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

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

	return(NULL);
}

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

VOID
SendTextFile(STRPTR TheFile)
{
	BYTE OldStatus = Status;

	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())
			{
				SaveProtocolOpts();

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

	if(XProtocolBase)
	{
		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(XProtocolSend(XprIO))
				TransferFailed = FALSE;
			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)
				DeleteTransferPanel(TRUE);
			else
			{
				if(TransferWindow)
				{
					WakeUp(TransferWindow);

					WaitTime(2,0);
				}

				DeleteTransferPanel(FALSE);
			}

			Status = OldStatus;

			if(ReadRequest)
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= ReadBuffer;
				ReadRequest -> IOSer . io_Length	= 1;

				SetSignal(0,SIG_SERIAL);

				BeginIO(ReadRequest);
			}
		}
	}

	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
StartXprReceive(BYTE Type,STRPTR Name,BYTE WaitForIt)
{
	struct FileRequester	*FileRequest;
	UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
	BYTE			 OldStatus = Status;

	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;
	}

	BlockWindows();

		/* Set up the library if necessary. */

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

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

	if(XProtocolBase)
	{
			/* 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(XProtocolReceive(XprIO))
					TransferFailed = FALSE;
				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 && WaitForIt)
					DeleteTransferPanel(TRUE);
				else
				{
					if(TransferWindow)
					{
						WakeUp(TransferWindow);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

					/* Queue another read request. */

				if(ReadRequest)
				{
					ReadRequest -> IOSer . io_Command	= CMD_READ;
					ReadRequest -> IOSer . io_Data		= ReadBuffer;
					ReadRequest -> IOSer . io_Length	= 1;

					SetSignal(0,SIG_SERIAL);

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

					strcpy(DownloadPath,FileRequest -> rf_Dir);

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

					XprIO -> xpr_filename = DummyBuffer;

					FreeAslRequest(FileRequest);
				}
			}
			else
			{
				STRPTR Index;

				strcpy(DownloadPath,Name);

				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(XProtocolReceive(XprIO))
						TransferFailed = FALSE;
					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 && WaitForIt)
						DeleteTransferPanel(TRUE);
					else
					{
						if(TransferWindow)
						{
							WakeUp(TransferWindow);

							WaitTime(2,0);
						}

						DeleteTransferPanel(FALSE);
					}

					Status = OldStatus;

						/* Queue another read
						 * request.
						 */

					if(ReadRequest)
					{
						ReadRequest -> IOSer . io_Command	= CMD_READ;
						ReadRequest -> IOSer . io_Data		= ReadBuffer;
						ReadRequest -> IOSer . io_Length	= 1;

						SetSignal(0,SIG_SERIAL);

						BeginIO(ReadRequest);
					}
				}
			}
		}
	}

	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
StartXprSend(BYTE Type,BYTE WaitForIt)
{
	struct FileRequester	*FileRequest;
	UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
	BYTE			 OldStatus = Status;
	UBYTE			*UploadPath;
	BYTE			 DidSend = TRUE;

	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;
	}

	BlockWindows();

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

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

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

	if(XProtocolBase)
	{
			/* 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(XProtocolSend(XprIO))
					TransferFailed = FALSE;
				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 && WaitForIt)
					DeleteTransferPanel(TRUE);
				else
				{
					if(TransferWindow)
					{
						WakeUp(TransferWindow);

						WaitTime(2,0);
					}

					DeleteTransferPanel(FALSE);
				}

				Status = OldStatus;

					/* And request another character. */

				if(ReadRequest)
				{
					ReadRequest -> IOSer . io_Command	= CMD_READ;
					ReadRequest -> IOSer . io_Data		= ReadBuffer;
					ReadRequest -> IOSer . io_Length	= 1;

					SetSignal(0,SIG_SERIAL);

					BeginIO(ReadRequest);
				}
			}
		}
		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(SendQuery[Type],UploadPath,"",DummyBuffer,"",FALSE,TRUE,FALSE,LocaleString(MSG_TERMTRANSFER_SEND_TXT)))
			{
				strcpy(UploadPath,FileRequest -> rf_Dir);

				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 = FileTransferInfo -> CurrentFile -> Name;

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

						ClearSerial();

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

						if(XProtocolSend(XprIO))
							TransferFailed = FALSE;
						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 && WaitForIt)
							DeleteTransferPanel(TRUE);
						else
						{
							if(TransferWindow)
							{
								WakeUp(TransferWindow);

								WaitTime(2,0);
							}

							DeleteTransferPanel(FALSE);
						}

						Status = OldStatus;

						if(ReadRequest)
						{
							ReadRequest -> IOSer . io_Command	= CMD_READ;
							ReadRequest -> IOSer . io_Data		= ReadBuffer;
							ReadRequest -> IOSer . io_Length	= 1;

							SetSignal(0,SIG_SERIAL);

							BeginIO(ReadRequest);
						}
					}
					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;
		}
	}

	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
StartXprSendFromList(BYTE Type,BYTE WaitForIt)
{
	BYTE	OldStatus	= Status,
		DidSend		= TRUE;

	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;

	BlockWindows();

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

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

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

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

		XprIO -> xpr_filename = FileTransferInfo -> CurrentFile -> Name;

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

			ClearSerial();

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

			if(XProtocolSend(XprIO))
				TransferFailed = FALSE;
			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 && WaitForIt)
				DeleteTransferPanel(TRUE);
			else
			{
				if(TransferWindow)
				{
					WakeUp(TransferWindow);

					WaitTime(2,0);
				}

				DeleteTransferPanel(FALSE);
			}

			Status = OldStatus;

			if(ReadRequest)
			{
				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= ReadBuffer;
				ReadRequest -> IOSer . io_Length	= 1;

				SetSignal(0,SIG_SERIAL);

				BeginIO(ReadRequest);
			}
		}

		if(FileTransferInfo)
		{
			FreeFileTransferInfo(FileTransferInfo);

			FileTransferInfo = NULL;
		}
	}
	else
		DidSend = FALSE;

	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);
}

	/* ASCIISetup():
	 *
	 *	Set up xprascii.library for plain ASCII file
	 *	transfer. This routine temporarily selects a
	 *	different protocol than currently set.
	 */

BYTE
ASCIISetup()
{
	UBYTE	Options[256];
	STRPTR	Index;

		/* Close the currently opened xpr.library. */

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;
	}

		/* Use the same path name as the
		 * default protocol.
		 */

	strcpy(Options,Config -> FileConfig -> ProtocolFileName);

	if(Index = PathPart(Options))
	{
		*Index = 0;

		if(!AddPart(Options,"xprascii.library",MAX_FILENAME_LENGTH))
			strcpy(Options,"xprascii.library");
	}
	else
		strcpy(Options,"xprascii.library");

		/* Open xprascii.library... */

	if(!(XProtocolBase = (struct Library *)OpenLibrary(Options,0)))
		XProtocolBase = (struct Library *)OpenLibrary("xprascii.library",0);

	if(XProtocolBase)
	{
			/* Clear the interface buffer. */

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

			/* Try to obtain the ASCII settings. */

		if(!GetEnvDOS("xprascii",Options))
			Options[0] = 0;

			/* Initialize the interface buffer. */

		XprIO -> xpr_filename	= Options;
		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;

			/* Initialize it. */

		TransferBits = XProtocolSetup(XprIO);

			/* Successful initialization? */

		if(!(TransferBits & XPRS_SUCCESS))
		{
			MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_SET_UP_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),"xprascii.library");

			CloseLibrary(XProtocolBase);

			XProtocolBase = NULL;

			ProtocolSetup();

			return(FALSE);
		}

		if(TransferBits & XPRS_HOSTMON)
			ConTransfer = ConTransferHost;
		else
			ConTransfer = ConProcess;
	}
	else
	{
		MyEasyRequest(Window,LocaleString(MSG_GLOBAL_FAILED_TO_OPEN_PROTOCOL_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT),"xprascii.library");

		ProtocolSetup();

		return(FALSE);
	}

	BinaryTransfer = FALSE;

	return(TRUE);
}

	/* ASCIIShutdown():
	 *
	 *	Close down xprascii.library and reopen the library
	 *	set in the current configuration.
	 */

VOID
ASCIIShutdown()
{
	XProtocolCleanup(XprIO);

	CloseLibrary(XProtocolBase);

	XProtocolBase = NULL;

	BinaryTransfer = TRUE;

	ProtocolSetup();
}

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

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

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

	if(XProtocolBase)
	{
		XProtocolCleanup(XprIO);

		CloseLibrary(XProtocolBase);

		XProtocolBase = NULL;
	}

		/* 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(!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. */

		TransferBits = XProtocolSetup(XprIO);

			/* Successful initialization? */

		if(!(TransferBits & XPRS_SUCCESS))
		{
			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;

			return(FALSE);
		}

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

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

				HostReadBuffer = NULL;
			}
		}
	}
	else
	{
		ConTransfer = ConProcess;

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

	return(TRUE);
}

	/* 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;

		XProtocolSetup(XprIO);

			/* 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);
	}
}

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

BYTE
SelectProtocol(STRPTR Name,struct Window *ParentWindow)
{
	struct FileRequester	*AslFileRequest;
	UBYTE			*File;
	BYTE			 UseNewLibrary = FALSE;

	File = Name;

	if(FilePart(File) == File)
		strcpy(SharedBuffer,"Libs:");
	else
	{
		STRPTR Temp;

		strcpy(SharedBuffer,File);

		Temp = PathPart(SharedBuffer);

		Temp[0] = 0;

		File = FilePart(File);
	}

	if(AslFileRequest = (struct FileRequester *)AllocAslRequestTags(ASL_FileRequest,
		ASL_Window,	ParentWindow,
		ASL_File,	File,
		ASL_Dir,	SharedBuffer,
		ASL_Hail,	LocaleString(MSG_TERMXPR_SELECT_TRANSFER_PROTOCOL_TXT),
		ASL_FuncFlags,	FILF_NEWIDCMP,
		ASL_Pattern,	"xpr#?.library",
		ASL_OKText,	LocaleString(MSG_GLOBAL_SELECT_TXT),
	TAG_END))
	{
		if(AslRequestTags(AslFileRequest,TAG_DONE))
		{
			if(AslFileRequest -> rf_File[0])
			{
				if(Stricmp("Libs:",AslFileRequest -> rf_Dir))
				{
					strcpy(SharedBuffer,AslFileRequest -> rf_Dir);

					if(!AddPart(SharedBuffer,AslFileRequest -> rf_File,MAX_FILENAME_LENGTH))
						strcpy(SharedBuffer,AslFileRequest -> rf_File);
				}
				else
					strcpy(SharedBuffer,AslFileRequest -> rf_File);

				if(SharedBuffer[0] && Stricmp(SharedBuffer,Name))
				{
					strcpy(LastXprLibrary,SharedBuffer);

					UseNewLibrary = TRUE;
				}
			}
		}

		FreeAslRequest(AslFileRequest);
	}

	return(UseNewLibrary);
}

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

VOID
TransferCleanup(VOID)
{
	if(DidTransfer)
	{
		if(TransferFailed)
			DeleteTransferPanel(TRUE);
		else
		{
			if(TransferWindow)
			{
				WakeUp(TransferWindow);

				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)
			DeleteTransferPanel(TRUE);
		else
		{
			if(TransferWindow)
			{
				WakeUp(TransferWindow);

				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))
					{
						Forbid();

						ReleaseSemaphore(&List -> ListSemaphore);

						DeleteGenericListNode(List,Node);

						Permit();

						UnLock(ListLock);

						UnLock(NameLock);

						return;
					}

					UnLock(ListLock);
				}
			}

			Node = Node -> ln_Succ;
		}

		ReleaseSemaphore(&List -> ListSemaphore);

		UnLock(NameLock);
	}
}
