Metropoli BBS
VIEWER: tapapp.c MODE: TEXT (ASCII)
//
// TAPAPP.C
//
// TAP
// File Transfer Data Sharing
// Application Code
// Revision 1.10
//
// 12/28/94   First created
//  4/24/95   Structures aligned with DWORDS
//            Dynamic extension lists added
//            Fixed EMERGENCY_CLOSE to use bitwise NOT (~)
//
#define INCL_DOSERRORS
#define INCL_WINSHELLDATA
#define INCL_DOS

#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <string.h>
#include <os2.h>
#include "tapapp.h"

//
// PTAPAPPENTRY BuildTapAppEntry_TAP(char *szDescription,
//                                  char *szProgram,
//                                  char *szParams,
//                                  PSZ pszExtension[],
//                                  ULONG ulNumExtensions);
//
// This function allocates and builds a TAPAPPENTRY
// that should be used to register a TAP application with
// the TAP servers.
//
// Parameters
// szDescription
//   Description, up to 255 characters, of the application.
// szProgram
//   Fully qualified path to the TAP executable.
// szParams
//   Parameters to pass to the TAP program (max: 83 characters)
// pszExtension
//   A pointer to an array of pointers to strings, each
//   string containing an file extension this application
//   supports.
// ulNumExtensions
//   Number of extensions the application can handle.
//   If this is zero, pszExtension may be NULL.
//
// Returns
//   A pointer to a TAPAPPENTRY.
//
//   WARNING: The CALLER (that's you) is responsible for
//            freeing this structure after using it.
//            This can be done by calling the standard
//            C function free.
//
PTAPAPPENTRY BuildTapAppEntry_TAP(char *szDescription,
                                  char *szProgram,
                                  char *szParams,
                                  PSZ pszExtension[],
                                  ULONG ulNumExtensions)
{
PTAPAPPENTRY pTapAppEntry;
PEXTENSIONLIST pExtList;
ULONG i, ulSize;

   // Compute the size of the TAPAPPENTRY structure with
   // the extension list appended
   for (i = 0, ulSize = sizeof(TAPAPPENTRY); i < ulNumExtensions; i++)
      ulSize += sizeof(((PEXTENSIONLIST)NULL)->cb) +
			       sizeof(((PEXTENSIONLIST)NULL)->szExtension) +
					 strlen(pszExtension[i]);

   // Allocate a TAPAPPENTRY structure
   pTapAppEntry = (PTAPAPPENTRY) malloc (ulSize);

   // Fill in the TAPAPPENTRY structure
   pTapAppEntry->cb = ulSize;
   strcpy(pTapAppEntry -> szDescription, szDescription);
   strcpy(pTapAppEntry -> szProgram, szProgram);
   strcpy(pTapAppEntry -> szParams, szParams);
   pTapAppEntry->ulExtensions = ulNumExtensions;

   // Set pExtList to point to the memory location immediately
   // following the TAPAPPENTRY structure
   pExtList = (PEXTENSIONLIST) ((PBYTE)pTapAppEntry + sizeof(TAPAPPENTRY));

   // Fill the memory after TAPPAPPENTRY with variable size EXTENSIONLISTs
   for (i = 0; i < ulNumExtensions; i++)
   {
      pExtList -> cb = sizeof(((PEXTENSIONLIST)NULL)->cb) +
			              sizeof(((PEXTENSIONLIST)NULL)->szExtension) +
							  strlen(pszExtension[i]);
      strcpy(pExtList->szExtension, pszExtension[i]);

      // Go to the next EXTENSIONLIST structure
      pExtList = (PEXTENSIONLIST) ((PBYTE)pExtList + pExtList -> cb);
   }

   // Warning, the caller must free this up.
   return pTapAppEntry;
}

//
// int RegisterApplication_TAP(char *szAppName,
//										 PTAPAPPENTRY pTapAppEntry);
//
// Registers a TAP Application so that TAP Servers can
// find it. This need only be done once.
//
// Parameters:
// szAppName
//   Name of the TAP Application
// pTapAppEntry
//   A pointer to a structure containing
//   information about this TAP application.
//   See TAP.H for the members of this
//   structure. The TAP application is
//	  responsible for filling this out
//   completely and correctly.
//
// Returns: TRUE on success
//
int RegisterApplication_TAP(char *szAppName,
									 PTAPAPPENTRY pTapAppEntry)
{
int iRet = TRUE;

	// Write application data to the INI file
	iRet =
	(INT)PrfWriteProfileData(HINI_SYSTEMPROFILE,
								TAP_INI_APPNAME,
								szAppName,
								(PVOID) pTapAppEntry,
								pTapAppEntry -> cb);

	return iRet;
}

