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