/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    faxmon.c

Abstract:

    Implementation of the following print monitor entry points:
        InitializePrintMonitor
        OpenPort
        ClosePort
        StartDocPort
        EndDocPort
        WritePort
        ReadPort

Environment:

    Windows NT fax print monitor

Revision History:

    05/09/96 -davidx-
        Remove caching of ports from the monitor.

    02/22/96 -davidx-
        Created it.

    mm/dd/yy -author-
        description

--*/

#include "faxmon.h"
#include "tiff.h"
#include "faxreg.h"
#include <splapip.h>

//
// Determine whether we're at the beginning of a TIFF file
//

#define ValidTiffFileHeader(p) \
        (((LPSTR) (p))[0] == 'I' && ((LPSTR) (p))[1] == 'I' && \
         ((PBYTE) (p))[2] == 42  && ((PBYTE) (p))[3] == 0)

//
// Read a DWORD value from an unaligned address
//

#define ReadUnalignedDWord(p) *((DWORD UNALIGNED *) (p))

//
// Write a DWORD value to an unaligned address
//

#define WriteUnalignedDWord(p, value) (*((DWORD UNALIGNED *) (p)) = (value))

//
// Fax monitor name string
//

TCHAR   faxMonitorName[CCHDEVICENAME] = TEXT("Windows NT Fax Monitor");

//
// DLL instance handle
//

HANDLE  ghInstance = NULL;

//
// Retry parameters when failing to connect to the fax service
//  default = infinite retry with 5 seconds interval
//

DWORD   connectRetryCount = 0;
DWORD   connectRetryInterval = 5;



BOOL
DllEntryPoint(
    HANDLE      hModule,
    ULONG       ulReason,
    PCONTEXT    pContext
    )

/*++

Routine Description:

    DLL initialization procedure.

Arguments:

    hModule - DLL instance handle
    ulReason - Reason for the call
    pContext - Pointer to context (not used by us)

Return Value:

    TRUE if DLL is initialized successfully, FALSE otherwise.

--*/

{
    switch (ulReason) {

    case DLL_PROCESS_ATTACH:

        ghInstance = hModule;
        LoadString(ghInstance, IDS_FAX_MONITOR_NAME, faxMonitorName, CCHDEVICENAME);
        break;

    case DLL_PROCESS_DETACH:

        break;
    }

    return TRUE;
}



LPMONITOREX
InitializePrintMonitor(
    LPTSTR  pRegistryRoot
    )

/*++

Routine Description:

    Initialize the print monitor

Arguments:

    pRegistryRoot = Points to a string that specifies the registry root for the monitor

Return Value:

    Pointer to a MONITOREX structure which contains function pointers
    to other print monitor entry points. NULL if there is an error.

--*/

{
    static MONITOREX faxmonFuncs = {

        sizeof(MONITOR),
        {
            FaxMonEnumPorts,
            FaxMonOpenPort,
            NULL,                   // OpenPortEx
            FaxMonStartDocPort,
            FaxMonWritePort,
            FaxMonReadPort,
            FaxMonEndDocPort,
            FaxMonClosePort,
            FaxMonAddPort,
            FaxMonAddPortEx,
            FaxMonConfigurePort,
            FaxMonDeletePort,
            NULL,                   // GetPrinterDataFromPort
            NULL,                   // SetPortTimeOuts
        }
    };


    Trace("InitializePrintMonitor");

    //
    // Get fax service connection retry parameters from the registry
    //

    if (pRegistryRoot) {

        HKEY    hRegKey;
        LONG    status;

        status = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
                                pRegistryRoot,
                                0,
                                NULL,
                                0,
                                KEY_READ | KEY_WRITE,
                                NULL,
                                &hRegKey,
                                NULL);

        if (status == ERROR_SUCCESS) {

            connectRetryCount =
                GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_COUNT, connectRetryCount);

            connectRetryInterval =
                GetRegistryDWord(hRegKey, REGVAL_CONNECT_RETRY_INTERVAL, connectRetryInterval);

            RegCloseKey(hRegKey);
        }
    }

    return &faxmonFuncs;
}



BOOL
FaxMonOpenPort(
    LPTSTR  pPortName,
    PHANDLE pHandle
    )

/*++

Routine Description:

    Provides a port for a newly connected printer

Arguments:

    pName - Points to a string that specifies the port name
    pHandle - Returns a handle to the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PFAXPORT         pFaxPort = NULL;


    Trace("OpenPort");
    Assert(pHandle != NULL && pPortName != NULL);

    //
    // Get information about the specified port
    //

    if ((pFaxPort = MemAllocZ(sizeof(FAXPORT))) &&
        (pPortName = DuplicateString(FAX_PORT_NAME)))
    {
        pFaxPort->startSig = pFaxPort->endSig = pFaxPort;
        pFaxPort->pName = pPortName;
        pFaxPort->hFile = INVALID_HANDLE_VALUE;

    } else {

        MemFree(pFaxPort);
        pFaxPort = NULL;
    }

    *pHandle = (HANDLE) pFaxPort;
    return (*pHandle != NULL);
}



VOID
FreeFaxJobInfo(
    PFAXPORT    pFaxPort
    )

/*++

Routine Description:

    Free up memory used for maintaining information about the current job

Arguments:

    pFaxPort - Points to a fax port structure

Return Value:

    NONE

--*/

{
    //
    // Close and delete the temporary file if necessary
    //

    if (pFaxPort->hFile != INVALID_HANDLE_VALUE) {

        CloseHandle(pFaxPort->hFile);
        pFaxPort->hFile = INVALID_HANDLE_VALUE;
    }

    if (pFaxPort->pFilename) {

        DeleteFile(pFaxPort->pFilename);
        MemFree(pFaxPort->pFilename);
        pFaxPort->pFilename = NULL;
    }

    if (pFaxPort->hPrinter) {

        ClosePrinter(pFaxPort->hPrinter);
        pFaxPort->hPrinter = NULL;
    }

    MemFree(pFaxPort->pPrinterName);
    pFaxPort->pPrinterName = NULL;

    MemFree(pFaxPort->pParameters);
    pFaxPort->pParameters = NULL;
    ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));

    //
    // Disconnect from the fax service if necessary
    //

    if (pFaxPort->hFaxSvc) {

        if (! pFaxPort->pFaxClose(pFaxPort->hFaxSvc)) {
            Error(("FaxClose failed: %d\n", GetLastError()));
        }

        FreeLibrary( pFaxPort->hWinFax );

        pFaxPort->hFaxSvc = NULL;
        pFaxPort->pFaxConnectFaxServerW = NULL;
        pFaxPort->pFaxClose = NULL;
        pFaxPort->pFaxSendDocumentW = NULL;
        pFaxPort->pFaxAccessCheck = NULL;

    }
}



