/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    prnevent.c

Abstract:

    This file handles the DrvPrinterEvent spooler API.

Environment:

    Win32 subsystem, DriverUI module, user mode

Revision History:

    08/30/00 -fengy-
        Added DrvDocumentEvent support.

    02/13/97 -davidx-
        Implement OEM plugin support.

    02/06/97 -davidx-
        Rewrote it to use common data management functions.

    02/04/97 -davidx-
        Reorganize driver UI to separate ps and uni DLLs.

    07/17/96 -amandan-
        Modified for common UI and shared binary data

    05/20/96 -davidx-
        Created it.

--*/


#include "precomp.h"

//
// DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
// We need to define it if it's not defined (on Win2K)
// so our code can be built with Win2K DDK.
//

#ifndef DOCUMENTEVENT_QUERYFILTER

#define DOCUMENTEVENT_QUERYFILTER  14

#endif

//
// Private APIs exported by the spooler for printer drivers and port monitors.
// These must be kept in sync with winsplp.h.
//

typedef HANDLE (*LPREVERTTOPRINTERSELF)(VOID);
typedef BOOL (*LPIMPERSONATEPRINTERCLIENT)(HANDLE);

//
// Forward and external function declarations
//

BOOL BInitOrUpgradePrinterProperties(PCOMMONINFO);
VOID DeleteFontIntallerFile(HANDLE);

PTSTR
GetBinaryFileName(
    PTSTR   ptstrDataFileName
    )
/*++

Routine Description:

    This function generates a binary file name from the data file name.

Arguments:

    ptstrDataFileName   specifies the data file name

Return Value:

    TRUE for success and FALSE for failure

Note:

--*/

{

    INT iLen;
    PTSTR   ptstrFileName, ptstrExtension;
    PTSTR   ptstrBinaryExt, ptstrFileExt;

    ptstrFileName = ptstrExtension = NULL;

#ifdef UNIDRV

    ptstrBinaryExt = BUD_FILENAME_EXT;
    ptstrFileExt = GPD_FILENAME_EXT;

#else

    ptstrBinaryExt = BPD_FILENAME_EXT;
    ptstrFileExt = PPD_FILENAME_EXT;

#endif

    iLen = _tcslen(ptstrDataFileName);

    if ((ptstrExtension = _tcsrchr(ptstrDataFileName, TEXT('.'))) == NULL ||
        _tcsicmp(ptstrExtension, ptstrFileExt) != EQUAL_STRING)
    {
        ptstrExtension = ptstrDataFileName + iLen;
        iLen += _tcslen(ptstrBinaryExt);

    }

    if (ptstrFileName = MemAlloc((iLen + 1) * sizeof(TCHAR)))
    {
        _tcscpy(ptstrFileName, ptstrDataFileName);

        _tcscpy(ptstrFileName + (ptstrExtension - ptstrDataFileName), ptstrBinaryExt);
    }

    return ptstrFileName;
}

BOOL
DrvDriverEvent(
    DWORD   dwDriverEvent,
    DWORD   dwLevel,
    LPBYTE  pDriverInfo,
    LPARAM  lParam
    )
/*++

Routine Description:

    This function handles the DrvDriverEvent spooler API

Arguments:

    dwDriverEvent       specifies the event
    dwLevel             level of DRIVER_INFO_*
    pDriverInfo         pointer to DRIVER_INFO_*
    lParam              event specific parameters.

Return Value:

    TRUE for success and FALSE for failure

Note:

--*/