//
// int DeRegisterApplication_TAP(char *szAppName);
//
// Deregisters a TAP Application so that it is unavailable
// to TAP Servers.
//
// Parameters:
// szAppName
//   Name of the TAP Application
//
// Returns: TRUE on success
//
int DeRegisterApplication_TAP(char *szAppName)
{
int iRet = TRUE;

	// Remove application from the INI file
	iRet =
	(INT)PrfWriteProfileData(HINI_SYSTEMPROFILE,
								TAP_INI_APPNAME,
								szAppName,
								NULL,
								0);

	return iRet;
}

//
// int OpenFile_TAP(PTAPAPPINFO pTapAppInfo, PHFILE phFile);
//
// Opens the current file being transferred for
// shared reading.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
// phFile
//  Pointer to an OS/2 file handle. (output)
//
// Returns: TRUE on success
//
int OpenFile_TAP(PTAPAPPINFO pTapAppInfo, PHFILE phFile)
{
APIRET rc;
ULONG ActionTaken;
int iRet = TRUE;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	//
	// Open an already existing file for read only access
	//
	// As a child process we really already have an
	// inherited file handle but we're ignoring this fact
	//
	rc = DosOpen(pTapAppInfo -> tiCurrent . szFileName,
						&(pTapAppInfo -> hUserFile),
						&ActionTaken,
						0,
						FILE_NORMAL,
						OPEN_ACTION_FAIL_IF_NEW |
						OPEN_ACTION_OPEN_IF_EXISTS,
						OPEN_SHARE_DENYNONE |
						OPEN_ACCESS_READONLY,
						NULL);

	if (rc)
		iRet = FALSE;
	else
		// Success, return file handle
		*phFile = pTapAppInfo -> hUserFile;

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return iRet;
}

//
// int CloseFile_TAP(PTAPAPPINFO pTapAppInfo, HFILE hFile)
//
// Closes a the current file opened with OpenFile_TAP.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
// hFile
//  OS/2 handle to the file to be closed.
//
// Returns: TRUE on success
//
int CloseFile_TAP(PTAPAPPINFO pTapAppInfo, HFILE hFile)
{
int iRet = TRUE;
APIRET rc;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Close file
	rc = DosClose(hFile);

	if (rc)
		iRet = FALSE;

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return iRet;
}

//
// PTAPAPPINFO InitializeApplication_TAP(int argc, char **argv);
//
// Initializes the TAP subsystem. This function must be called
// as one of the first functions called in the TAP application.
//
// Parameters:
// argc & argv - Standard command line parameters passed
// to main on startup
//
// Returns: A pointer to the TAP instance data on success,
// otherwise NULL. A NULL return value most likely means this
// program was NOT started from a TAP server as required.
//
PTAPAPPINFO InitializeApplication_TAP(int argc, char **argv)
{
PTAPAPPINFO pTapAppInfo = NULL;
int cnter;
char szPipeName[CCHMAXPATH];
HFILE PipeHandle;
ULONG ActionTaken;
APIRET rc;

	// Parse pipe name from the command line
	for(cnter=0; cnter<argc; cnter++)
		if (!strncmp(argv[cnter], "/TAP=", 5))
			strcpy(szPipeName, (argv[cnter] + 5));

	// Open named pipe
	rc = DosOpen(szPipeName,
					  &PipeHandle,
					  &ActionTaken,
					  0L,
					  FILE_NORMAL,
					  FILE_OPEN,
					  OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE,
					  (PEAOP2) NULL);

	// Allocate memory
	if (!rc)
		pTapAppInfo = (PTAPAPPINFO) malloc(sizeof(TAPAPPINFO));

	if (pTapAppInfo)
	{
		// Initialize TAPAPPINFO structure
		pTapAppInfo -> cb = sizeof(TAPAPPINFO);
		pTapAppInfo -> lShutdown = FALSE;
		pTapAppInfo -> hReadPipe = (HFILE) PipeHandle;

		// Create mutex semaphore
		DosCreateMutexSem(NULL,
								&(pTapAppInfo -> hAppMutex),
								0,
								FALSE);

		// Initialize version info

		pTapAppInfo -> szVersion[0] = '\0';
		pTapAppInfo -> lVersionAvailable = FALSE;

		// Initialize TAPINFO structures
		pTapAppInfo -> tiCurrent . szFileName [0] = '\0';
		pTapAppInfo -> tiCurrent . lCurrentFileSize = TAP_SIZE_UNKNOWN;
		pTapAppInfo -> tiCurrent . lCompleteFileSize = TAP_SIZE_UNKNOWN;
		pTapAppInfo -> tiCurrent . ulFlags = 0;

		pTapAppInfo -> tiNext . szFileName [0] = '\0';
		pTapAppInfo -> tiNext . lCurrentFileSize = TAP_SIZE_UNKNOWN;
		pTapAppInfo -> tiNext . lCompleteFileSize = TAP_SIZE_UNKNOWN;
		pTapAppInfo -> tiNext . ulFlags = 0;

		// Start TAP Application thread
      #ifdef __BORLANDC__
		_beginthread(ApplicationThread_TAP,
							8192,
							(void *) pTapAppInfo);
      #else
		_beginthread(ApplicationThread_TAP,
							NULL,
							8192,
							(void *) pTapAppInfo);
      #endif
	}

	return pTapAppInfo;
}