BOOL
FaxMonClosePort(
    HANDLE  hPort
    )

/*++

Routine Description:

    Closes the port specified by hPort when there are no printers connected to it

Arguments:

    hPort - Specifies the handle of the port to be close

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PFAXPORT    pFaxPort = (PFAXPORT) hPort;

    Trace("ClosePort");

    //
    // Make sure we have a valid handle
    //

    if (! ValidFaxPort(pFaxPort)) {

        Error(("Trying to close an invalid fax port handle\n"));
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    //
    // Free up memory used for maintaining information about the current job
    //

    FreeFaxJobInfo(pFaxPort);

    MemFree(pFaxPort->pName);
    MemFree(pFaxPort);
    return TRUE;
}



LPTSTR
CreateTempFaxFile(
    VOID
    )

/*++

Routine Description:

    Create a temporary file in the system spool directory for storing fax data

Arguments:

    NONE

Return Value:

    Pointer to the name of the newly created temporary file
    NULL if there is an error

--*/

{
    //TCHAR   spoolDir[MAX_PATH];
    //HANDLE  hServer;
    LPTSTR  pFilename;

    TCHAR   TempDir[MAX_PATH];
    TCHAR   FileName[MAX_PATH];

    //
    // Allocate a memory buffer for holding the temporary filename
    //

    if (pFilename = MemAlloc(sizeof(TCHAR) * MAX_PATH)) {
    
        if (!GetTempPath(sizeof(TempDir)/sizeof(TCHAR),TempDir)||
            !GetTempFileName(TempDir, TEXT("fax"), 0, FileName))
        {
            MemFree(pFilename);
            pFilename = NULL;
        }  else {
            lstrcpy(pFilename,FileName);
        }

    }

    if (! pFilename)
        Error(("Failed to create temporary file in the spool directory\n"));

    return pFilename;
}



BOOL
OpenTempFaxFile(
    PFAXPORT    pFaxPort,
    BOOL        doAppend
    )

/*++

Routine Description:

    Open a handle to the current fax job file associated with a port

Arguments:

    pFaxPort - Points to a fax port structure
    doAppend - Specifies whether to discard existing data in the file or
        append new data to it

Return Value:

    TRUE if successful, FALSE otherwise

--*/

{
    DWORD   creationFlags;

    Assert(pFaxPort->pFilename && pFaxPort->hFile == INVALID_HANDLE_VALUE);
    Verbose(("Temporary fax job file: %ws\n", pFaxPort->pFilename));

    //
    // Open the file for reading and writing
    //

    creationFlags = doAppend ? OPEN_ALWAYS : (OPEN_ALWAYS | TRUNCATE_EXISTING);

    pFaxPort->hFile = CreateFile(pFaxPort->pFilename,
                                 GENERIC_READ | GENERIC_WRITE,
                                 0,
                                 NULL,
                                 creationFlags,
                                 FILE_ATTRIBUTE_NORMAL,
                                 NULL);

    //
    // If we're appending, then move the file pointer to end of file
    //

    if (doAppend && pFaxPort->hFile != INVALID_HANDLE_VALUE &&
        SetFilePointer(pFaxPort->hFile, 0, NULL, FILE_END) == 0xffffffff)
    {
        Error(("SetFilePointer failed: %d\n", GetLastError()));

        CloseHandle(pFaxPort->hFile);
        pFaxPort->hFile = INVALID_HANDLE_VALUE;
    }

    return (pFaxPort->hFile != INVALID_HANDLE_VALUE);
}



LPCTSTR
ExtractFaxTag(
    LPCTSTR      pTagKeyword,
    LPCTSTR      pTaggedStr,
    INT        *pcch
    )

/*++

Routine Description:

    Find the value of for the specified tag in a tagged string.

Arguments:

    pTagKeyword - specifies the interested tag keyword
    pTaggedStr - points to the tagged string to be searched
    pcch - returns the length of the specified tag value (if found)

Return Value:

    Points to the value for the specified tag.
    NULL if the specified tag is not found

NOTE:

    Tagged strings have the following form:
        <tag>value<tag>value

    The format of tags is defined as:
        <$FAXTAG$ tag-name>

    There is exactly one space between the tag keyword and the tag name.
    Characters in a tag are case-sensitive.

--*/

{
    LPCTSTR  pValue;

    if (pValue = _tcsstr(pTaggedStr, pTagKeyword)) {

        pValue += _tcslen(pTagKeyword);

        if (pTaggedStr = _tcsstr(pValue, FAXTAG_PREFIX))
            *pcch = (INT)(pTaggedStr - pValue);
        else
            *pcch = _tcslen(pValue);
    }

    return pValue;
}



BOOL
GetJobInfo(
    PFAXPORT    pFaxPort,
    DWORD       jobId
    )

/*++

Routine Description:

    Retrieve recipient information from the devmode associated with the job

Arguments:

    pFaxPort - Points to a fax port structure
    jobId - Specifies the current job ID

Return Value:

    TRUE if the job parameters are successfully retrieved or
    the fax job is from a downlevel fax client.

    FALSE if there is an error.

--*/