{
    BOOL           bResult      = TRUE;

#ifndef WINNT_40

    PDRIVER_INFO_3 pDriverInfo3 = (PDRIVER_INFO_3)pDriverInfo;
    PTSTR          ptstrFileName;

    if (pDriverInfo3 == NULL || pDriverInfo3->pDataFile == NULL)
        return FALSE;

    switch (dwDriverEvent)
    {
    case DRIVER_EVENT_INITIALIZE:
        break;

    case DRIVER_EVENT_DELETE:

        //
        // Need to delete the binary data generated by the parsers
        //

        ptstrFileName = GetBinaryFileName(pDriverInfo3->pDataFile);

        if (ptstrFileName)
        {
            DeleteFile(ptstrFileName);
            MemFree(ptstrFileName);
        }
    }

    //
    // Call the OEM to handle DrvDriverEvent
    //

    {
        PFN_OEMDriverEvent  pfnOEMDriverEvent;
        POEM_PLUGINS        pOemPlugins;
        POEM_PLUGIN_ENTRY   pOemEntry;
        DWORD               dwOemCount;

        if (! (pOemPlugins = PGetOemPluginInfo(NULL, pDriverInfo3->pConfigFile, pDriverInfo3)) ||
            ! BLoadOEMPluginModules(pOemPlugins))
        {
            ERR(("DrvDriverEvent, Cannot load OEM plugins: %d\n", GetLastError()));
            if (pOemPlugins)
            {
                VFreeOemPluginInfo(pOemPlugins);
            }

            return FALSE;
        }

        dwOemCount = pOemPlugins->dwCount;
        pOemEntry =  pOemPlugins->aPlugins;

        //
        // call OEMDriverEvent entrypoint for each plugin
        //

        for (; dwOemCount--; pOemEntry++)
        {
            if (pOemEntry->hInstance == NULL)
                continue;

            if (HAS_COM_INTERFACE(pOemEntry))
            {
                HRESULT hr;

                hr = HComOEMDriverEvent(pOemEntry,
                                        dwDriverEvent,
                                        dwLevel,
                                        pDriverInfo,
                                        lParam);

                if (hr == E_NOTIMPL)
                    continue;

                bResult = SUCCEEDED(hr);

            }
            else
            {
                if ((pfnOEMDriverEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMDriverEvent)) &&
                    !pfnOEMDriverEvent(dwDriverEvent, dwLevel, pDriverInfo, lParam))
                {
                    ERR(("OEMDriverEvent failed for '%ws': %d\n",
                        CURRENT_OEM_MODULE_NAME(pOemEntry),
                        GetLastError()));

                    bResult = FALSE;
                }
            }
        }

        if (pOemPlugins)
            VFreeOemPluginInfo(pOemPlugins);
    }

#endif  // WINNT_40

    return bResult;
}


/*++

Routine Name:

    DrvDocumentEvent

Routine Description:

    Handle certain events associated with printing a document.

    Although our core driver doesn't do anything for any events,
    this function allows OEM plugins to add their event handling.

Arguments:

    hPrinter - printer handle
    hdc - device contect handle
    iEsc - escape code identifying the event to be handled
    cbIn - size in bytes of the array pointed to by pbIn
    pbIn - pointer to a ULONG array, whose usage depends on iEsc
    cbOut - only used as cbOutput parameter for ExtEscape
    pbOut - pointer to an output buffer, whose usage depends on iEsc

Return Value:

    DOCUMENTEVENT_FAILURE - iEsc event is supported but a failure occurred
    DOCUMENTEVENT_SUCCESS - iEsc event is handled successfully
    DOCUMENTEVENT_UNSUPPORTED - iEsc event is not supported

Last Error:

    None

--*/
INT
DrvDocumentEvent(
    HANDLE  hPrinter,
    HDC     hdc,
    int     iEsc,
    ULONG   cbIn,

    #ifdef WINNT_40
    PULONG  pbIn,
    #else
    PVOID   pbIn,
    #endif

    ULONG   cbOut,

    #ifdef WINNT_40
    PULONG  pbOut
    #else
    PVOID   pbOut
    #endif
    )
{
    POEM_PLUGINS   pOemPlugins = NULL;
    PDRIVER_INFO_3 pDriverInfo3 = NULL;
    INT            iReturn;

    if ((pDriverInfo3 = MyGetPrinterDriver(hPrinter, NULL, 3)) == NULL)
    {
       ERR(("Cannot get printer driver info: %d\n", GetLastError()));
        iReturn = DOCUMENTEVENT_FAILURE;
        goto docevent_exit;
    }

    if (!(pOemPlugins = PGetOemPluginInfo(hPrinter,
                                          pDriverInfo3->pConfigFile,
                                          pDriverInfo3)) ||
        !BLoadOEMPluginModules(pOemPlugins))
    {
        //
        // Note that BLoadOEMPluginModules always returns TRUE, even in
        // the case when plugin(s) can't be loaded, which is treated
        // as there is no plugin(s).
        //

        ERR(("Cannot get OEM plugin info: %d\n", GetLastError()));
        iReturn = DOCUMENTEVENT_FAILURE;
        goto docevent_exit;
    }

    if (pOemPlugins->dwCount)
    {
        POEM_PLUGIN_ENTRY pOemEntry = pOemPlugins->aPlugins;
        DWORD cOemCount = pOemPlugins->dwCount;
        INT   iResult;
        BOOL  bOEMDocEventOK = FALSE;

        for ( ; cOemCount--; pOemEntry++)
        {
            HRESULT hr;

            if (pOemEntry->hInstance == NULL ||
                !HAS_COM_INTERFACE(pOemEntry))
            {
                continue;
            }

            hr = HComOEMDocumentEvent(pOemEntry,
                                      hPrinter,
                                      hdc,
                                      iEsc,
                                      cbIn,
                                      (PVOID)pbIn,
                                      cbOut,
                                      (PVOID)pbOut,
                                      &iResult);

            if (SUCCEEDED(hr))
            {
                bOEMDocEventOK = TRUE;

                #ifndef WINNT_40

                //
                // DOCUMENTEVENT_QUERYFILTER is introduced in Whistler.
                //

                if (iEsc == DOCUMENTEVENT_QUERYFILTER)
                {
                    //
                    // At most one plugin is allowed to handle the event
                    // DOCUMENTEVENT_QUERYFILTER, and the filter it specifies
                    // will be used by spooler.
                    //
                    // For all other events, we will call every plugin so
                    // each will have the chance to perform its tasks.
                    //

                    break;
                }

                #endif // !WINNT_40
            }
        }

        if (bOEMDocEventOK)
        {
            //
            // At least one plugin handled the event successfully, so
            // use the return value specified by the plugin(s).
            //

            iReturn = iResult;
        }
        else
        {
            //
            // None of the plugins handled the event successfully.
            //

            iReturn = DOCUMENTEVENT_UNSUPPORTED;
        }
    }
    else
    {
        //
        // There is no plugin.
        //

        iReturn = DOCUMENTEVENT_UNSUPPORTED;
    }

    docevent_exit:

    if (pDriverInfo3)
    {
        MemFree(pDriverInfo3);
    }

    if (pOemPlugins)
    {
        VFreeOemPluginInfo(pOemPlugins);
    }

    //
    // If there is no plugin, or none of the plugins handles DocumentEvent
    // successfully, we return DOCUMENTEVENT_UNSUPPORTED since our driver
    // doesn't do anything for DrvDocumentEvent. When spooler sees this
    // return value for DOCUMENTEVENT_CREATEDCPRE, it will decide not to
    // make any more event calls to the driver.
    //
    // If the event is handled successfully by the plugins, we will return
    // the return value specified by the plugin(s).
    //

    return iReturn;
}