//
// int DeInitializeApplication_TAP(PTAPAPPINFO pTapAppInfo);
//
// Deinitializes the TAP subsystem.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: TRUE on success
//
int DeInitializeApplication_TAP(PTAPAPPINFO pTapAppInfo)
{
int iRet = TRUE;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Tell the application thread we want to shut down
	pTapAppInfo -> lShutdown = TRUE;

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return iRet;
}

//
// int NextFile_TAP(PTAPAPPINFO pTapAppInfo);
//
// Waits for the next file in the transfer session
// or a cancel or an end of batch condition.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: TRUE if another file transfer has started.
// FALSE if a cancel or end of batch condition has
// been encountered.
//
int NextFile_TAP(PTAPAPPINFO pTapAppInfo)
{
int iRet = TRUE;
int ExitCondition = FALSE;

	do
	{
		// Request exclusive access
		DosRequestMutexSem(pTapAppInfo -> hAppMutex,
									(ULONG)SEM_INDEFINITE_WAIT);

		// Check if the next packet begins a new file
		ExitCondition = pTapAppInfo -> tiNext.ulFlags & TAP_BOF ? TRUE : FALSE;

		// Special condition: if the TAP applicationstarted at this middle
		// of a file, we get no BOF (Begin of File) flag. To detect this
		// condition we check if the current filename is the null string
		// while the next filename is the non-empty string
		if ( (pTapAppInfo -> tiCurrent.szFileName [0] == 0) &&
			  (pTapAppInfo -> tiNext.szFileName [0] != 0) )
			ExitCondition = TRUE;

		// Check for exit conditions
		if ( (pTapAppInfo -> tiNext.ulFlags & TAP_CANCEL) ||
			  (pTapAppInfo -> tiNext.ulFlags & TAP_EOB) )
		{
			iRet = FALSE;
			ExitCondition = TRUE;
		}

		// If we're going to exit, update the current data packet
		if (ExitCondition)
		{
			pTapAppInfo -> tiCurrent = pTapAppInfo -> tiNext;
			pTapAppInfo -> tiNext . ulFlags = 0;
		}

		// Release exclusive access
		DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

		// Wait for 1/4 sec if this loop will repeat
		if (!ExitCondition)
			DosSleep(250);

	} while (!ExitCondition);

	return iRet;
}


//
// int MoreData_TAP(PTAPAPPINFO pTapAppInfo);
//
// Waits for more data to arrive or one of
// the following conditions to occur:
// CANCEL, END OF BATCH, BEGIN NEW FILE, EOF
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: TRUE if more data has been received.
// FALSE if any other condition has occured.
//
// If FALSE is returned there will be no more data
// for the current file. Recommended action is to
// check the file size and process any still
// unprocessed data, then call NextFile_TAP
// to determine if there are any more files to
// process.
//
int MoreData_TAP(PTAPAPPINFO pTapAppInfo)
{
int iRet = TRUE;
int ExitCondition = FALSE;

	do
	{
		// Request exclusive access
		DosRequestMutexSem(pTapAppInfo -> hAppMutex,
									(ULONG)SEM_INDEFINITE_WAIT);

		// Check for exit conditions that pertain to
		// batch conditions (End of batch, cancel)
		// that should be handled by NextFile
		if ( (pTapAppInfo -> tiNext.ulFlags & TAP_CANCEL) ||
			  (pTapAppInfo -> tiNext.ulFlags & TAP_EOB)    ||
			  (pTapAppInfo -> tiNext.ulFlags & TAP_BOF) )
		{
			iRet = FALSE;
			ExitCondition = TRUE;
		}
		 else
		// Check for exit conditions we need
		// to handle here (pertain to the file)
		if (pTapAppInfo -> tiNext.ulFlags & TAP_EOF)
		{
			iRet = FALSE;
			ExitCondition = TRUE;
			pTapAppInfo -> tiNext . ulFlags = 0;
		}
		 else
		// Check if the next packet has new data
		if (pTapAppInfo -> tiNext.ulFlags & TAP_NEW_SIZE)
		{
			ExitCondition = TRUE;
			pTapAppInfo -> tiNext . ulFlags = 0;
		}

		if (ExitCondition)
			pTapAppInfo -> tiCurrent = pTapAppInfo -> tiNext;

		// Release exclusive access
		DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

		// Wait for 1/4 sec if this loop will repeat
		if (!ExitCondition)
			DosSleep(250);

	} while (!ExitCondition);

	return iRet;
}