{
    JOB_INFO_2 *pJobInfo2;
    LPCTSTR      pParameters = NULL;

    //
    // Retrieve the parameter string associated with the specified job.
    // If there is no job parameter or the parameter contains the tag
    // <$FAXTAG$ DOWNLEVEL>, then we assume the job comes from downlevel client.
    //

    ZeroMemory(&pFaxPort->jobParam, sizeof(pFaxPort->jobParam));
    pFaxPort->jobParam.SizeOfStruct = sizeof( FAX_JOB_PARAM );

    if ((pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, jobId)) == NULL ||
        (pParameters = pJobInfo2->pParameters) == NULL ||
        _tcsstr(pParameters, FAXTAG_DOWNLEVEL_CLIENT) != NULL)
    {
        MemFree(pJobInfo2);
        return TRUE;
    }

    if ((pFaxPort->pParameters = DuplicateString(pParameters)) != NULL) {

        //
        // Tags used to pass information about fax jobs
        //

        static LPCTSTR faxtagNames[NUM_JOBPARAM_TAGS] = {

            FAXTAG_RECIPIENT_NUMBER,
            FAXTAG_RECIPIENT_NAME,
            FAXTAG_TSID,
            FAXTAG_SENDER_NAME,
            FAXTAG_SENDER_COMPANY,
            FAXTAG_SENDER_DEPT,
            FAXTAG_BILLING_CODE,
            FAXTAG_WHEN_TO_SEND,
            FAXTAG_SEND_AT_TIME,
            FAXTAG_PROFILE_NAME,
            FAXTAG_EMAIL_NAME
        };

        LPTSTR WhenToSend = NULL;
        LPTSTR SendAtTime = NULL;
        LPTSTR DeliveryReportType = NULL;

        LPTSTR *fieldStr[NUM_JOBPARAM_TAGS] = {

            (LPTSTR *)&pFaxPort->jobParam.RecipientNumber,
            (LPTSTR *)&pFaxPort->jobParam.RecipientName,
            (LPTSTR *)&pFaxPort->jobParam.Tsid,
            (LPTSTR *)&pFaxPort->jobParam.SenderName,
            (LPTSTR *)&pFaxPort->jobParam.SenderCompany,
            (LPTSTR *)&pFaxPort->jobParam.SenderDept,
            (LPTSTR *)&pFaxPort->jobParam.BillingCode,
            &WhenToSend,
            &SendAtTime,
            (LPTSTR *)&pFaxPort->jobParam.DeliveryReportAddress,
            &DeliveryReportType
        };

        INT     fieldLen[NUM_JOBPARAM_TAGS];
        INT     count;

        pParameters = pFaxPort->pParameters;
        Verbose(("JOB_INFO_2.pParameter = %ws\n", pParameters));

        //
        // Extract individual fields out of the tagged string
        //

        for (count=0; count < NUM_JOBPARAM_TAGS; count++) {

            *fieldStr[count] = (LPTSTR)ExtractFaxTag(faxtagNames[count],
                                             pParameters,
                                             &fieldLen[count]);
        }

        //
        // Null-terminate each field
        //

        for (count=0; count < NUM_JOBPARAM_TAGS; count++) {
            if (*fieldStr[count]) {
                (*fieldStr[count])[fieldLen[count]] = NUL;
            }
        }

        if (WhenToSend) {
            if (_tcsicmp( WhenToSend, TEXT("cheap") ) == 0) {
                pFaxPort->jobParam.ScheduleAction = JSA_DISCOUNT_PERIOD;
            } else if (_tcsicmp( WhenToSend, TEXT("at") ) == 0) {
                pFaxPort->jobParam.ScheduleAction = JSA_SPECIFIC_TIME;
            }
        }

        if (SendAtTime) {
            if (_tcslen(SendAtTime) == 5 && SendAtTime[2] == L':' &&
                _istdigit(SendAtTime[0]) && _istdigit(SendAtTime[1]) &&
                _istdigit(SendAtTime[3]) && _istdigit(SendAtTime[4]))
            {
                DWORDLONG FileTime;
                SYSTEMTIME LocalTime;
                INT Minutes;
                INT SendMinutes;

                SendAtTime[2] = 0;
                
                //
                // Calculate the number of minutes from now to send and add that to the current time.
                //
                
                GetLocalTime( &LocalTime );
                SystemTimeToFileTime( &LocalTime, (LPFILETIME) &FileTime );

                SendMinutes = min(23,_ttoi( &SendAtTime[0] )) * 60 + min(59,_ttoi( &SendAtTime[3] ));

                Minutes = LocalTime.wHour * 60 + LocalTime.wMinute;

                Minutes = SendMinutes - Minutes;


                // Account for passing midnight
                //
                if (Minutes < 0) {
                    Minutes += 24 * 60;
                }
                
                FileTime += (DWORDLONG)(Minutes * 60I64 * 1000I64 * 1000I64 * 10I64);

                FileTimeToSystemTime((LPFILETIME) &FileTime, &pFaxPort->jobParam.ScheduleTime );
    
                SendAtTime[2] = L':';
                
            }
        }

        if (DeliveryReportType) {
            if (_tcsicmp( DeliveryReportType, TEXT("email") ) == 0) {
                pFaxPort->jobParam.DeliveryReportType = DRT_EMAIL;
            } else if (_tcsicmp( DeliveryReportType, TEXT("inbox") ) == 0) {
                pFaxPort->jobParam.DeliveryReportType = DRT_INBOX;
            }
        }

        if (pFaxPort->jobParam.RecipientNumber == NULL) {

            Error(("Missing recipient phone number.\n"));
            SetJob(pFaxPort->hPrinter, jobId, 0, NULL, JOB_CONTROL_PAUSE);
            SetLastError(ERROR_INVALID_PARAMETER);
        }
    }

    MemFree(pJobInfo2);

    return (pFaxPort->jobParam.RecipientNumber != NULL);
}



BOOL
FaxMonStartDocPort(
    HANDLE  hPort,
    LPTSTR  pPrinterName,
    DWORD   JobId,
    DWORD   Level,
    LPBYTE  pDocInfo
    )