BOOL
DrvPrinterEvent(
    LPWSTR  pPrinterName,
    INT     DriverEvent,
    DWORD   Flags,
    LPARAM  lParam
    )
/*++

Routine Description:

    This function handles the DrvPrinterEvent spooler API

Arguments:

    pPrinterName        name of device
    DriverEvent         specifies the event
    Flags               bits flag
    lParam              event specific parameters.

Return Value:

    TRUE for success and FALSE for failure

Note:

--*/

{
    LPREVERTTOPRINTERSELF       pRevertToPrinterSelf;
    LPIMPERSONATEPRINTERCLIENT  pImpersonatePrinterClient;
    HINSTANCE                   hSpoolss = NULL;
    HANDLE                      hToken = NULL;
    PCOMMONINFO                 pci = NULL;
    HANDLE                      hPrinter = NULL;
    BOOL                        bResult = TRUE;
    CACHEDFILE                  CachedFile;

    VERBOSE(("Entering DrvPrinterEvent: %d ...\n", DriverEvent));

    switch (DriverEvent)
    {
    case PRINTER_EVENT_CACHE_REFRESH:

        //
        // Open a handle to the printer connection
        //

        if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
        {
            ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
            hPrinter = NULL;
            break;
        }

        //
        // Prepare to copy cached driver files from the server, if any
        //

        #ifdef PSCRIPT
        _BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_NTFFILENAME);
        #else
        _BPrepareToCopyCachedFile(hPrinter, &CachedFile, REGVAL_FONTFILENAME);
        #endif

        //
        // Load spoolss.dll and get address of functions:
        //  RevertToPrinterSelf - switch to spooler's security context
        //  ImpersonatePrinterClient - switch to current user's security context
        //

        if (! (hSpoolss = LoadLibrary(TEXT("spoolss.dll"))) ||
            ! (pRevertToPrinterSelf = (LPREVERTTOPRINTERSELF)
                    GetProcAddress(hSpoolss, "RevertToPrinterSelf")) ||
            ! (pImpersonatePrinterClient = (LPIMPERSONATEPRINTERCLIENT)
                    GetProcAddress(hSpoolss, "ImpersonatePrinterClient")))
        {
            ERR(("Couldn't load spoolss.dll: %d\n", GetLastError()));

            if (hSpoolss != NULL)
                FreeLibrary(hSpoolss);

            _VDisposeCachedFileInfo(&CachedFile);

            break;
        }

        //
        // Switch to spooler security context so that we can create
        // binary printer description data file in the driver directory
        //
        // When we call to load raw printer description data, the parser
        // will check its cache. If no binary data file exists or existing
        // binary data file is out of date, the parser will regenerate
        // an up-to-date binary data file.
        //

        hToken = pRevertToPrinterSelf();
        pci = PLoadCommonInfo(hPrinter, pPrinterName, 0);


        //
        // Copy cached driver file from the server
        //

        _BCopyCachedFile(pci, &CachedFile);
        _VDisposeCachedFileInfo(&CachedFile);

        if (hToken)
        {
            if (!(bResult = pImpersonatePrinterClient(hToken)))
            {
                ERR(("PrinterEvent-ImpersonatePrinterClient failed: %d\n", GetLastError()));
            }
        }

        FreeLibrary(hSpoolss);
        break;

    case PRINTER_EVENT_INITIALIZE:

        //
        // Open a printer with administrator privilege, and
        // process OEM plugin configuration information
        //

        pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_ADMIN|FLAG_INIT_PRINTER);

        if (pci == NULL)
            break;

        //
        // Initialize default printer-sticky properties in registry
        // Add printer forms to the spooler's forms database
        //

        (VOID) BInitOrUpgradePrinterProperties(pci);

        #ifndef WINNT_40

        VNotifyDSOfUpdate(pci->hPrinter);

        #endif // !WINNT_40


        break;

    case PRINTER_EVENT_ADD_CONNECTION:

        //
        // Fix the bug where when NT5 client connects to NT4 server, the server registry
        // doesn't have the REGVAL_INIDATA entry. Calling PLoadCommonInfo with
        // FLAG_PROCESS_INIFILE will write REGVAL_INIDATA to NT4 registry.
        //

        pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_ADMIN|FLAG_PROCESS_INIFILE);
        break;

    #ifdef UNIDRV

    case PRINTER_EVENT_DELETE:
    case PRINTER_EVENT_DELETE_CONNECTION:

        //
        // Delete font installer file
        //

        //
        // Open a handle to the printer
        //

        if (! OpenPrinter(pPrinterName, &hPrinter, NULL))
        {
            ERR(("OpenPrinter '%ws' failed: %d\n", pPrinterName, GetLastError()));
            hPrinter = NULL;
            break;
        }

        pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);

        DeleteFontIntallerFile(hPrinter);

        break;

    #endif

    default:

        pci = PLoadCommonInfo(NULL, pPrinterName, FLAG_OPENPRINTER_NORMAL);
        break;
    }

    if (pci != NULL)
    {
        if (bResult)
        {
            PFN_OEMPrinterEvent pfnOEMPrinterEvent;

            //
            // call OEMPrinterEvent entrypoint for each plugin
            //

            FOREACH_OEMPLUGIN_LOOP(pci)

                if (HAS_COM_INTERFACE(pOemEntry))
                {
                    HRESULT hr;

                    hr = HComOEMPrinterEvent(pOemEntry,
                                            pPrinterName,
                                            DriverEvent,
                                            Flags,
                                            lParam);

                    if (hr == E_NOTIMPL)
                        continue;

                    bResult = SUCCEEDED(hr);

                }
                else
                {
                    if ((pfnOEMPrinterEvent = GET_OEM_ENTRYPOINT(pOemEntry, OEMPrinterEvent)) &&
                        !pfnOEMPrinterEvent(pPrinterName, DriverEvent, Flags, lParam))
                    {
                        ERR(("OEMPrinterEvent failed for '%ws': %d\n",
                            CURRENT_OEM_MODULE_NAME(pOemEntry),
                            GetLastError()));

                        bResult = FALSE;
                    }
                }

           END_OEMPLUGIN_LOOP
        }
        VFreeCommonInfo(pci);
    }
    else
        bResult = FALSE;

    if (hPrinter != NULL)
        ClosePrinter(hPrinter);

    return (bResult);
}


