You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
705 lines
15 KiB
705 lines
15 KiB
/********************************************************************/
|
|
/** Copyright(c) 1989 Microsoft Corporation. **/
|
|
/********************************************************************/
|
|
|
|
//***
|
|
//
|
|
// Filename: port.c
|
|
//
|
|
// Description: This module contains the entry points for the AppleTalk
|
|
// monitor that manipulate ports.
|
|
//
|
|
// The following are the functions contained in this module.
|
|
// All these functions are exported.
|
|
//
|
|
// OpenPort
|
|
// ClosePort
|
|
// EnumPortsW
|
|
// AddPortW
|
|
// ConfigurePortW
|
|
// DeletePortW
|
|
//
|
|
// History:
|
|
//
|
|
// Aug 26,1992 frankb Initial version
|
|
// June 11,1993. NarenG Bug fixes/clean up
|
|
//
|
|
|
|
#include <windows.h>
|
|
#include <winspool.h>
|
|
#include <winsplp.h>
|
|
#include <winsock.h>
|
|
#include <atalkwsh.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <lmcons.h>
|
|
|
|
#include <prtdefs.h>
|
|
|
|
#include "atalkmon.h"
|
|
#include "atmonmsg.h"
|
|
#include <bltrc.h>
|
|
#include "dialogs.h"
|
|
|
|
|
|
//**
|
|
//
|
|
// Call: AddPort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - False
|
|
//
|
|
// Description:
|
|
// This routine is called when the user selects 'other...'
|
|
// from the port list of the print manager. It presents a browse
|
|
// dialog to the user to allow the user to locate a LaserWriter
|
|
// on the AppleTalk network.
|
|
//
|
|
BOOL
|
|
AddPort(
|
|
IN LPWSTR pName,
|
|
IN HWND hwnd,
|
|
IN LPWSTR pMonitorName
|
|
){
|
|
|
|
PATALKPORT pNewPort;
|
|
PATALKPORT pWalker;
|
|
HANDLE hToken;
|
|
DWORD dwRetCode;
|
|
INT i=0;
|
|
|
|
DBGPRINT(("Entering AddPort\n")) ;
|
|
|
|
//
|
|
// Allocate an initialized port
|
|
//
|
|
|
|
if ( ( pNewPort = AllocAndInitializePort()) == NULL )
|
|
{
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// Set up the query socket. If this fails we assume that it is because
|
|
// the stack is not started and we let the Add Port dialogs bring
|
|
// up the error
|
|
//
|
|
|
|
if ( OpenAndBindAppleTalkSocket( &(pNewPort->sockQuery) ) != NO_ERROR )
|
|
pNewPort->sockQuery = INVALID_SOCKET;
|
|
|
|
|
|
if ( !AddPortDialog( hwnd, pNewPort ) )
|
|
{
|
|
//
|
|
// If the dialog failed for some reason then we just return. The
|
|
// dialog has taken care of displaying an error popup.
|
|
//
|
|
|
|
if ( pNewPort->sockQuery != INVALID_SOCKET )
|
|
{
|
|
closesocket( pNewPort->sockQuery );
|
|
pNewPort->sockQuery = INVALID_SOCKET;
|
|
}
|
|
|
|
FreeAppleTalkPort( pNewPort );
|
|
|
|
DBGPRINT(("AddPortDialog returns not OK\n")) ;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
//
|
|
// Clean up the query socket
|
|
//
|
|
|
|
closesocket( pNewPort->sockQuery );
|
|
pNewPort->sockQuery = INVALID_SOCKET;
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
do {
|
|
|
|
//
|
|
// walk the list and make sure we are not a duplicate
|
|
//
|
|
|
|
dwRetCode = NO_ERROR;
|
|
|
|
for( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( _wcsicmp( pWalker->pPortName, pNewPort->pPortName ) == 0 )
|
|
{
|
|
dwRetCode = ERROR_ALREADY_EXISTS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// check if the key name does not contain "\", else the
|
|
// key name will be broken up over various levels
|
|
// Reject such a name
|
|
//
|
|
i=0;
|
|
while (pNewPort->pPortName[i] != L'\0')
|
|
{
|
|
if (pNewPort->pPortName[i] == L'\\')
|
|
{
|
|
dwRetCode = ERROR_INVALID_PRINTER_NAME;
|
|
DBGPRINT(("sfmmon: AddPort: Detected invalid character in port %ws to be added, rejecting port addition\n", pNewPort->pPortName));
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// add port to registry
|
|
//
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
dwRetCode = CreateRegistryPort( pNewPort );
|
|
|
|
if (hToken)
|
|
{
|
|
if (!ImpersonatePrinterClient( hToken ))
|
|
{
|
|
dwRetCode = ERROR_CANNOT_IMPERSONATE;
|
|
}
|
|
}
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Add port to our list
|
|
//
|
|
|
|
pNewPort->pNext = pPortList;
|
|
pPortList = pNewPort;
|
|
|
|
} while ( FALSE );
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
SetLastError( dwRetCode );
|
|
FreeAppleTalkPort( pNewPort );
|
|
return( FALSE );
|
|
}
|
|
|
|
SetEvent( hevConfigChange );
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: DeletePort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// This routine is called by the print manager to remove
|
|
// a port from our configuration. Need to verify that it can only
|
|
// be called when the port is not active, or we need to resolve
|
|
// the issue of deleting an active port. DeletePort will release
|
|
// the printer if it is captured.
|
|
BOOL
|
|
DeletePort(
|
|
IN LPWSTR pName,
|
|
IN HWND hwnd,
|
|
IN LPWSTR pPortName
|
|
){
|
|
|
|
PATALKPORT pPrevious;
|
|
PATALKPORT pWalker;
|
|
HANDLE hToken;
|
|
DWORD dwRetCode = ERROR_UNKNOWN_PORT;
|
|
|
|
DBGPRINT(("Entering DeletePort\n")) ;
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
for ( pWalker = pPortList, pPrevious = pPortList;
|
|
pWalker != NULL;
|
|
pPrevious = pWalker,
|
|
pWalker = pWalker->pNext )
|
|
{
|
|
|
|
if ( _wcsicmp( pPortName, pWalker->pPortName ) == 0 )
|
|
{
|
|
|
|
if ( pWalker->fPortFlags & SFM_PORT_IN_USE )
|
|
{
|
|
dwRetCode = ERROR_DEVICE_IN_USE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// remove from registry
|
|
//
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
dwRetCode = RegDeleteKey( hkeyPorts, pPortName );
|
|
|
|
if (hToken)
|
|
{
|
|
if (!ImpersonatePrinterClient( hToken ))
|
|
{
|
|
dwRetCode = ERROR_CANNOT_IMPERSONATE;
|
|
}
|
|
}
|
|
|
|
if ( dwRetCode != ERROR_SUCCESS )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remove from active list
|
|
//
|
|
|
|
if ( pWalker == pPortList )
|
|
pPortList = pPortList->pNext;
|
|
else
|
|
pPrevious->pNext = pWalker->pNext;
|
|
|
|
//
|
|
// Put it in the delete list
|
|
//
|
|
|
|
WaitForSingleObject( hmutexDeleteList, INFINITE );
|
|
|
|
pWalker->pNext = pDeleteList;
|
|
pDeleteList = pWalker;
|
|
|
|
ReleaseMutex( hmutexDeleteList );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
SetLastError( dwRetCode );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
SetEvent( hevConfigChange );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: EnumPorts
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// EnumPorts is called by the print manager to get
|
|
// information about all configured ports for the monitor.
|
|
BOOL
|
|
EnumPorts(
|
|
IN LPWSTR pName,
|
|
IN DWORD dwLevel,
|
|
IN LPBYTE pPorts,
|
|
IN DWORD cbBuf,
|
|
OUT LPDWORD pcbNeeded,
|
|
OUT PDWORD pcReturned
|
|
)
|
|
{
|
|
|
|
PATALKPORT pWalker;
|
|
LPWSTR pNames;
|
|
|
|
*pcReturned = 0;
|
|
*pcbNeeded = 0;
|
|
|
|
//
|
|
// validate parameters
|
|
//
|
|
|
|
if ( dwLevel != 1 && dwLevel != 2 )
|
|
{
|
|
SetLastError( ERROR_INVALID_LEVEL );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// get size needed
|
|
//
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
for ( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( dwLevel == 1 )
|
|
{
|
|
*pcbNeeded += ((sizeof(WCHAR) * (wcslen(pWalker->pPortName) + 1))
|
|
+ sizeof(PORT_INFO_1));
|
|
}
|
|
else // if ( dwLevel == 2 )
|
|
{
|
|
*pcbNeeded += ((sizeof(WCHAR) * (wcslen(pWalker->pPortName) + 1))
|
|
+ sizeof(WCHAR) * (wcslen (wchPortDescription) + 1)+
|
|
+ sizeof(WCHAR) * (wcslen (wchDllName) + 1) +
|
|
+ sizeof (PORT_INFO_2));
|
|
}
|
|
}
|
|
|
|
DBGPRINT(("buffer size needed=%d\n", *pcbNeeded)) ;
|
|
|
|
//
|
|
// if buffer too small, return error
|
|
//
|
|
|
|
if ( ( *pcbNeeded > cbBuf ) || ( pPorts == NULL ))
|
|
{
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
DBGPRINT(("insufficient buffer\n"));
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// fill the buffer
|
|
//
|
|
|
|
DBGPRINT(("attempting to copy to buffer\n")) ;
|
|
|
|
for ( pWalker = pPortList, pNames = (LPWSTR)(pPorts+cbBuf);
|
|
pWalker != NULL;
|
|
pWalker = pWalker->pNext )
|
|
{
|
|
|
|
if ( dwLevel == 1)
|
|
{
|
|
DWORD dwLen;
|
|
PPORT_INFO_1 pPortInfo1 = (PPORT_INFO_1)pPorts;
|
|
|
|
DBGPRINT(("copying %ws\n", pWalker->pPortName)) ;
|
|
|
|
#if 0
|
|
pNames -= ( wcslen( pWalker->pPortName ) + 1 );
|
|
wcscpy( (LPWSTR)pNames, pWalker->pPortName );
|
|
pPortInfo->pName = pNames;
|
|
pPorts += sizeof (PORT_INFO_1);
|
|
#endif
|
|
dwLen = wcslen (pWalker->pPortName) + 1;
|
|
pNames -= dwLen;
|
|
pPortInfo1->pName = pNames;
|
|
wcscpy (pPortInfo1->pName, pWalker->pPortName);
|
|
pPorts += sizeof (PORT_INFO_1);
|
|
}
|
|
else // if dwLevel == 2
|
|
{
|
|
DWORD dwLen;
|
|
PPORT_INFO_1 pPortInfo1 = (LPPORT_INFO_1)pPorts;
|
|
PPORT_INFO_2 pPortInfo2 = (LPPORT_INFO_2)pPorts;
|
|
|
|
dwLen = wcslen (wchDllName) + 1;
|
|
pNames -= dwLen;
|
|
pPortInfo2->pMonitorName = (LPWSTR)pNames;
|
|
wcscpy (pPortInfo2->pMonitorName, (LPWSTR)wchDllName);
|
|
|
|
dwLen = wcslen (wchPortDescription) + 1;
|
|
pNames -= dwLen;
|
|
pPortInfo2->pDescription = (LPWSTR)pNames;
|
|
wcscpy (pPortInfo2->pDescription, (LPWSTR)wchPortDescription);
|
|
|
|
dwLen = wcslen (pWalker->pPortName) + 1;
|
|
pNames -= dwLen;
|
|
pPortInfo1->pName = (LPWSTR)pNames;
|
|
wcscpy(pPortInfo1->pName, pWalker->pPortName);
|
|
|
|
pPorts += sizeof (PORT_INFO_2);
|
|
}
|
|
|
|
|
|
(*pcReturned)++;
|
|
}
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: OpenPort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// This routine is called by the print manager to
|
|
// get a handle for a port to be used in subsequent calls
|
|
// to read and write data to the port. It opens an AppleTalk
|
|
// Address on the server for use in establishing connections
|
|
// when a job is sent to print. It looks like the NT Print
|
|
// Spooler only calls OpenPort once.
|
|
//
|
|
// NOTE: In order to allow for the AppleTalk stack to be turned off
|
|
// while printing is not happening, OpenPort will not go to the
|
|
// stack. Instead, it will just validate the parameters and
|
|
// return a handle. The stack will be accessed on StartDocPort.
|
|
//
|
|
// OpenPort is called whenever a port becomes configured to
|
|
// be used by one or more NT Printers. We use this fact to recognize
|
|
// when we need to start capturing the printer. This routine sets
|
|
// the port state to open and then kicks off a config event to
|
|
// capture or release it.
|
|
//
|
|
BOOL
|
|
OpenPort(
|
|
IN LPWSTR pName,
|
|
IN PHANDLE pHandle
|
|
){
|
|
|
|
PATALKPORT pWalker;
|
|
|
|
DBGPRINT(("Entering OpenPort\n")) ;
|
|
|
|
//
|
|
// find the printer in our list
|
|
//
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
for ( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( _wcsicmp( pWalker->pPortName, pName ) == 0 )
|
|
{
|
|
pWalker->fPortFlags |= SFM_PORT_OPEN;
|
|
pWalker->fPortFlags &= ~SFM_PORT_CLOSE_PENDING;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( pWalker == NULL )
|
|
{
|
|
SetLastError( ERROR_UNKNOWN_PORT );
|
|
|
|
DBGPRINT(("ERROR: Could not find printer %ws\n", pName)) ;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
SetEvent( hevConfigChange );
|
|
|
|
*pHandle = pWalker;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: ClosePort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
// This routine is called to release the handle to
|
|
// the open port. It looks like the spooler only calls
|
|
// ClosePort prior to deleting a port (maybe). Otherwise,
|
|
// ports are never closed by the spooler.
|
|
//
|
|
// This routine simply cleans up the handle and returns.
|
|
//
|
|
// When the NT spooler recognizes that no printers are configured
|
|
// to use a port, it calls ClosePort(). We mark the port status as
|
|
// closed, and release the printer if it is captured.
|
|
//
|
|
BOOL
|
|
ClosePort(
|
|
IN HANDLE hPort
|
|
){
|
|
|
|
PATALKPORT pPort = (PATALKPORT)hPort;
|
|
PATALKPORT pWalker;
|
|
DWORD dwRetCode = ERROR_UNKNOWN_PORT;
|
|
|
|
DBGPRINT(("Entering ClosePort\n"));
|
|
|
|
if ( pPort == NULL )
|
|
{
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
|
|
DBGPRINT(("ERROR: ClosePort on closed handle\n")) ;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// find the printer in our list
|
|
//
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
for ( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( _wcsicmp( pWalker->pPortName, pPort->pPortName ) == 0 )
|
|
{
|
|
if ( pWalker->fPortFlags & SFM_PORT_IN_USE )
|
|
dwRetCode = ERROR_BUSY;
|
|
else
|
|
{
|
|
pWalker->fPortFlags &= ~SFM_PORT_OPEN;
|
|
pWalker->fPortFlags |= SFM_PORT_CLOSE_PENDING;
|
|
pWalker->fPortFlags &= ~SFM_PORT_CAPTURED;
|
|
dwRetCode = NO_ERROR;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
SetLastError( dwRetCode );
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
SetEvent( hevConfigChange );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
//**
|
|
//
|
|
// Call: ConfigurePort
|
|
//
|
|
// Returns: TRUE - Success
|
|
// FALSE - Failure
|
|
//
|
|
// Description:
|
|
//
|
|
BOOL
|
|
ConfigurePort(
|
|
IN LPWSTR pName,
|
|
IN HWND hwnd,
|
|
IN LPWSTR pPortName
|
|
){
|
|
|
|
DWORD dwRetCode;
|
|
HANDLE hToken;
|
|
BOOL fCapture;
|
|
BOOL fIsSpooler;
|
|
PATALKPORT pWalker;
|
|
|
|
DBGPRINT(("Entering ConfigurePort\n")) ;
|
|
|
|
//
|
|
// find the port structure
|
|
//
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
for ( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( _wcsicmp( pPortName, pWalker->pPortName ) == 0 )
|
|
{
|
|
fCapture = pWalker->fPortFlags & SFM_PORT_CAPTURED;
|
|
fIsSpooler = pWalker->fPortFlags & SFM_PORT_IS_SPOOLER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( pWalker == NULL )
|
|
{
|
|
DBGPRINT(("ERROR: port not found\n")) ;
|
|
SetLastError( ERROR_UNKNOWN_PORT );
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// configure the port. If there was any error in the dialog, it would
|
|
// have been displayed already.
|
|
//
|
|
|
|
if ( !ConfigPortDialog( hwnd, fIsSpooler, &fCapture ) )
|
|
return( TRUE );
|
|
|
|
WaitForSingleObject( hmutexPortList, INFINITE );
|
|
|
|
do {
|
|
|
|
for ( pWalker = pPortList; pWalker != NULL; pWalker = pWalker->pNext )
|
|
{
|
|
if ( _wcsicmp( pPortName, pWalker->pPortName ) == 0 )
|
|
break;
|
|
}
|
|
|
|
if ( pWalker == NULL )
|
|
{
|
|
dwRetCode = ERROR_UNKNOWN_PORT;
|
|
break;
|
|
}
|
|
|
|
if ( fCapture )
|
|
pWalker->fPortFlags |= SFM_PORT_CAPTURED;
|
|
else
|
|
pWalker->fPortFlags &= ~SFM_PORT_CAPTURED;
|
|
|
|
//
|
|
// save changes to registry
|
|
//
|
|
|
|
hToken = RevertToPrinterSelf();
|
|
|
|
dwRetCode = SetRegistryInfo( pWalker );
|
|
|
|
if (hToken)
|
|
{
|
|
if (!ImpersonatePrinterClient( hToken ))
|
|
{
|
|
dwRetCode = ERROR_CANNOT_IMPERSONATE;
|
|
}
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
ReleaseMutex( hmutexPortList );
|
|
|
|
if ( dwRetCode != NO_ERROR )
|
|
{
|
|
SetLastError( dwRetCode );
|
|
return( FALSE );
|
|
}
|
|
|
|
SetEvent( hevConfigChange );
|
|
|
|
return( TRUE );
|
|
}
|
|
|