/*++

Routine Description:

    Spooler calls this function to start a new print job on the port

Arguments:

    hPort - Identifies the port
    pPrinterName - Specifies the name of the printer to which the job is being sent
    JobId - Identifies the job being sent by the spooler
    Level - Specifies the DOC_INFO_x level
    pDocInfo - Points to the document information

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PFAXPORT                pFaxPort = (PFAXPORT) hPort;


    Verbose(("Entering StartDocPort: %d ...\n", JobId));

    //
    // Make sure we have a valid handle
    //

    if (! ValidFaxPort(pFaxPort)) {

        Error(("StartDocPort is given an invalid fax port handle\n"));
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    //
    // Check if we're at the beginning of a series of chained jobs
    //

    if (! pFaxPort->hFaxSvc) {

        PJOB_INFO_1 pJobInfo1;
        PORT_INFO_3 portInfo3;
        HANDLE      hPrinter = NULL;
        BOOL        offline = FALSE;
        DWORD       count = connectRetryCount;
        DWORD       jobStatus = 0;

        Assert(pFaxPort->pPrinterName == NULL &&
               pFaxPort->hPrinter == NULL &&
               pFaxPort->pParameters == NULL &&
               pFaxPort->pFilename == NULL &&
               pFaxPort->hFile == INVALID_HANDLE_VALUE);

        //
        // load the winfax dll
        //

        pFaxPort->hWinFax = LoadLibrary( L"winfax.dll" );
        if (pFaxPort->hWinFax == NULL) {
            Error(("LoadLibrary failed loading winfax.dll\n"));
            return FALSE;
        }

        //
        // get the function addresses
        //

        pFaxPort->pFaxConnectFaxServerW = (PFAXCONNECTFAXSERVERW) GetProcAddress( pFaxPort->hWinFax, "FaxConnectFaxServerW" );
        pFaxPort->pFaxClose = (PFAXCLOSE) GetProcAddress( pFaxPort->hWinFax, "FaxClose" );
        pFaxPort->pFaxSendDocumentW = (PFAXSENDDOCUMENTW) GetProcAddress( pFaxPort->hWinFax, "FaxSendDocumentW" );
        pFaxPort->pFaxAccessCheck = (PFAXACCESSCHECK) GetProcAddress( pFaxPort->hWinFax, "FaxAccessCheck" );

        if (pFaxPort->pFaxConnectFaxServerW == NULL || 
            pFaxPort->pFaxClose == NULL || 
            pFaxPort->pFaxSendDocumentW == NULL ||
            pFaxPort->pFaxAccessCheck == NULL) {
            Error(("GetProcAddress failed loading winfax.dll\n"));
            return FALSE;
        }

        //
        // Connect to the fax service and obtain a session handle
        //

        while (! pFaxPort->pFaxConnectFaxServerW(NULL, &pFaxPort->hFaxSvc)) {

            Error(("FaxConnectFaxServer failed: %d\n", GetLastError()));
            pFaxPort->hFaxSvc = NULL;

            if (! offline) {

                portInfo3.dwStatus = PORT_STATUS_OFFLINE;
                portInfo3.pszStatus = NULL;
                portInfo3.dwSeverity = PORT_STATUS_TYPE_WARNING;

                if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3))
                    Error(("SetPort failed: %d\n", GetLastError()));
            }

            offline = TRUE;
            Sleep(connectRetryInterval * 1000);

            //
            // Check if the job has been deleted or restarted
            //

            if (!hPrinter && !OpenPrinter(pPrinterName, &hPrinter, NULL)) {

                Error(("OpenPrinter failed: %d\n", GetLastError()));
                hPrinter = NULL;

            } else if (pJobInfo1 = MyGetJob(hPrinter, 1, JobId)) {

                jobStatus = pJobInfo1->Status;

            }

            MemFree(pJobInfo1);

            if (--count == 0 || (jobStatus & (JOB_STATUS_DELETING|
                                              JOB_STATUS_DELETED|
                                              JOB_STATUS_RESTART)))
            {
                break;
            }
        }

        //
        // Remove the offline status on the port
        //

        if (offline) {

            portInfo3.dwStatus = 0;
            portInfo3.pszStatus = NULL;
            portInfo3.dwSeverity = PORT_STATUS_TYPE_INFO;

            if (! SetPort(NULL, pFaxPort->pName, 3, (LPBYTE) &portInfo3)) {
                Error(("SetPort failed: %d\n", GetLastError()));
            }
        }

        if (hPrinter) {
            ClosePrinter(hPrinter);
        }

        if (pFaxPort->hFaxSvc) {
            if (!pFaxPort->pFaxAccessCheck(pFaxPort->hFaxSvc, FAX_JOB_SUBMIT) ) {
                FreeFaxJobInfo(pFaxPort);
                Error(("FaxAccessCheck failed : %d\n", GetLastError() ));
                SetLastError(ERROR_ACCESS_DENIED);
                return FALSE;
            }
      //      HANDLE      hToken;

            //
            // The monitor runs in the context of the current job's owner.
            // In order to create temporary files in the spool directory,
            // we need to revert to the spooler context first.
            //

/*            if (! (hToken = RevertToPrinterSelf()))
                Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
*/
            //
            // Remember the printer name because we'll need it at EndDocPort time.
            // Get a temporary filename and open it for reading and writing.
            // Remember other job related information
            //

            if (! (pFaxPort->pPrinterName = DuplicateString(pPrinterName)) ||
                ! OpenPrinter(pPrinterName, &pFaxPort->hPrinter, NULL) ||
                ! GetJobInfo(pFaxPort, JobId) ||
                ! (pFaxPort->pFilename = CreateTempFaxFile()) ||
                ! OpenTempFaxFile(pFaxPort, FALSE))
            {
                FreeFaxJobInfo(pFaxPort);
            } else
                pFaxPort->jobId = pFaxPort->nextJobId = JobId;

            //
            // Switch back to the original context if necessary
            //

/*            if (hToken && !ImpersonatePrinterClient(hToken))
                Error(("ImpersonatePrinterClient failed: %d\n", GetLastError())); */
        }

    } else {

        Assert(pFaxPort->jobId == JobId);
    }

    return (pFaxPort->hFaxSvc != NULL);
}



INT
FixUpFaxFile(
    PFAXPORT    pFaxPort
    )

/*++

Routine Description:

    Fixed up the saved print job data into a well-formed TIFF file

Arguments:

    pFaxPort - Points to a fax port structure

Return Value:

    error code FAXERR_*

--*/