BOOL
BUpgradePrivateFlags(
    PCOMMONINFO pci
    )

/*++

Routine Description:

    Upgrade pPrinterData->dwFlags if necessary

Arguments:

    pci - Points to basic printer info

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{

#if 0

    DWORD    dwFlag, dwIndex, dwSelection, dwFeatureIndex;
    PFEATURE pFeature;
    PPAGEPROTECT pPageProtect;

    //
    // UniDriver specific upgrade steps
    //

    if (BGetPrinterDataDWord(pci->hPrinter, REGVAL_PAGE_PROTECTION, &dwFlag) &&
        (pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGEPROTECTION)) &&
        BCombineCommonInfoOptionsArray(pci))
    {
        if (dwFlag & DXF_PAGEPROT)
            dwSelection = PAGEPRO_ON;
        else
            dwSelection = PAGEPRO_OFF;

        pPageProtect = PGetIndexedOption(pci->pUIInfo, pFeature, 0);

        for (dwIndex = 0; dwIndex < pFeature->Options.dwCount; dwIndex++, pPageProtect++)
        {
            if (dwSelection == pPageProtect->dwPageProtectID)
                break;
        }

        if (dwIndex == pFeature->Options.dwCount)
            dwIndex = pFeature->dwDefaultOptIndex;

        dwFeatureIndex = GET_INDEX_FROM_FEATURE(pci->pUIInfo, pFeature);

        pci->pCombinedOptions[dwFeatureIndex].ubCurOptIndex = (BYTE)dwIndex;

        SeparateOptionArray(pci->pRawData,
                            pci->pCombinedOptions,
                            pci->pPrinterData->aOptions,
                            MAX_PRINTER_OPTIONS,
                            MODE_PRINTER_STICKY
                            );

    }

#endif

    return TRUE;

}


BOOL
BInitOrUpgradePrinterData(
    PCOMMONINFO pci
    )

/*++

Routine Description:

    Initialize the driver's printer-sticky property data
    or upgrade it to current version if it already exists

Arguments:

    pci - Points to basic printer info

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    DWORD   dwSize;
    BOOL    bResult = TRUE;

    //
    // If the printer property data already exists in the registry
    // and it's at least as big as the current PRINTERDATA, then
    // we assume it's ok and there is no need to upgrade it.
    //

    if (!BGetPrinterDataDWord(pci->hPrinter, REGVAL_PRINTER_DATA_SIZE, &dwSize) ||
        dwSize < sizeof(PRINTERDATA))
    {
        //
        // Otherwise, upgrade the existing printer property data in the registry
        // or save a copy of the default printer property data to registry.
        //

        bResult = BFillCommonInfoPrinterData(pci) &&
                  BUpgradePrivateFlags(pci) &&
                  BSavePrinterProperties(pci->hPrinter, pci->pRawData,
                                         pci->pPrinterData, sizeof(PRINTERDATA));
    }

    return bResult;
}



BOOL
BAddOrUpgradePrinterForms(
    PCOMMONINFO pci
    )

/*++

Routine Description:

    Add printer specific forms to the spooler's forms database

Arguments:

    pci - Points to basic printer info

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PPAGESIZE       pPageSize;
    FORM_INFO_1     FormInfo1;
    DWORD           dwIndex, dwChecksum32, dwForm;
    PFEATURE        pFeature;
    WCHAR           awchBuf[CCHPAPERNAME];

    //
    // If forms has already been added and printer description
    // data hasn't changed, we don't need to do anything
    //

    if (BGetPrinterDataDWord(pci->hPrinter, REGVAL_FORMS_ADDED, &dwChecksum32) &&
        dwChecksum32 == pci->pRawData->dwChecksum32)
    {
        return TRUE;
    }

    if (pci->pSplForms == NULL)
        pci->pSplForms = MyEnumForms(pci->hPrinter, 1, &pci->dwSplForms);

    //
    //  Get pointer to PageSize feature
    //

    if ((pFeature = GET_PREDEFINED_FEATURE(pci->pUIInfo, GID_PAGESIZE)) == NULL)
    {
        WARNING(("No paper size supported\n"));
        return FALSE;
    }

    ZeroMemory(&FormInfo1, sizeof(FormInfo1));
    FormInfo1.Flags = FORM_PRINTER;
    FormInfo1.pName = awchBuf;

    //
    // Go through each printer form
    //

    for (dwIndex=0; dwIndex < pFeature->Options.dwCount; dwIndex++)
    {
        pPageSize = PGetIndexedOption(pci->pUIInfo, pFeature, dwIndex);
        ASSERT(pPageSize != NULL);

        //
        // Ignore the custom page size option
        //

        if (pPageSize->dwPaperSizeID == DMPAPER_USER ||
            pPageSize->dwPaperSizeID == DMPAPER_CUSTOMSIZE)
        {
            continue;
        }

        if (pPageSize->szPaperSize.cx <= 0 ||
            pPageSize->szPaperSize.cy <= 0)
        {
            ERR(("Paper size is too small\n"));
            continue;
        }

        if (! LOAD_STRING_PAGESIZE_NAME(pci, pPageSize, awchBuf, CCHPAPERNAME))
        {
            ERR(("Cannot get paper name\n"));
            continue;
        }

        //
        // Check if the paper name is already in the forms database.
        // If it's already in the database as a
        //

        for (dwForm=0; dwForm < pci->dwSplForms; dwForm++)
        {
            if (pci->pSplForms[dwForm].Flags == FORM_USER &&
                wcscmp(pci->pSplForms[dwForm].pName, awchBuf) == EQUAL_STRING)
            {
                VERBOSE(("Delete user/driver rdefined form: %ws\n", awchBuf));
                DeleteForm(pci->hPrinter, awchBuf);
            }
        }

        //
        // Page size:
        //  remember that FORM_INFO_1 uses micron units while
        //  PAGESIZE.szPaperSize are in Master units.
        //

        FormInfo1.Size.cx = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cx,
                                                  pci->pUIInfo->ptMasterUnits.x);

        FormInfo1.Size.cy = MASTER_UNIT_TO_MICRON(pPageSize->szPaperSize.cy,
                                                  pci->pUIInfo->ptMasterUnits.y);

        //
        // Imageable area:
        //  for driver-defined forms, all margins should be set to 0.
        //

        FormInfo1.ImageableArea.left =
        FormInfo1.ImageableArea.top = 0;
        FormInfo1.ImageableArea.right = FormInfo1.Size.cx;
        FormInfo1.ImageableArea.bottom = FormInfo1.Size.cy;

        //
        // We'll try to add the form first. If that fails,
        // we assume the form is already there and try to
        // update the form with the new info.
        //

        (VOID) AddForm(pci->hPrinter, 1, (PBYTE) &FormInfo1);
    }

    (VOID) BSetPrinterDataDWord(pci->hPrinter,
                                REGVAL_FORMS_ADDED,
                                pci->pRawData->dwChecksum32);

    return TRUE;
}



BOOL
BInitOrUpgradePrinterProperties(
    PCOMMONINFO pci
    )

/*++

Routine Description:

    Initialize or upgrade printer property information in the registry

Arguments:

    pci - Points to basic printer info

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    BOOL    bResult;

    //
    // Handle PRINTERDATA structure in registry
    //

    bResult = BInitOrUpgradePrinterData(pci);

    //
    // Handle driver-defined forms in the spooler's database
    //

    if (! BAddOrUpgradePrinterForms(pci))
        bResult = FALSE;

    #ifdef PSCRIPT

    //
    // pscript specific initializations
    //

    // Save model-specific NTF filename in registry for NT4 compatibility

    if (! BUpdateModelNtfFilename(pci))
        bResult = FALSE;

    #ifdef WINNT_40

    // Also save the current user locale too.

    if (! BUpdateVMErrorMessageID(pci))
        bResult = FALSE;

    #endif // WINNT_40

    #endif // PSCRIPT

    return bResult;
}



PWSTR
PGetFileDirectory(
    PWSTR   pServerName
    )

/*++

Routine Description:

    Get the name of the directory used by the font downloader
    to store NTF information about downloaded fonts

Arguments:

    pServerName - Name of the print server

Return Value:

    Pointer to the directory used for storing NTF information
    about downloaded fonts, NULL if there is an error

--*/

