/********************************************************************/ /** 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 #include #include #include #include #include #include #include #include #include #include "atalkmon.h" #include "atmonmsg.h" #include #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 ); }