{
    DWORD   fileSize;
    PBYTE   pFileEnd, pFileHdr;
    PBYTE   pFileView = NULL;
    HANDLE  hFileMap = NULL;
    INT     result = FAXERR_BAD_TIFF;

    //
    // Get the size of print job file
    //

    FlushFileBuffers(pFaxPort->hFile);

    if ((fileSize = GetFileSize(pFaxPort->hFile, NULL)) == 0)
        return FAXERR_IGNORE;

    if (fileSize == 0xffffffff)
        return FAXERR_FATAL;

    __try {

        //
        // Map the fax job file into memory
        //

        if ((hFileMap = CreateFileMapping(pFaxPort->hFile, NULL, PAGE_READWRITE, 0, 0, NULL)) &&
            (pFileHdr = pFileView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, fileSize)) &&
            ValidTiffFileHeader(pFileHdr))
        {
            DWORD   ifdOffset, maxOffset, fileOffset;
            PBYTE   pIfdOffset;

            //
            // A fax print job may contain multiple TIFF files. Each each iteration
            // of the outer loop below deals with a single embedded TIFF file.
            //

            pFileEnd = pFileHdr + fileSize;
            ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));

            do {

                Verbose(("Reading embedded TIFF file ...\n"));
                maxOffset = 0;
                fileOffset = (DWORD)(pFileHdr - pFileView);

                //
                // Each iteration of the following loops processes one IFD
                // from an embedded TIFF file.
                //

                do {

                    PTIFF_IFD           pIfd;
                    PTIFF_TAG           pIfdEntry;
                    INT                 ifdCount;
                    DWORD               size, index, stripCount = 0;
                    PDWORD              pStripOffsets = NULL;

                    pIfd = (PTIFF_IFD) (pFileHdr + ifdOffset);
                    Assert( (PBYTE) pIfd < pFileEnd);
                    if ((PBYTE) pIfd >= pFileEnd) {
                        result = FAXERR_FATAL;
                        __leave;
                    }
                    ifdOffset += sizeof(WORD) + pIfd->wEntries * sizeof(TIFF_TAG);
                    pIfdOffset = pFileHdr + ifdOffset;

                    Assert(pIfdOffset < pFileEnd);
                    if (pIfdOffset >= pFileEnd) {
                        result = FAXERR_FATAL;
                        __leave;
                    }

                    if ((ifdOffset + sizeof(DWORD)) > maxOffset)
                        maxOffset = ifdOffset + sizeof(DWORD);

                    //
                    // We should add the file offset to any non-zero IFD offset
                    //

                    if ((ifdOffset = ReadUnalignedDWord(pIfdOffset)) != 0)
                        WriteUnalignedDWord(pIfdOffset, ifdOffset + fileOffset);

                    //
                    // Now go through each IFD entry and calculate the largest offset
                    //

                    pIfdEntry = (PTIFF_TAG) ((PBYTE) pIfd + sizeof(WORD));
                    ifdCount = pIfd->wEntries;

                    Verbose(("  Reading IFD: %d entries ...\n", ifdCount));

                    while (ifdCount-- > 0) {

                        //
                        // Figure the size of various TIFF data types
                        //

                        switch (pIfdEntry->DataType) {

                        case TIFF_ASCII:
                        case TIFF_BYTE:
                        case TIFF_SBYTE:
                        case TIFF_UNDEFINED:

                            size = 1;
                            break;

                        case TIFF_SHORT:
                        case TIFF_SSHORT:

                            size = 2;
                            break;

                        case TIFF_LONG:
                        case TIFF_SLONG:
                        case TIFF_FLOAT:

                            size = 4;
                            break;

                        case TIFF_RATIONAL:
                        case TIFF_SRATIONAL:
                        case TIFF_DOUBLE:

                            size = 8;
                            break;

                        default:

                            Warning(("Unknown TIFF data type: %d\n", pIfdEntry->DataType));
                            size = 1;
                            break;
                        }

                        //
                        // Look for StripOffsets and StripByteCounts tags
                        //

                        if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS ||
                            pIfdEntry->TagId == TIFFTAG_STRIPBYTECOUNTS)
                        {
                            DWORD   n = pIfdEntry->DataCount;

                            if ((pIfdEntry->DataType == TIFF_LONG) &&
                                (stripCount == 0 || stripCount == n) &&
                                (pStripOffsets || (pStripOffsets = MemAllocZ(sizeof(DWORD)*n))))
                            {
                                if ((stripCount = n) == 1) {

                                    pStripOffsets[0] += pIfdEntry->DataOffset;

                                    if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
                                        pIfdEntry->DataOffset += fileOffset;

                                } else {

                                    DWORD UNALIGNED *p;

                                    Verbose(("Multiple strips per page: %d\n", n));

                                    p = (DWORD UNALIGNED *) (pFileHdr + pIfdEntry->DataOffset);

                                    for (index=0; index < stripCount; index++) {

                                        n = *p;
                                        pStripOffsets[index] += n;

                                        if (pIfdEntry->TagId == TIFFTAG_STRIPOFFSETS)
                                            *p = n + fileOffset;

                                        p = (DWORD UNALIGNED *) ((LPBYTE) p + sizeof(DWORD));
                                    }
                                }

                            } else
                                Error(("Bad StripOffsets/StripByteCounts tag\n"));
                        }

                        //
                        // For a composite value, IFDENTRY.value is an offset
                        //

                        if (size * pIfdEntry->DataCount > sizeof(DWORD)) {

                            if (pIfdEntry->DataOffset > maxOffset)
                                maxOffset = pIfdEntry->DataOffset;

                            pIfdEntry->DataOffset += fileOffset;
                        }

                        pIfdEntry++;
                    }

                    //
                    // Make sure to skip the image data when search for the next file
                    //

                    if (pStripOffsets) {

                        for (index=0; index < stripCount; index++) {

                            if (pStripOffsets[index] > maxOffset)
                                maxOffset = pStripOffsets[index];
                        }

                        MemFree(pStripOffsets);
                    }

                } while (ifdOffset);

                //
                // Search for the beginning of next TIFF file
                //

                pFileHdr += maxOffset;

                while (pFileHdr < pFileEnd) {

                    if (ValidTiffFileHeader(pFileHdr)) {

                        //
                        // Modify the offset in the last IFD
                        //

                        ifdOffset = ReadUnalignedDWord(pFileHdr + sizeof(DWORD));
                        WriteUnalignedDWord(pIfdOffset, ifdOffset + (DWORD)(pFileHdr - pFileView));
                        break;
                    }

                    pFileHdr++;
                }

            } while (pFileHdr < pFileEnd);

            result = FAXERR_NONE;
        }

    } __finally {

        //
        // Perform necessary cleanup before returning to caller
        //

        if (pFileView)
            UnmapViewOfFile(pFileView);

        if (hFileMap)
            CloseHandle(hFileMap);

        CloseHandle(pFaxPort->hFile);
        pFaxPort->hFile = INVALID_HANDLE_VALUE;
    }

    return result;
}



INT
CheckJobRestart(
    PFAXPORT    pFaxPort
    )

/*++

Routine Description:

    Check if the job has been restarted.
    If not, get the ID of the next job in the chain.

Arguments:

    pFaxPort - Points to a fax port structure

Return Value:

    FAXERR_RESTART or FAXERR_NONE

--*/