{
    PWSTR           p, pDir = NULL;
    DWORD           cbNeeded;
    static WCHAR    wszDir[] = FONTDIR;

    //
    // Get the printer driver directory path
    //

    if (GetPrinterDriverDirectory(pServerName, NULL, 1, NULL, 0, &cbNeeded) ||
        GetLastError() != ERROR_INSUFFICIENT_BUFFER ||
        (pDir = MemAlloc(cbNeeded + sizeof(wszDir))) == NULL ||
        !GetPrinterDriverDirectory(pServerName, NULL, 1, (PBYTE) pDir, cbNeeded, &cbNeeded))
    {
        ERR(("GetPrinterDriverDirectory failed: %d\n", GetLastError()));
        MemFree(pDir);
        return NULL;
    }

    //
    // Replace the last component of the directory path (which should be w32...)
    // with \psfont\
    //

    if (p = wcsrchr(pDir, TEXT(PATH_SEPARATOR)))
        wcscpy(p, wszDir);
    else
    {
        WARNING(("Driver directory is not fully-qualified: %ws\n", pDir));
        wcscat(pDir, wszDir);
    }

    return pDir;
}



PWSTR
PConcatFilename(
    PWSTR   pDir,
    PWSTR   pFilename
    )

{
    PWSTR   pBasename;

    //
    // Strip any directory prefix from the input filename
    //

    if (pBasename = wcsrchr(pFilename, TEXT(PATH_SEPARATOR)))
        pBasename++;
    else
        pBasename = pFilename;

    //
    // Concatenate the input directory with the base filename
    //

    if (! (pFilename = MemAlloc(SIZE_OF_STRING(pDir) + SIZE_OF_STRING(pBasename))))
    {
        ERR(("Memory allocation failed\n"));
        return NULL;
    }

    wcscpy(pFilename, pDir);
    wcscat(pFilename, pBasename);

    return pFilename;
}