//
// long QueryStatus_TAP(PTAPAPPINFO pTapAppInfo);
//
// Queries the status of the current file.
// This really isn't very useful.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: Bitmapped flags
//
long QueryStatus_TAP(PTAPAPPINFO pTapAppInfo)
{
long lRet = 0;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Return current TAP flags
	lRet = (LONG)(pTapAppInfo -> tiCurrent . ulFlags);

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return lRet;
}

//
// int QueryServerVersion_TAP(PTAPAPPINFO pTapAppInfo,
//									char *szVersion);
//
// Queries the server version string. This function
// returns a valid server version string only when it
// has received at least one packet of information
// over the named pipe. It's therefore recommended
// that this not be used until the first call to
// NextFile_TAP returns.
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
// szVersion
//  Pointer to a memory location of at least 31 bytes
//  to hold the server version string (output)
//
// Returns: TRUE on success
//
int QueryServerVersion_TAP(PTAPAPPINFO pTapAppInfo,
									char *szVersion,
									ULONG ulBufLen)
{
int iRet = TRUE;
	ULONG ulRetries = 10; // Max 10 retries = 2.5 sec

	for (;;) {

		// Request exclusive access
		DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

		if (pTapAppInfo -> lVersionAvailable)
			break;

		DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

		if (ulRetries-- == 0)	
			return (FALSE);

		DosSleep (250);
		}

	// Make a copy of the server version string
	strncpy (szVersion, pTapAppInfo -> szVersion, ulBufLen - 1);
	szVersion[ulBufLen - 1] = '\0';

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return iRet;
}

//
// int QueryFileName_TAP(PTAPAPPINFO pTapAppInfo,
//									char *szFileName);
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
// szFileName
//  Pointer to a memory block of CCHMAXPATH length
//  where the current fully qualified path and file name
//  is copied. (output)
//
// Returns: TRUE on success;
//
int QueryFileName_TAP(PTAPAPPINFO pTapAppInfo,
								char *szFileName)
{
int iRet = TRUE;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Make a copy of the fully qualified file name
	strcpy(szFileName, pTapAppInfo -> tiCurrent . szFileName);

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return iRet;
}

//
// long QueryCompleteSize_TAP((PTAPAPPINFO pTapAppInfo);
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: The size of the complete file or -1 (TAP_SIZE_UNKNOWN)
// if that size is unknown.
//
long QueryCompleteSize_TAP(PTAPAPPINFO pTapAppInfo)
{
long lRet = TRUE;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Return complete size
	lRet = pTapAppInfo -> tiCurrent . lCompleteFileSize;

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return lRet;
}

//
// long QueryCurrentSize_TAP((PTAPAPPINFO pTapAppInfo);
//
// Parameters:
// pTapAppInfo
//  Pointer to the TAP application's instance data.
//
// Returns: The size of the current file or -1 (TAP_SIZE_UNKNOWN)
// if that size is unknown.
//
long QueryCurrentSize_TAP(PTAPAPPINFO pTapAppInfo)
{
long lRet = TRUE;

	// Request exclusive access
	DosRequestMutexSem(pTapAppInfo -> hAppMutex,
								(ULONG)SEM_INDEFINITE_WAIT);

	// Return current size
	lRet = pTapAppInfo -> tiCurrent . lCurrentFileSize;

	// Release exclusive access
	DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

	return lRet;
}