{
    JOB_INFO_3 *pJobInfo3;
    JOB_INFO_2 *pJobInfo2;
    INT         status = FAXERR_NONE;

    //
    // If not, get the ID of the next job in the chain.
    //

    Verbose(("Job chain: id = %d\n", pFaxPort->nextJobId));

    if (pJobInfo3 = MyGetJob(pFaxPort->hPrinter, 3, pFaxPort->jobId)) {

        pFaxPort->nextJobId = pJobInfo3->NextJobId;
        MemFree(pJobInfo3);

    } else
        pFaxPort->nextJobId = 0;

    //
    // Determine whether the job has been restarted or deleted
    //

    if (pJobInfo2 = MyGetJob(pFaxPort->hPrinter, 2, pFaxPort->jobId)) {

        if (pJobInfo2->Status & (JOB_STATUS_RESTART | JOB_STATUS_DELETING))
            status = FAXERR_RESTART;

        MemFree(pJobInfo2);
    }

    return status;
}


BOOL
FaxMonEndDocPort(
    HANDLE  hPort
    )

/*++

Routine Description:

    Spooler calls this function at the end of a print job

Arguments:

    hPort - Identifies the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PFAXPORT    pFaxPort = (PFAXPORT) hPort;
    INT         status;
    LPTSTR      pAtSign, pNewRecipName = NULL;
    //HANDLE      hToken;
    DWORD       FaxJobId;
    BOOL        Rslt;
    JOB_INFO_2  *pJobInfo2;


    Trace("EndDocPort");

    //
    // Make sure we have a valid handle
    //

    if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {

        Error(("EndDocPort is given an invalid fax port handle\n"));
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    //
    // Check if the job has been restarted. If not, get the ID of
    // the next job in the chain.
    //

    if ((status = CheckJobRestart(pFaxPort)) != FAXERR_NONE)
        goto ExitEndDocPort;

    //
    // Check if we're at the end of a job chain
    //

    if (pFaxPort->nextJobId != 0 && pFaxPort->pParameters != NULL) {

        SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
        return TRUE;
    }

    //
    // The monitor runs in the context of the current job's owner.
    // In order to create temporary files in the spool directory,
    // we need to revert to the spooler context first.
    //

/*    if (! (hToken = RevertToPrinterSelf()))
        Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
*/
    //
    // Check if we're dealing with fax jobs from win31 or win95 clients
    //

    if ((pFaxPort->pParameters == NULL) &&
        (status = ProcessDownlevelFaxJob(pFaxPort)) != FAXERR_NONE)
    {
        goto ExitEndDocPort;
    }

    //
    // Fix up the temporary fax data into a properly formatted TIFF file.
    //

    if ((status = FixUpFaxFile(pFaxPort)) != FAXERR_NONE) {
        goto ExitEndDocPort;
    }

    //
    // Call the fax service to send the TIFF file
    //

    #if DBG

    if (_debugLevel > 0) {

        DbgPrint("Send document to fax service:\n");
        DbgPrint("  Printer Name: %ws\n", pFaxPort->pPrinterName);
        DbgPrint("  Job ID: %d\n", pFaxPort->jobId);
        DbgPrint("  File Name: %ws\n", pFaxPort->pFilename);
        DbgPrint("  Recipient Number: %ws\n", pFaxPort->jobParam.RecipientNumber);
        DbgPrint("  Recipient Name: %ws\n", pFaxPort->jobParam.RecipientName);
        DbgPrint("  TSID: %ws\n", pFaxPort->jobParam.Tsid);
        DbgPrint("  Sender Name: %ws\n", pFaxPort->jobParam.SenderName);
        DbgPrint("  Sender Company: %ws\n", pFaxPort->jobParam.SenderCompany);
        DbgPrint("  Sender Dept: %ws\n", pFaxPort->jobParam.SenderDept);
        DbgPrint("  Billing Code: %ws\n", pFaxPort->jobParam.BillingCode);
    }

    #endif

    //
    // fixup the fax address
    //

    if (pAtSign = _tcschr(pFaxPort->jobParam.RecipientNumber, TEXT('@'))) {

        *pAtSign++ = NUL;

        if (pFaxPort->jobParam.RecipientName == NULL)
            pNewRecipName = (LPTSTR) pFaxPort->jobParam.RecipientName = (LPTSTR)DuplicateString(pFaxPort->jobParam.RecipientNumber);

        _tcscpy((LPTSTR)pFaxPort->jobParam.RecipientNumber, pAtSign);
    }

    //
    // send the fax
    //

    pJobInfo2 = MyGetJob( pFaxPort->hPrinter, 2, pFaxPort->jobId );
    if (pJobInfo2) {
        pFaxPort->jobParam.DocumentName = pJobInfo2->pDocument;
    } else {
        pFaxPort->jobParam.DocumentName = NULL;
    }

/*    if (hToken && !ImpersonatePrinterClient(hToken)) {
        Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));
    } */

    pFaxPort->jobParam.Reserved[0] = 0xffffffff;
    pFaxPort->jobParam.Reserved[1] = pFaxPort->jobId;

    Rslt = pFaxPort->pFaxSendDocumentW( pFaxPort->hFaxSvc, pFaxPort->pFilename, &pFaxPort->jobParam, NULL, &FaxJobId );

/*    if (! (hToken = RevertToPrinterSelf())) {
        Error(("RevertToPrinterSelf failed: %d\n", GetLastError()));
    } */

    if (pJobInfo2) {
        MemFree( pJobInfo2 );
        pFaxPort->jobParam.DocumentName = NULL;
    }

    if (Rslt) {
        status = FAXERR_NONE;
        SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
        DeleteFile( pFaxPort->pFilename );
    } else {
        status = GetLastError();
        Error(("FaxSendDocumentForSpooler failed: %d\n", GetLastError()));
    }

ExitEndDocPort:

    if (status == FAXERR_NONE) {

        //
        // If the job was successfully sent to the fax service, then
        // the service will delete the temporary file when it's done
        // with it. So we don't need to delete it here.
        //

        MemFree(pFaxPort->pFilename);
        pFaxPort->pFilename = NULL;

    } else {

        //
        // If the job wasn't successfully sent to the fax service,
        // inform the spooler that there is an error on the job.
        //
        // Or if the print job has no data, simply ignore it.
        //

        switch (status) {

        case FAXERR_RESTART:

            Warning(("Job restarted or deleted: id = %d\n", pFaxPort->jobId));

        case FAXERR_IGNORE:

            SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER);
            break;

        default:

            Error(("Error sending fax job: id = %d\n", pFaxPort->jobId));

            SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_PAUSE);
            SetJobStatus(pFaxPort->hPrinter, pFaxPort->jobId, status);
            break;
        }
    }

    if (pNewRecipName) {

        MemFree(pNewRecipName);
        pFaxPort->jobParam.RecipientName = NULL;
    }

    FreeFaxJobInfo(pFaxPort);

    //
    // Switch back to the original context if necessary
    //