BOOL
_BPrepareToCopyCachedFile(
    HANDLE      hPrinter,
    PCACHEDFILE pCachedFile,
    PWSTR       pRegKey
    )

/*++

Routine Description:

    Prepare to copy files from the server during a printer-connection
    cache refresh event

Arguments:

    hPrinter - Handle to the printer connection
    pCachedFile - Buffer to store information about cached file

Return Value:

    TRUE if successful, FALSE if there is an error

Note:

    We assume this function is called from within the spooler process
    and with current user's security context. Specifically, we must
    be able to access the server's print$ share at this point.

--*/

{
    PPRINTER_INFO_2 pPrinterInfo2 = NULL;
    PWSTR           pRemoteFilename;
    DWORD           dwSize;

    ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
    pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;

    //
    // Find out the name of the file to copy
    //
#if !defined(PSCRIPT)
    pCachedFile->pFilename = PtstrGetPrinterDataString(hPrinter, pRegKey, &dwSize);
#else
    return TRUE;
#endif

    if (pCachedFile->pFilename == NULL || *pCachedFile->pFilename == NUL)
        return TRUE;

    //
    // Get the remote NTF filename on the server
    //
    // NOTE: We're really like to use level 4 here. But due to bug in the
    // spooler, GetPrinter level 4 doesn't work for printer connections.
    //

    if (! (pPrinterInfo2 = MyGetPrinter(hPrinter, 2)) ||
        ! pPrinterInfo2->pServerName ||
        ! (pCachedFile->pRemoteDir = PGetFileDirectory(pPrinterInfo2->pServerName)) ||
        ! (pCachedFile->pLocalDir = PGetFileDirectory(NULL)) ||
        ! (pRemoteFilename = PConcatFilename(pCachedFile->pRemoteDir, pCachedFile->pFilename)))
    {
        goto exit_prepare_copyfile;
    }

    pCachedFile->hRemoteFile = CreateFile(pRemoteFilename,
                                          GENERIC_READ,
                                          FILE_SHARE_READ,
                                          NULL,
                                          OPEN_EXISTING,
                                          FILE_FLAG_SEQUENTIAL_SCAN,
                                          NULL);

    MemFree(pRemoteFilename);

exit_prepare_copyfile:

    MemFree(pPrinterInfo2);

    if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
    {
        ERR(("Couldn't open remote NTF/FontInfo file: %d\n", GetLastError()));
        _VDisposeCachedFileInfo(pCachedFile);
    }

    return (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE);
}



