|
|
/*****************************************************************************\
* MODULE: msw3prt.cxx * * The module contains routines to implement the ISAPI interface. * * PURPOSE Windows HTTP/HTML printer interface * * Copyright (C) 1996-1997 Microsoft Corporation * * History: * 01/16/96 eriksn Created based on ISAPI sample DLL * 07/15/96 babakj Ported to NT * 02/04/97 weihaic Enabled Unicode mode * \*****************************************************************************/
#include "pch.h"
/*****************************************************************************\
* GetClientInfo * * Returns a DWORD representation of the client architecture/ver information. * \*****************************************************************************/ DWORD GetClientInfo( PALLINFO pAllInfo) { DWORD dwCliInfo = 0; LPSTR lpszPtr;
if (pAllInfo->pECB->cbAvailable) { //
// First Allocate a new buffer on the heap to hold the input string
//
lpszPtr = (LPSTR) LocalAlloc(LPTR, (pAllInfo->pECB->cbAvailable+1));
if (lpszPtr) { //
// Copy the memory over and then null terminate past the message
//
memcpy( lpszPtr, pAllInfo->pECB->lpbData, pAllInfo->pECB->cbAvailable); lpszPtr[pAllInfo->pECB->cbAvailable] = 0x00;
while (*lpszPtr && (*lpszPtr != '=')) lpszPtr++;
if (*lpszPtr) dwCliInfo = atoi(++lpszPtr);
LocalFree(lpszPtr); } } else { if (lpszPtr = pAllInfo->pECB->lpszQueryString) { while (*lpszPtr && (*lpszPtr != '&')) lpszPtr++;
if (*lpszPtr) dwCliInfo = atoi(++lpszPtr); } }
return dwCliInfo; }
/*****************************************************************************\
* GetIppReq * * Returns the request-type of the IPP-stream. * \*****************************************************************************/ WORD GetIppReq( PALLINFO pAllInfo) { LPWORD pwPtr; WORD wValue = 0;
if (pAllInfo->pECB->cbAvailable >= sizeof(DWORD)) {
if (pwPtr = (LPWORD)pAllInfo->pECB->lpbData) {
CopyMemory (&wValue, pwPtr + 1, sizeof (WORD));
return ntohs(wValue); } }
return 0; }
/*****************************************************************************\
* IsSecureReq * * Returns whether the request comes from a secure https channel. * \*****************************************************************************/ BOOL IsSecureReq( EXTENSION_CONTROL_BLOCK *pECB) { BOOL bRet; DWORD cbBuf; CHAR szBuf[10];
cbBuf = 10; bRet = pECB->GetServerVariable(pECB->ConnID, "HTTPS", &szBuf, &cbBuf);
if (bRet && (cbBuf <= 4)) {
if (lstrcmpiA(szBuf, "on") == 0) return TRUE; }
return FALSE; }
/*****************************************************************************\
* GetPrinterName * * Get the printer name from the path * \*****************************************************************************/ LPTSTR GetPrinterName (LPTSTR lpszPathInfo) { // The only format we support is "/printers/ShareName|Encoded Printer Name/.printer"
static TCHAR szPrinter[] = TEXT ("/.printer"); static TCHAR szPrinters[] = TEXT ("/printers/");
LPTSTR lpPtr = NULL; LPTSTR lpPathInfo = NULL; LPTSTR lpPrinterName = NULL; LPTSTR lpSuffix = NULL; DWORD dwLen;
// Make a copy of lpszPathInfo
if (! (lpPathInfo = lpPtr = AllocStr (lpszPathInfo))) return NULL;
// Verify the prefix
if (_tcsnicmp (lpPathInfo, szPrinters, COUNTOF (szPrinters) - 1)) { goto Cleanup; } lpPathInfo += COUNTOF (szPrinters) - 1;
dwLen = lstrlen (lpPathInfo); // Compare the length of the printer name with .printer suffix
if (dwLen <= COUNTOF (szPrinter) - 1) { goto Cleanup; }
lpSuffix = lpPathInfo + dwLen - COUNTOF (szPrinter) + 1; //lpszStr should point to .printer
// Verify the suffix.
if (lstrcmpi(lpSuffix, szPrinter)) { goto Cleanup; }
*lpSuffix = TEXT('\0'); // Terminate string
lpPrinterName = AllocStr (lpPathInfo);
Cleanup:
LocalFree(lpPtr); return lpPrinterName; }
/*****************************************************************************\
* DllMain * * \*****************************************************************************/ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { BOOL bRet = TRUE;
if (fdwReason == DLL_PROCESS_ATTACH) { g_hInstance = hinstDLL;
// Init debug support in spllib.lib
bSplLibInit( NULL );
__try { InitializeCriticalSection(&SplCritSect); InitializeCriticalSection(&TagCritSect);
// Initialize the CAB generation crit-sect for web-pnp.
//
InitCABCrit(); } __except (1) { bRet = FALSE; SetLastError (ERROR_INVALID_HANDLE); }
if (bRet) { // Initializa the sleeper, which is used to cleanup the pritner jobs
InitSleeper ();
// We don't care about fdwReason==DLL_THREAD_ATTACH or _DETACH
DisableThreadLibraryCalls(hinstDLL);
srand( (UINT)time( NULL ) ); }
}
if (fdwReason == DLL_PROCESS_DETACH) { // Terminate the additional cleanup thread
ExitSleeper ();
DeleteCriticalSection(&SplCritSect); DeleteCriticalSection(&TagCritSect);
// Free our web-pnp crit-sect.
//
FreeCABCrit();
// Free debug support in spllib.lib
vSplLibFree(); }
// Do any other required initialize/deinitialize here.
return bRet; }
/*****************************************************************************\
* GetExtensionVersion * * Required ISAPI export function. * \*****************************************************************************/ BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer) { pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
TCHAR szBuf[HSE_MAX_EXT_DLL_NAME_LEN+1]; szBuf[0] = TEXT('\0'); LoadString(g_hInstance, IDS_ISAPI_DESCRIPTION, szBuf, sizeof(szBuf) / sizeof (TCHAR));
// Convert szBuf to ANSI
if (UnicodeToAnsiString (szBuf, (LPSTR) szBuf, NULL)) { lstrcpynA( pVer->lpszExtensionDesc, (LPSTR) szBuf, HSE_MAX_EXT_DLL_NAME_LEN );
return TRUE; } else return FALSE; } // GetExtensionVersion()
/*****************************************************************************\
* GetServerName * * Get the server name and convert it to the unicode string. * \*****************************************************************************/ BOOL GetServerName (EXTENSION_CONTROL_BLOCK *pECB)
{ static char c_szServerName[] = "SERVER_NAME"; DWORD dwSize; char szAnsiHttpServerName[INTERNET_MAX_HOST_NAME_LENGTH + 1]; BOOL bRet = FALSE; DWORD dwClusterState; BOOL bCluster = FALSE;
dwSize = sizeof(szAnsiHttpServerName); if (pECB->GetServerVariable (pECB->ConnID, c_szServerName, szAnsiHttpServerName, &dwSize)) {
AnsiToUnicodeString(szAnsiHttpServerName, g_szHttpServerName, 0); // Now, the computer name becomes the server name. In case of the intranet, it is the computer
// name, in case of internet, it is either the IP address or the DNS name
if (!lstrcmpi (g_szHttpServerName, TEXT ("localhost")) || !lstrcmpi (g_szHttpServerName, TEXT ("127.0.0.1"))) {
dwSize = ARRAY_COUNT (g_szHttpServerName);
bRet = GetComputerName( g_szHttpServerName, &dwSize); } else bRet = TRUE; }
if (bRet) {
bRet = FALSE;
// Now let's get the printer server name
//
// Check if we are running in a cluster node
//
if ((GetNodeClusterState (NULL, &dwClusterState) == ERROR_SUCCESS) && (dwClusterState == ClusterStateRunning)) {
bCluster = TRUE; }
//
// If we are running in the cluster mode, we have to use the ServerName referred in the HTTP header.
// Otherwise, we can use the computer name directly.
//
// Get size in Charaters of the buffer
dwSize = ARRAY_COUNT (g_szPrintServerName); if (bCluster) { if (SUCCEEDED( StringCchCopy (g_szPrintServerName, dwSize, g_szHttpServerName))) bRet = TRUE; } else { bRet = GetComputerName( g_szPrintServerName, &dwSize); }
}
return bRet; }
/*****************************************************************************\
* ParseQueryString * * ParseQueryString converts the query string into a sequence of arguments. * The main command is converted to a command ID. Subsequent arguments are * converted to strings or DWORDs. * * Format: Command & Arg1 & Arg2 & Arg3 ... * Each arg is either a number or a string in quotes. * * returns FALSE if the query string exists but is invalid * \*****************************************************************************/ BOOL ParseQueryString(PALLINFO pAllInfo) { LPTSTR pszQueryString, pszTmp, pszTmp2, pszTmp3; int iNumArgs = 0;
pszQueryString = pAllInfo->lpszQueryString;
if ( !pszQueryString || (*pszQueryString == 0) ) { // Check if the method is post
if ( !lstrcmp (pAllInfo->lpszMethod, TEXT ("POST")) ) { // also check here for content-type application/ipp ???
//
pAllInfo->iQueryCommand = CMD_IPP; // can we use the NULL cmd ???
} else { pAllInfo->iQueryCommand = CMD_WebUI; // redirect to webui
}
return TRUE; }
// Get a copy of the string to do surgery on it in this routine and save pieces of it as other info.
// Save it too so it can be freed later.
pszQueryString = AllocStr ( pszQueryString );
if ( pszQueryString != NULL ) { // We will find and replace the first '&' with NULL. This is to isolate the first
// piece of the query string and examine it.
// pszQueryString then points to this first piece (command), pszTmp to the rest.
if ( pszTmp = _tcschr( pszQueryString, TEXT('&')) ) { *pszTmp = TEXT('\0'); pszTmp++; }
// Search for main command
pAllInfo->iQueryCommand = CMD_Invalid;
// If we had {WinPrint.PrinterCommandURL}?/myfile.htm&bla1&bla2&bla3.....
// or {WinPrint.PrinterCommandURL}?/bla1/bla2/.../blaN/myfile.htm&aaa&bbb&ccc...
// then pszQueryString is pointing to a NULL we inserted in place of '/', so it is OK.
// So just attempt to find a iQueryCommand only if pszQueryString is pointing to a non-NULL char.
if ( *pszQueryString ) {
for ( int i=0; i<iNumQueryMap; i++ ) { if ( !lstrcmpi(rgQueryMap[i].pszCommand, pszQueryString) ) { pAllInfo->iQueryCommand = rgQueryMap[i].iCommandID; break; } }
if ( pAllInfo->iQueryCommand == CMD_Invalid ) { LocalFree( pszQueryString ); return FALSE; // No command found. Bad query string.
} }
// At this point we continue with pszTmp for the arguments.
// We take at most MAX_Q_ARG arguments to avoid memory corruption
while ( (NULL != pszTmp) && (*pszTmp) && iNumArgs < MAX_Q_ARG ) { pszTmp2 = pszTmp; pszTmp = _tcschr( pszTmp, TEXT('&')); if ( pszTmp != NULL ) { *pszTmp = 0; pszTmp ++; } if ( *pszTmp2 >= TEXT('0') && *pszTmp2 <= TEXT('9') ) { // DWORD integer value
pAllInfo->fQueryArgIsNum[iNumArgs] = TRUE; pAllInfo->QueryArgValue[iNumArgs] = (DWORD)_ttoi(pszTmp2); } else { // Pointer to string
pAllInfo->fQueryArgIsNum[iNumArgs] = FALSE; pAllInfo->QueryArgValue[iNumArgs] = (UINT_PTR)pszTmp2; } iNumArgs ++; }
pAllInfo->iNumQueryArgs = iNumArgs;
DBGMSG(DBG_INFO, ("ParseQueryString: %d query arguments\r\n", iNumArgs));
LocalFree( pszQueryString ); return TRUE; }
return FALSE; }
/*****************************************************************************\
* CreateExe * * \*****************************************************************************/ DWORD CreateExe(PALLINFO pAllInfo, PPRINTERPAGEINFO pPageInfo, DWORD dwCliInfo) { LPTSTR lpPortName = NULL; LPTSTR lpExeName = NULL; LPTSTR lpFriendlyName = NULL; DWORD dwRet = HSE_STATUS_ERROR ; DWORD dwLen = 0; DWORD dwFNBytes = 0; DWORD dwLastError = ERROR_INVALID_DATA; BOOL bSecure = IsSecureReq (pAllInfo->pECB);
GetWebpnpUrl (g_szHttpServerName, pPageInfo->pPrinterInfo->pShareName, NULL, bSecure, NULL, &dwLen); if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) goto Cleanup;
if (! (lpPortName = (LPTSTR)LocalAlloc(LPTR, dwLen * sizeof (*lpPortName)))) goto Cleanup;
if (!GetWebpnpUrl (g_szHttpServerName, pPageInfo->pPrinterInfo->pShareName, NULL, bSecure, lpPortName, &dwLen)) goto Cleanup;
lpExeName = (LPTSTR)LocalAlloc(LPTR, MAX_PATH * sizeof (*lpExeName)); if (!lpExeName) goto Cleanup;
dwFNBytes = (lstrlen(pPageInfo->pszFriendlyName)+1) * sizeof (*lpFriendlyName); lpFriendlyName = (LPTSTR)LocalAlloc(LPTR, dwFNBytes); if (!lpFriendlyName) goto Cleanup;
if (FAILED( StringCbCopy(lpFriendlyName, dwFNBytes, pPageInfo->pszFriendlyName))) goto Cleanup;
dwRet = GenerateCAB(lpFriendlyName, lpPortName, dwCliInfo, lpExeName, IsSecureReq(pAllInfo->pECB));
if (dwRet == HSE_STATUS_SUCCESS) { LPTSTR lpEncodedExeName = EncodeString (lpExeName, TRUE);
if (!lpEncodedExeName) { dwRet = HSE_STATUS_ERROR; goto Cleanup; } htmlSendRedirect (pAllInfo, lpEncodedExeName); LocalFree (lpEncodedExeName); } else { dwLastError = GetLastError ();
if (dwLastError == ERROR_FILE_NOT_FOUND) { dwLastError = ERROR_DRIVER_NOT_FOUND; }
if (dwLastError == ERROR_DISK_FULL) { dwLastError = ERROR_SERVER_DISK_FULL; } }
Cleanup:
LocalFree(lpPortName); LocalFree(lpExeName); LocalFree(lpFriendlyName);
if (dwRet != HSE_STATUS_SUCCESS) return ProcessErrorMessage (pAllInfo, dwLastError); else return dwRet; }
/*****************************************************************************\
* ProcessRequest * * Process the incoming request * \*****************************************************************************/ DWORD ProcessRequest(PALLINFO pAllInfo, LPTSTR lpszPrinterName) { DWORD dwRet = HSE_STATUS_ERROR; PPRINTER_INFO_2 pPrinterInfo = NULL; HANDLE hPrinter = NULL; DWORD iQueryCommand; LPTSTR lpszFriendlyName; DWORD dwCliInfo; WORD wIppReq = 0; LPTSTR pszDecodedName = NULL; DWORD dwSize = 0; PRINTER_DEFAULTS pd = {NULL, NULL, PRINTER_ACCESS_USE}; LPTSTR lpszWebUIUrl = NULL; LPTSTR pszOpenName = NULL; LPTSTR pszTmpName = NULL;
iQueryCommand = pAllInfo->iQueryCommand;
DBGMSG(DBG_INFO, ("ShowPrinterPage for printer \"%ws\"\n", lpszPrinterName));
// Open the printer and get printer info level 2.
DecodePrinterName (lpszPrinterName, NULL, &dwSize);
if (! (pszDecodedName = (LPTSTR) LocalAlloc (LPTR, sizeof (TCHAR) * dwSize))) return ProcessErrorMessage (pAllInfo, GetLastError ());
if (!DecodePrinterName (lpszPrinterName, pszDecodedName, &dwSize)) { dwRet = ProcessErrorMessage (pAllInfo, GetLastError ()); goto Cleanup; }
if (*pszDecodedName != TEXT ('\\') ) { dwSize = sizeof(TCHAR) * (lstrlen (pszDecodedName) + lstrlen (g_szPrintServerName) + 4 );
// There is no server name before the printer name, append the server name
if (! (pszOpenName = pszTmpName = (LPTSTR) LocalAlloc (LPTR, dwSize))) goto Cleanup;
StringCbCopy (pszOpenName, dwSize, TEXT ("\\\\")); StringCbCat (pszOpenName, dwSize, g_szPrintServerName); StringCbCat (pszOpenName, dwSize, TEXT ("\\")); StringCbCat (pszOpenName, dwSize, pszDecodedName);
} else { pszOpenName = pszDecodedName; }
if (! OpenPrinter(pszOpenName, &hPrinter, &pd)) { dwRet = ProcessErrorMessage (pAllInfo, GetLastError ()); goto Cleanup; }
// Get a PRINTER_INFO_2 structure filled up
dwSize = 0; GetPrinter(hPrinter, 2, NULL, 0, &dwSize); if ((GetLastError() != ERROR_INSUFFICIENT_BUFFER) || (NULL == (pPrinterInfo = (PPRINTER_INFO_2)LocalAlloc(LPTR, dwSize))) || (!GetPrinter(hPrinter, 2, (byte *)pPrinterInfo, dwSize, &dwSize))) { dwRet = ProcessErrorMessage (pAllInfo, GetLastError ()); goto Cleanup; }
if (! (pPrinterInfo->Attributes & PRINTER_ATTRIBUTE_SHARED)) { // If the printer is not shared, return access denied
dwRet = ProcessErrorMessage (pAllInfo, ERROR_ACCESS_DENIED); goto Cleanup; }
// Find printer friendly name.
// If we opened with UNC path we need to remove server name
if (pPrinterInfo->pPrinterName) { lpszFriendlyName = _tcsrchr (pPrinterInfo->pPrinterName, TEXT('\\')); if (lpszFriendlyName) lpszFriendlyName ++; else lpszFriendlyName = pPrinterInfo->pPrinterName; }
// We've got an open printer and some printer info. Ready to go.
// Fill in structure of info for whatever function we call
PRINTERPAGEINFO ppi; ZeroMemory(&ppi, sizeof(ppi));
ppi.pszFriendlyName = lpszFriendlyName; ppi.pPrinterInfo = pPrinterInfo; ppi.hPrinter = hPrinter;
// Do appropriate action based on query string
switch (iQueryCommand) { case CMD_WebUI: // Construct a URL to redirect
dwSize = 0; if (GetWebUIUrl (NULL, pszDecodedName, lpszWebUIUrl, &dwSize)) goto Cleanup;
if (GetLastError () != ERROR_INSUFFICIENT_BUFFER) goto Cleanup;
if (!(lpszWebUIUrl = (LPTSTR)LocalAlloc(LPTR, dwSize * sizeof (TCHAR)))) goto Cleanup;
if (! GetWebUIUrl (NULL, pszDecodedName, lpszWebUIUrl, &dwSize)) goto Cleanup;
dwRet = htmlSendRedirect (pAllInfo, lpszWebUIUrl);
break;
case CMD_IPP:
if (wIppReq = GetIppReq(pAllInfo)) {
dwRet = SplIppJob(wIppReq, pAllInfo, &ppi);
} else {
DBGMSG(DBG_WARN, ("ShowPrinterPage: Warn : Invalid IPP Stream.\n"));
if (IsClientHttpProvider (pAllInfo)){ // To improve the perfomance for the internet provider by returning something really quick
LPTSTR pszContent = GetString(pAllInfo, IDS_ERROR_200CONTENT); htmlSendHeader (pAllInfo, TEXT ("200 OK"), pszContent); dwRet = HSE_STATUS_SUCCESS; }
if (INVALID_HANDLE_VALUE != hPrinter) ClosePrinter(hPrinter);
break; }
break;
case CMD_CreateExe:
DBGMSG(DBG_TRACE, ("Calling CreateExe.\n"));
if (dwCliInfo = GetClientInfo(pAllInfo)) {
dwRet = CreateExe(pAllInfo, &ppi, dwCliInfo);
} else {
dwRet = ProcessErrorMessage (pAllInfo, ERROR_NOT_SUPPORTED); goto Cleanup; } break;
default: dwRet = ProcessErrorMessage (pAllInfo, ERROR_INVALID_PRINTER_COMMAND); break; }
Cleanup: // Clean up our stuff
if (dwRet != HSE_STATUS_ERROR) pAllInfo->pECB->dwHttpStatusCode=200; // 200 OK
LocalFree (lpszWebUIUrl);
LocalFree (pszDecodedName); LocalFree (pszTmpName); LocalFree (pPrinterInfo);
// For any commands other than CMD_IPP commands, we can close the
// printer-handle. Otherwise, we rely on the Spool*() routines
// to handle this for us after we're done reading and processing
// the entire print-job.
//
if (hPrinter && (iQueryCommand != CMD_IPP)) ClosePrinter(hPrinter);
return dwRet;
}
/*****************************************************************************\
* GetString * * \*****************************************************************************/ LPTSTR GetString(PALLINFO pAllInfo, UINT iStringID) { LPTSTR lpszBuf = pAllInfo->szStringBuf;
lpszBuf[0] = TEXT('\0'); LoadString(g_hInstance, iStringID, lpszBuf, STRBUFSIZE); SPLASSERT(lpszBuf[0] != TEXT('\0'));
return lpszBuf; }
/*****************************************************************************\
* HttpExtensionProc * * Main entrypoint for HTML generation. * \*****************************************************************************/ DWORD WINAPI HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB) { DWORD dwRet = HSE_STATUS_ERROR; PALLINFO pAllInfo = NULL; // This structure contains all relevant info for this connection
LPTSTR pPrinterName = NULL;
// Assume failure
if(!pECB) return HSE_STATUS_ERROR;
pECB->dwHttpStatusCode = HTTP_STATUS_NOT_SUPPORTED;
// Get the server name and convert it to the unicode string.
if (!GetServerName (pECB)) return HSE_STATUS_ERROR;
if (!(pAllInfo = (PALLINFO) LocalAlloc (LPTR, sizeof (ALLINFO)))) return HSE_STATUS_ERROR;
// Initialize pAllInfo
ZeroMemory(pAllInfo, sizeof(ALLINFO));
pAllInfo->pECB = pECB;
// Convert the ANSI string in ECB to Unicode
// weihaic
// pAllInfo->lpszQueryString = AllocateUnicodeString(DecodeStringA (pECB->lpszQueryString));
// We can not decode now becuase the decoded string will bring troubles in parsing
//
pAllInfo->lpszQueryString = AllocateUnicodeString(pECB->lpszQueryString); pAllInfo->lpszMethod = AllocateUnicodeString(pECB->lpszMethod); pAllInfo->lpszPathInfo = AllocateUnicodeString(pECB->lpszPathInfo); pAllInfo->lpszPathTranslated = AllocateUnicodeString(pECB->lpszPathTranslated);
if (!pAllInfo->lpszQueryString || !pAllInfo->lpszMethod || !pAllInfo->lpszPathInfo || !pAllInfo->lpszPathTranslated ) {
goto Cleanup; }
// weihaic
// The query string contain user entered text such as printer location
// priner description, etc. These strings are case sensitive, so we
// could not convert them to upper case at the very beginning
// CharUpper (pAllInfo->lpszQueryString);
//
CharUpper (pAllInfo->lpszMethod); CharUpper (pAllInfo->lpszPathInfo); CharUpper (pAllInfo->lpszPathTranslated);
if (! (pPrinterName = GetPrinterName (pAllInfo->lpszPathInfo))) { // This is a wrong URL, return error code
dwRet = ProcessErrorMessage (pAllInfo, ERROR_INVALID_DATA); goto Cleanup; }
if (! ParseQueryString (pAllInfo)) goto Cleanup;
dwRet = ProcessRequest (pAllInfo, pPrinterName); // We always hit Cleanup anyway
Cleanup:
LocalFree (pPrinterName); LocalFree (pAllInfo->lpszQueryString); LocalFree (pAllInfo->lpszMethod); LocalFree (pAllInfo->lpszPathInfo); LocalFree (pAllInfo->lpszPathTranslated);
LocalFree (pAllInfo);
return dwRet; } // HttpExtensionProc()
|