/*    if (hToken && !ImpersonatePrinterClient(hToken))
        Error(("ImpersonatePrinterClient failed: %d\n", GetLastError()));*/

    return (status < FAXERR_SPECIAL);
}



BOOL
FaxMonWritePort(
    HANDLE  hPort,
    LPBYTE  pBuffer,
    DWORD   cbBuf,
    LPDWORD pcbWritten
    )

/*++

Routine Description:

    Writes data to a port

Arguments:

    hPort - Identifies the port
    pBuffer - Points to a buffer that contains data to be written to the port
    cbBuf - Specifies the size in bytes of the buffer
    pcbWritten - Returns the count of bytes successfully written to the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    PFAXPORT    pFaxPort = (PFAXPORT) hPort;

    //
    // Make sure we have a valid handle
    //

    if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) {

        Error(("WritePort is given an invalid fax port handle\n"));
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    Assert(pFaxPort->hFile != INVALID_HANDLE_VALUE);
    return WriteFile(pFaxPort->hFile, pBuffer, cbBuf, pcbWritten, NULL);
}



BOOL
FaxMonReadPort(
    HANDLE  hPort,
    LPBYTE  pBuffer,
    DWORD   cbBuf,
    LPDWORD pcbRead
    )

/*++

Routine Description:

    Reads data from the port

Arguments:

    hPort - Identifies the port
    pBuffer - Points to a buffer where data read from the printer can be written
    cbBuf - Specifies the size in bytes of the buffer pointed to by pBuffer
    pcbRead - Returns the number of bytes successfully read from the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    Trace("ReadPort");
    SetLastError(ERROR_NOT_SUPPORTED);
    return FALSE;
}



BOOL
FaxMonEnumPorts(
    LPTSTR  pServerName,
    DWORD   Level,
    LPBYTE  pPorts,
    DWORD   cbBuf,
    LPDWORD pcbNeeded,
    LPDWORD pReturned
    )

/*++

Routine Description:

    Enumerates the ports available on the specified server

Arguments:

    pServerName - Specifies the name of the server whose ports are to be enumerated
    dwLevel - Specifies the version of the structure to which pPorts points
    pPorts - Points to an array of PORT_INFO_1 structures where data describing
        the available ports will be writteno
    cbBuf - Specifies the size in bytes of the buffer to which pPorts points
    pcbNeeded - Returns the required buffer size identified by pPorts
    pReturned -  Returns the number of PORT_INFO_1 structures returned

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

#define MAX_DESC_LEN    64

{
    TCHAR            portDescStr[MAX_DESC_LEN];
    INT              descStrSize, faxmonNameSize;
    DWORD            cbNeeded;
    BOOL             status = TRUE;
    PORT_INFO_1      *pPortInfo1 = (PORT_INFO_1 *) pPorts;
    PORT_INFO_2      *pPortInfo2 = (PORT_INFO_2 *) pPorts;
    INT              strSize;


    Trace("EnumPorts");

    if (pcbNeeded == NULL || pReturned == NULL || (pPorts == NULL && cbBuf != 0)) {

        Error(("Invalid input parameters\n"));
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    //
    // Load the fax port description string
    //

    if (! LoadString(ghInstance, IDS_FAX_PORT_DESC, portDescStr, MAX_DESC_LEN))
        portDescStr[0] = NUL;

    descStrSize = SizeOfString(portDescStr);
    faxmonNameSize = SizeOfString(faxMonitorName);

    switch (Level) {

    case 1:

        cbNeeded = sizeof(PORT_INFO_1) + SizeOfString(FAX_PORT_NAME);
        break;

    case 2:

        cbNeeded = sizeof(PORT_INFO_2) + descStrSize + faxmonNameSize + SizeOfString(FAX_PORT_NAME);
        break;
    }

    *pReturned = 1;
    *pcbNeeded = cbNeeded;

    if (cbNeeded > cbBuf) {

        //
        // Caller didn't provide a big enough buffer
        //

        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        status = FALSE;

    } else {

        //
        // Strings must be packed at the end of the caller provided buffer.
        // Otherwise, spooler will mess up.
        //

        pPorts += cbBuf;

        //
        // Copy the requested port information to the caller provided buffer
        //

        strSize = SizeOfString(FAX_PORT_NAME);
        pPorts -= strSize;
        CopyMemory(pPorts, FAX_PORT_NAME, strSize);

        switch (Level) {

        case 1:

            pPortInfo1->pName = (LPTSTR) pPorts;
            Verbose(("Port info 1: %ws\n", pPortInfo1->pName));

            pPortInfo1++;
            break;

        case 2:

            pPortInfo2->pPortName = (LPTSTR) pPorts;

            //
            // Copy the fax monitor name string
            //

            pPorts -= faxmonNameSize;
            pPortInfo2->pMonitorName = (LPTSTR) pPorts;
            CopyMemory(pPorts, faxMonitorName, faxmonNameSize);

            //
            // Copy the fax port description string
            //

            pPorts -= descStrSize;
            pPortInfo2->pDescription = (LPTSTR) pPorts;
            CopyMemory(pPorts, portDescStr, descStrSize);

            pPortInfo2->fPortType = PORT_TYPE_WRITE;
            pPortInfo2->Reserved = 0;

            Verbose(("Port info 2: %ws, %ws, %ws\n",
                     pPortInfo2->pPortName,
                     pPortInfo2->pMonitorName,
                     pPortInfo2->pDescription));

            pPortInfo2++;
            break;
        }
    }

    return status;
}



BOOL
DisplayErrorNotImplemented(
    HWND    hwnd,
    INT     titleId
    )

/*++

Routine Description:

    Display an error dialog to tell the user that he cannot manage
    fax devices in the Printers folder.

Arguments:

    hwnd - Specifies the parent window for the message box
    titleId - Message box title string resource ID

Return Value:

    FALSE

--*/