//
// void ApplicationThread_TAP(void *AppInfo)
//
// -= NOT to be called by any other module =-
//
// This is a thread function which serves
// the purpose of servicing the queue and
// updating data structures accordingly.
//
// This function also handles emergency file
// closes.
//
void ApplicationThread_TAP (void *AppInfo) {
	PTAPAPPINFO pTapAppInfo = (PTAPAPPINFO)AppInfo;

	while (!pTapAppInfo->lShutdown) {
		USHORT	  usPacketSize = 0;	
		ULONG 	  ulBytesRead;
		PTAPPACKET pTapPacket;

		for (;;) {
			AVAILDATA availData;
			ULONG     ulState;

			DosPeekNPipe (pTapAppInfo->hReadPipe, &usPacketSize, sizeof (usPacketSize), &ulBytesRead, &availData, &ulState);

			// Have we got the whole packet yet ?

			if ((ulBytesRead == sizeof (usPacketSize)) && (availData.cbpipe >= usPacketSize))
				break;

			// Shutdown on request or if pipe is no longer connected

			if ((pTapAppInfo->lShutdown) || (ulState != NP_STATE_CONNECTED))
				goto _ShutDown;

			// Not enough data yet, sleep for 1/4 second

			DosSleep (250);
			}

		// Read packet

		pTapPacket = (PTAPPACKET)malloc (usPacketSize);

		DosRead(pTapAppInfo->hReadPipe, pTapPacket, usPacketSize, &ulBytesRead);

		if (ulBytesRead == usPacketSize) {
			ULONG ulFlags = pTapPacket->flagPacket.usFlags;

			if (ulFlags & TAP_EMERGENCY_CLOSE) {
				// Close up file ASAP
				DosClose (pTapAppInfo -> hUserFile);
				// Reset emergency flag, it has been handled
				ulFlags &= ~(ULONG)TAP_EMERGENCY_CLOSE;
				}

			// Request exclusive access
			DosRequestMutexSem (pTapAppInfo->hAppMutex, (ULONG)SEM_INDEFINITE_WAIT);

			if (ulFlags & TAP_VERSION) {
				LONG lLen = min (sizeof (pTapAppInfo->szVersion) - 1, pTapPacket->versionPacket.usVersionLength);
				strncpy (pTapAppInfo->szVersion, pTapPacket->versionPacket.szVersion, (UINT)lLen);
				pTapAppInfo->szVersion[lLen] = '\0';
				pTapAppInfo -> lVersionAvailable = TRUE;
				}
			else if (ulFlags & TAP_BOF) {
				LONG lLen;
				pTapAppInfo->tiNext.lCurrentFileSize = pTapPacket->beginFilePacket.lCurrentFileSize;
				pTapAppInfo->tiNext.lCompleteFileSize = pTapPacket->beginFilePacket.lCompleteFileSize;
				lLen = min (sizeof (pTapAppInfo->tiNext.szFileName) - 1, pTapPacket->beginFilePacket.usFileNameLength);
				strncpy (pTapAppInfo->tiNext.szFileName, pTapPacket->beginFilePacket.szFileName, (UINT)lLen);
				pTapAppInfo->tiNext.szFileName[lLen] = '\0';
				}
			else if (ulFlags & TAP_NEW_SIZE) {
				pTapAppInfo->tiNext.lCurrentFileSize = pTapPacket->newSizePacket.lCurrentFileSize;
				pTapAppInfo->tiNext.lCompleteFileSize = pTapPacket->newSizePacket.lCompleteFileSize;
				}

			// Acknowledge server if requested
			if (ulFlags & TAP_ACKNOWLEDGE) {
				ULONG ulBytesWritten;
				pTapPacket->flagPacket.cb = sizeof (pTapPacket->flagPacket);
				pTapPacket->flagPacket.usFlags = TAP_ACKNOWLEDGE;
				DosWrite (pTapAppInfo->hReadPipe, pTapPacket, pTapPacket->flagPacket.cb, &ulBytesWritten);
				}

			pTapAppInfo->tiNext.ulFlags |= ulFlags;

			// Release exclusive access
			DosReleaseMutexSem(pTapAppInfo -> hAppMutex);

			}

		free (pTapPacket);
		}

_ShutDown:

	// ***
	// Shutdown
	// ***

	// Free mutex
	DosCloseMutexSem(pTapAppInfo -> hAppMutex);

	// Shut down pipe
	DosClose(pTapAppInfo -> hReadPipe);

	// Free memory
	free (pTapAppInfo);
}

[ RETURN TO DIRECTORY ]