BOOL
_BCopyCachedFile(
    PCOMMONINFO pci,
    PCACHEDFILE pCachedFile
    )

/*++

Routine Description:

    Copy files from the server during printer-connection cache refresh event

Arguments:

    pci - Points to basic printer information
    pCachedFile - Points to information about cached file

Return Value:

    TRUE if successful, FALSE if there is an error

Note:

    We assume this function is called from within the spooler process
    and with system's security context. Specifically, we must
    be able to write into local machines' printer driver directory.

--*/

#define BUFFER_SIZE  4096

{
    HANDLE  hLocalFile;
    PWSTR   pLocalFilename = NULL;
    PVOID   pBuffer = NULL;
    BOOL    bResult = FALSE;
    DWORD   dwCount;

    //
    // We don't have any file to copy
    //

    if (pCachedFile->hRemoteFile == INVALID_HANDLE_VALUE)
        return TRUE;

    //
    // Get the name for the local copy of the NTF file
    // and allocate temporary buffer
    //

    ASSERT(BUFFER_SIZE >= MAX_PATH * sizeof(WCHAR));

    if (! (pLocalFilename = PConcatFilename(pCachedFile->pLocalDir, pCachedFile->pFilename)) ||
        ! (pBuffer = MemAlloc(BUFFER_SIZE)))
    {
        goto exit_copyfile;
    }

    // Make sure the local directory is created

    (VOID) CreateDirectory(pCachedFile->pLocalDir, NULL);

    for (dwCount=0; dwCount < 2; dwCount++)
    {
        hLocalFile = CreateFile(pLocalFilename,
                                GENERIC_WRITE,
                                0,
                                NULL,
                                CREATE_ALWAYS,
                                FILE_FLAG_SEQUENTIAL_SCAN,
                                NULL);

        if (hLocalFile != INVALID_HANDLE_VALUE)
            break;

        if (dwCount == 0)
        {
            //
            // If this is our first try, then attempt to move
            // the existing file to a temporary file and set
            // it to be delete-on-reboot.
            //

            #ifdef PSCRIPT
            if (! GetTempFileName(pCachedFile->pLocalDir, L"NTF", 0, pBuffer) ||
            #else
            if (! GetTempFileName(pCachedFile->pLocalDir, L"FON", 0, pBuffer) ||
            #endif
                ! MoveFileEx(pLocalFilename, pBuffer, MOVEFILE_REPLACE_EXISTING) ||
                ! MoveFileEx(pBuffer, NULL, MOVEFILE_DELAY_UNTIL_REBOOT))
            {
                break;
            }
        }
    }

    if (hLocalFile != INVALID_HANDLE_VALUE)
    {
        while (ReadFile(pCachedFile->hRemoteFile, pBuffer, BUFFER_SIZE, &dwCount, NULL))
        {
            //
            // Have we reached end-of-file?
            //

            if (dwCount == 0)
            {
                bResult = TRUE;
                break;
            }

            if (! WriteFile(hLocalFile, pBuffer, dwCount, &dwCount, NULL))
                break;
        }

        CloseHandle(hLocalFile);

        //
        // If file copying failed, be sure to delete the temporary file
        //

        if (! bResult)
            DeleteFile(pLocalFilename);
    }

exit_copyfile:

    MemFree(pLocalFilename);
    MemFree(pBuffer);

    if (! bResult)
        ERR(("Couldn't copy remote NTF/FontInfo file: %d\n", GetLastError()));

    return bResult;
}



VOID
_VDisposeCachedFileInfo(
    PCACHEDFILE pCachedFile
    )

/*++

Routine Description:

    Clean up after copying files from the server
    during printer-connection cache refresh

Arguments:

    pCachedFile - Points to information about cached file

Return Value:

    NONE

--*/

{
    if (pCachedFile->hRemoteFile != INVALID_HANDLE_VALUE)
        CloseHandle(pCachedFile->hRemoteFile);

    MemFree(pCachedFile->pFilename);
    MemFree(pCachedFile->pRemoteDir);
    MemFree(pCachedFile->pLocalDir);

    ZeroMemory(pCachedFile, sizeof(CACHEDFILE));
    pCachedFile->hRemoteFile = INVALID_HANDLE_VALUE;
}




#ifdef UNIDRV

VOID
DeleteFontIntallerFile(
    HANDLE hPrinter
    )

/*++

Routine Description:

    Delete font installer file when printer is deleted

Arguments:

    hPrinter - Handle to printer

Return Value:

    NONE

--*/

{
    PWSTR pFilename;
    PWSTR pLocalDir;
    PWSTR pLocalFilename = NULL;

    pFilename = PtstrGetPrinterDataString(hPrinter, REGVAL_FONTFILENAME, NULL);

    if (!pFilename || !*pFilename)
        return;

    if (!(pLocalDir = PGetFileDirectory(NULL)))
        goto exit_deletefile;

    if (!(pLocalFilename = PConcatFilename(pLocalDir, pFilename)))
        goto exit_deletefile;

    DeleteFile(pLocalFilename);

exit_deletefile:

    MemFree(pFilename);
    MemFree(pLocalDir);
    MemFree(pLocalFilename);

    return;
}

#endif