{
    TCHAR   title[128];
    TCHAR   message[256];

    LoadString(ghInstance, titleId, title, 128);
    LoadString(ghInstance, IDS_CONFIG_ERROR, message, 256);
    MessageBox(hwnd, message, title, MB_OK|MB_ICONERROR);

    SetLastError(ERROR_SUCCESS);
    return FALSE;
}



BOOL
FaxMonAddPort(
    LPTSTR  pServerName,
    HWND    hwnd,
    LPTSTR  pMonitorName
    )

/*++

Routine Description:

    Adds the name of a port to the list of supported ports

Arguments:

    pServerName - Specifies the name of the server to which the port is to be added
    hwnd - Identifies the parent window of the AddPort dialog box
    pMonitorName - Specifies the monitor associated with the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    Trace("AddPort");

    return DisplayErrorNotImplemented(hwnd, IDS_ADD_PORT);
}



BOOL
FaxMonAddPortEx(
    LPTSTR  pServerName,
    DWORD   level,
    LPBYTE  pBuffer,
    LPTSTR  pMonitorName
    )

/*++

Routine Description:

    Adds the name of a port to the list of supported ports

Arguments:

    pServerName - Specifies the name of the server to which the port is to be added
    hwnd - Identifies the parent window of the AddPort dialog box
    pMonitorName - Specifies the monitor associated with the port

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    Trace("AddPortEx");
    SetLastError(ERROR_NOT_SUPPORTED);
    return FALSE;
}



BOOL
FaxMonDeletePort(
    LPTSTR  pServerName,
    HWND    hwnd,
    LPTSTR  pPortName
    )

/*++

Routine Description:

    Delete the specified port from the list of supported ports

Arguments:

    pServerName - Specifies the name of the server from which the port is to be removed
    hwnd - Identifies the parent window of the port-deletion dialog box
    pPortName - Specifies the name of the port to be deleted

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    Trace("DeletePort");
    return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
}



BOOL
FaxMonConfigurePort(
    LPWSTR  pServerName,
    HWND    hwnd,
    LPWSTR  pPortName
    )

/*++

Routine Description:

    Display a dialog box to allow user to configure the specified port

Arguments:

    pServerName - Specifies the name of the server on which the given port exists
    hwnd - Identifies the parent window of the port-configuration dialog
    pPortName - Specifies the name of the port to be configured

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    Trace("ConfigurePort");

    return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT);
}



LPTSTR
DuplicateString(
    LPCTSTR pSrcStr
    )

/*++

Routine Description:

    Make a duplicate of the given character string

Arguments:

    pSrcStr - Specifies the string to be duplicated

Return Value:

    Pointer to the duplicated string, NULL if there is an error

--*/

{
    LPTSTR  pDestStr;
    INT     strSize;

    if (pSrcStr != NULL) {

        strSize = SizeOfString(pSrcStr);

        if (pDestStr = MemAlloc(strSize))
            CopyMemory(pDestStr, pSrcStr, strSize);
        else
            Error(("Memory allocation failed\n"));

    } else
        pDestStr = NULL;

    return pDestStr;
}



PVOID
MyGetJob(
    HANDLE  hPrinter,
    DWORD   level,
    DWORD   jobId
    )

/*++

Routine Description:

    Wrapper function for spooler API GetJob

Arguments:

    hPrinter - Handle to the printer object
    level - Level of JOB_INFO structure interested
    jobId - Specifies the job ID

Return Value:

    Pointer to a JOB_INFO structure, NULL if there is an error

--*/

{
    PBYTE   pJobInfo = NULL;
    DWORD   cbNeeded;

    if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) &&
        GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
        (pJobInfo = MemAlloc(cbNeeded)) &&
        GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded))
    {
        return pJobInfo;
    }

    Error(("GetJob failed: %d\n", GetLastError()));
    MemFree(pJobInfo);
    return NULL;
}



BOOL
SetJobStatus(
    HANDLE  hPrinter,
    DWORD   jobId,
    INT     statusStrId
    )

/*++

Routine Description:

    Update the status information of a print job

Arguments:

    hPrinter - Specifies the printer on which the job is printed
    jobId - Specifies the job identifier
    statusStrID - Specifies the status string resource ID

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

#define MAX_MESSAGE_LEN 256

{
    JOB_INFO_1 *pJobInfo1;
    BOOL        result = FALSE;
    TCHAR       message[MAX_MESSAGE_LEN];

    //
    // Get the current job information
    //

    if (pJobInfo1 = MyGetJob(hPrinter, 1, jobId)) {

        //
        // Update the status field
        //

        if (LoadString(ghInstance, statusStrId, message, MAX_MESSAGE_LEN))
            pJobInfo1->pStatus = message;
        else {

            pJobInfo1->pStatus = NULL;
            pJobInfo1->Status = JOB_STATUS_ERROR;
        }

        pJobInfo1->Position = JOB_POSITION_UNSPECIFIED;

        if (! (result = SetJob(hPrinter, jobId, 1, (PBYTE) pJobInfo1, 0)))
            Error(("SetJob failed: %d\n", GetLastError()));

        MemFree(pJobInfo1);
    }

    return result;
}



DWORD
GetRegistryDWord(
    HKEY    hRegKey,
    LPTSTR  pValueName,
    DWORD   defaultValue
    )

/*++

Routine Description:

    Retrieve a DWORD value from the registry

Arguments:

    hRegKey - Handle to the user info registry key
    pValueName - Specifies the name of the string value in registry
    defaultValue - Specifies the default value to be used in case of an error

Return Value:

    Requested DWORD value from the user info registry key

--*/

{
    DWORD   size, type, value;

    //
    // Retrieve the country code value from the registry.
    // Use the default value if none exists.
    //

    size = sizeof(value);

    if (RegQueryValueEx(hRegKey, pValueName, NULL, &type, (PBYTE) &value, &size) != ERROR_SUCCESS ||
        type != REG_DWORD)
    {
        value = defaultValue;
    }

    return value;
}



#if DBG

//
// Variable for controlling the amount of debug messages generated
//

INT _debugLevel = 1;


LPCSTR
StripDirPrefixA(
    LPCSTR  pFilename
    )

/*++

Routine Description:

    Strip the directory prefix off a filename

Arguments:

    pFilename - Pointer to filename string

Return Value:

    Pointer to the last component of a filename (without directory prefix)

--*/

{
    LPCSTR  pstr;

    if (pstr = strrchr(pFilename, PATH_SEPARATOR))
        return pstr + 1;

    return pFilename;
}

#endif