/*++

Copyright (c) 1998 Microsoft Corporation

Module Name:

  util.cpp

Abstract:

  This module implements utility functions for the fax queue viewer

Environment:

  WIN32 User Mode

Author:

  Andrew Ritz (andrewr) 14-jan-1998
  Steven Kehrli (steveke) 30-oct-1998 - major rewrite

--*/

#include "faxqueue.h"

VOID
GetFaxQueueRegistryData(
    PWINPOSINFO  pWinPosInfo
)
/*++

Routine Description:

  Get the persistent data for the fax queue viewer

Arguments:

  pWinPosInfo - pointer to the structure that contains the persistent data

Return Value:

  None

--*/
{
    HKEY             hKey;
    DWORD            dwDisposition;
    DWORD            dwType;

#ifdef DEBUG
    // bDebug indicates if debugging is enabled
    BOOL             bDebug;
    DWORD            dwDebugSize;
#endif // DEBUG

#ifdef TOOLBAR_ENABLED
    // bToolbarVisible is the status of the toolbar
    BOOL             bToolbarVisible;
    DWORD            dwToolbarSize;
#endif

    // bStatusBarVisible is the state of the status bar
    BOOL             bStatusBarVisible;
    DWORD            dwStatusBarSize;

    // nColumnIndex is used to enumerate each column of the list view
    INT              nColumnIndex;
    // szColumnKey is the string representation of a column's registry value
    TCHAR            szColumnKey[RESOURCE_STRING_LEN];

    // dwColumnWidth is the column width
    DWORD            dwColumnWidth;
    DWORD            dwColumnSize;

    // WindowPlacement is the window placement
    WINDOWPLACEMENT  WindowPlacement;
    DWORD            dwWindowPlacementSize;

#ifdef DEBUG
    if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGKEY_FAXSERVER, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) {
        // Get the state of the job id
        dwDebugSize = sizeof(bDebug);
        if (RegQueryValueEx(hKey, REGVAL_DBGLEVEL, NULL, &dwType, (LPBYTE) &bDebug, &dwDebugSize) == ERROR_SUCCESS) {
            pWinPosInfo->bDebug = bDebug;
        }

        RegCloseKey(hKey);
    }
#endif // DEBUG

    if (RegCreateKeyEx(HKEY_CURRENT_USER, REGKEY_FAXQUEUE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) {
#ifdef TOOLBAR_ENABLED
        // Get the state of the toolbar
        dwToolbarSize = sizeof(bToolbarVisible);
        if (RegQueryValueEx(hKey, REGVAL_TOOLBARS, NULL, &dwType, (LPBYTE) &bToolbarVisible, &dwToolbarSize) == ERROR_SUCCESS) {
            pWinPosInfo->bToolbarVisible = bToolbarVisible;
        }
#endif // TOOLBAR_ENABLED

        // Get the state of the status bar
        dwStatusBarSize = sizeof(bStatusBarVisible);
        if (RegQueryValueEx(hKey, REGVAL_STATUSBAR, NULL, &dwType, (LPBYTE) &bStatusBarVisible, &dwStatusBarSize) == ERROR_SUCCESS) {
            pWinPosInfo->bStatusBarVisible = bStatusBarVisible;
        }

        // Get the column widths
        for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
            // Set the column's registry value
            wsprintf(szColumnKey, REGVAL_COLUMNWIDTH, nColumnIndex);

            dwColumnSize = sizeof(dwColumnWidth);
            if (RegQueryValueEx(hKey, szColumnKey, NULL, &dwType, (LPBYTE) &dwColumnWidth, &dwColumnSize) == ERROR_SUCCESS) {
                pWinPosInfo->ColumnWidth[nColumnIndex] = dwColumnWidth;
            }
        }

        // Get the window placement
        dwWindowPlacementSize = sizeof(WindowPlacement);
        if (RegQueryValueEx(hKey, REGVAL_WINDOW_PLACEMENT, NULL, &dwType, (LPBYTE) &WindowPlacement, &dwWindowPlacementSize) == ERROR_SUCCESS) {
            if (dwWindowPlacementSize == sizeof(WindowPlacement)) {
                CopyMemory((LPBYTE) &pWinPosInfo->WindowPlacement, (LPBYTE) &WindowPlacement, sizeof(WindowPlacement));
            }
        }

        RegCloseKey(hKey);
    }
}

VOID
SetFaxQueueRegistryData(
#ifdef TOOLBAR_ENABLED
    BOOL  bToolbarVisible,
#endif // TOOLBAR_ENABLED
    BOOL  bStatusBarVisible,
    HWND  hWndList,
    HWND  hWnd
)
/*++

Routine Description:

  Set the persistent data for the fax queue viewer

Arguments:

  bToolbarVisible - status of the toolbar
  bStatusBarVisible - status of the status bar
  hWndList - handle to the list view
  hWnd - handle to the fax queue viewer window

Return Value:

  None

--*/
{
    HKEY             hKey;
    DWORD            dwDisposition;

    // nColumnIndex is used to enumerate each column of the list view
    INT              nColumnIndex;
    // szColumnKey is the string representation of a column's registry value
    TCHAR            szColumnKey[RESOURCE_STRING_LEN];

    // dwColumnWidth is the column width
    DWORD            dwColumnWidth;

    // WindowPlacement is the window placement
    WINDOWPLACEMENT  WindowPlacement;

    if (RegCreateKeyEx(HKEY_CURRENT_USER, REGKEY_FAXQUEUE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) {
#ifdef TOOLBAR_ENABLED
        // Set the state of the toolbar
        RegSetValueEx(hKey, REGVAL_TOOLBARS, 0, REG_DWORD, (LPBYTE) &bToolbarVisible, sizeof(bToolbarVisible));
#endif // TOOLBAR_ENABLED

        // Set the state of the status bar
        RegSetValueEx(hKey, REGVAL_STATUSBAR, 0, REG_DWORD, (LPBYTE) &bStatusBarVisible, sizeof(bStatusBarVisible));

        // Set the column widths
        for (nColumnIndex = 0; nColumnIndex < (INT) eIllegalColumnIndex; nColumnIndex++) {
            // Set the column's registry value
            wsprintf(szColumnKey, REGVAL_COLUMNWIDTH, nColumnIndex);

            dwColumnWidth = ListView_GetColumnWidth(hWndList, nColumnIndex);
            RegSetValueEx(hKey, szColumnKey, 0, REG_DWORD, (LPBYTE) &dwColumnWidth, sizeof(dwColumnWidth));
        }

        // Set the window placement
        GetWindowPlacement(hWnd, &WindowPlacement);
        WindowPlacement.showCmd = SW_SHOWNORMAL;
        RegSetValueEx(hKey, REGVAL_WINDOW_PLACEMENT, 0, REG_BINARY, (LPBYTE) &WindowPlacement, sizeof(WindowPlacement));

        RegCloseKey(hKey);
    }
}

VOID
GetColumnHeaderText(
    eListViewColumnIndex  eColumnIndex,
    LPTSTR                szColumnHeader
)
/*++

Routine Description:

  Builds a string containing the text of a column header to be added to the list view

Arguments:

  eColumnIndex - indicates the column number
  szColumnHeader - column header text

Return Value:

  None

--*/
{
    UINT   uResource = 0;
    TCHAR  szString[RESOURCE_STRING_LEN];

    switch (eColumnIndex) {
        case eDocumentName:
            uResource = IDS_DOCUMENT_NAME_COLUMN;
            break;

        case eJobType:
            uResource = IDS_JOB_TYPE_COLUMN;
            break;

        case eStatus:
            uResource = IDS_STATUS_COLUMN;
            break;

        case eOwner:
            uResource = IDS_OWNER_COLUMN;
            break;

        case ePages:
            uResource = IDS_PAGES_COLUMN;
            break;

        case eSize:
            uResource = IDS_SIZE_COLUMN;
            break;
         
        case eScheduledTime:
            uResource = IDS_SCHEDULED_TIME_COLUMN;
            break;

        case ePort:
            uResource = IDS_PORT_COLUMN;
            break;
    }

    if (uResource) {
        LoadString(g_hInstance, uResource, szString, RESOURCE_STRING_LEN);
        lstrcpy(szColumnHeader, szString);
    }
    else {
        lstrcpy(szColumnHeader, TEXT(""));
    }
}

LPVOID
LocalEnumPrinters(
    DWORD    dwFlags,
    DWORD    dwLevel,
    LPDWORD  pdwNumPrinters
)
/*++

Routine Description:

  Enumerate all the printers

Arguments:

  dwFlags - type of print objects to enumerate
  dwLevel - type of printer info structure
  pdwNumPrinters - pointer to the number of printers

Return Value:

  Pointer to the printers configuration

--*/
{
    // pPrintersConfig is a pointer to the printers configuration
    LPVOID  pPrintersConfig;
    DWORD   cb;

    *pdwNumPrinters = 0;

    // Enumerate all the printers
    if ((!EnumPrinters(dwFlags, NULL, dwLevel, NULL, 0, &cb, pdwNumPrinters)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
        // EnumPrinters failed because the buffer is too small
        // cb is the size of the buffer needed, so allocate a buffer of that size
        pPrintersConfig = MemAlloc(cb);

        // Call EnumPrinters again with the correct size buffer
        if (!EnumPrinters(dwFlags, NULL, dwLevel, (LPBYTE) pPrintersConfig, cb, &cb, pdwNumPrinters)) {
            // EnumPrinters failed
            MemFree(pPrintersConfig);

            // Return NULL pointer
            return NULL;
        }

        // Return pointer to the buffer
        return pPrintersConfig;
    }

    // Return NULL pointer
    return NULL;
}

int __cdecl
ComparePrinterNames(
    const void  *arg1,
    const void  *arg2
)
{
    return (CompareString(LOCALE_USER_DEFAULT, 0, ((LPPRINTER_INFO_2) arg1)->pPrinterName, -1, ((LPPRINTER_INFO_2) arg2)->pPrinterName, -1) - 2);
}

LPVOID
GetFaxPrinters(
    LPDWORD  pdwNumFaxPrinters
)
/*++

Routine Description:

  Get the fax printers

Arguments:

  pdwNumFaxPrinters - pointer to the number of fax printers

Return Value:

  Pointer to the fax printers configuration

--*/
{
    // pFaxPrintersConfig is a pointer to the printers configuration
    LPPRINTER_INFO_2  pFaxPrintersConfig;
    // dwNumPrinters is the number of printers
    DWORD             dwNumPrinters;
    // dwNumFaxPrinters is the number of fax printers
    DWORD             dwNumFaxPrinters;
    // dwIndex is a counter to enumerate each printer
    DWORD             dwIndex;
    // dwFlags is the type of print objects to enumerate
    DWORD             dwFlags;

#ifdef WIN95
    dwFlags = PRINTER_ENUM_LOCAL;
#else
    dwFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS;
#endif // WIN95

    // Enumerate all the printers
    pFaxPrintersConfig = (LPPRINTER_INFO_2) LocalEnumPrinters(dwFlags, 2, &dwNumPrinters);
    if (!pFaxPrintersConfig) {
        *pdwNumFaxPrinters = 0;
        return NULL;
    }

    // Determine the number of fax printers
    for (dwIndex = 0, dwNumFaxPrinters = 0; dwIndex < dwNumPrinters; dwIndex++) {
        // A fax printer is determined by comparing the name of the current printer driver against the name of the fax printer driver
        if (!lstrcmpi((pFaxPrintersConfig)[dwIndex].pDriverName, FAX_DRIVER_NAME)) {
            // Name of the current printer driver and the name of the fax printer driver match
            // Increment the number of fax printers
            dwNumFaxPrinters += 1;
        }
    }

    if (dwNumFaxPrinters > 0) {
        for (dwIndex = 0, dwNumFaxPrinters = 0; dwIndex < dwNumPrinters; dwIndex++) {
            // A fax printer is determined by comparing the name of the current printer driver against the name of the fax printer driver
            if (!lstrcmpi((pFaxPrintersConfig)[dwIndex].pDriverName, FAX_DRIVER_NAME)) {
                // Name of the current printer driver and the name of the fax printer driver match
                // Move fax printer up to the next available slot
                pFaxPrintersConfig[dwNumFaxPrinters] = pFaxPrintersConfig[dwIndex];
                // Increment the number of fax printers
                dwNumFaxPrinters++;
            }
        }

        // Quick sort the fax printers
        qsort(pFaxPrintersConfig, dwNumFaxPrinters, sizeof(PRINTER_INFO_2), ComparePrinterNames);
    }
    else {
        MemFree(pFaxPrintersConfig);
        pFaxPrintersConfig = NULL;
    }

    *pdwNumFaxPrinters = dwNumFaxPrinters;
    return pFaxPrintersConfig;
}

LPTSTR
GetDefaultPrinterName(
)
/*++

Routine Description:

  Get the default printer

Return Value:

  Name of the default printer

--*/
{
    // szPrinterName is the printer name
    LPTSTR            szPrinterName;

#ifdef WIN95
    // pPrintersConfig is a pointer to the printers configuration
    LPPRINTER_INFO_5  pPrintersConfig;
    // dwNumPrinters is the number of printers
    DWORD             dwNumPrinters;

    // Enumerate all the printers
    pPrintersConfig = (LPPRINTER_INFO_5) LocalEnumPrinters(PRINTER_ENUM_DEFAULT, 5, &dwNumPrinters);
    if (!pPrintersConfig) {
        return NULL;
    }

    // Allocate the memory for the printer name
    szPrinterName = (LPTSTR) MemAlloc((lstrlen(pPrintersConfig->pPrinterName) + 1) * sizeof(TCHAR));
    // Copy the printer name
    lstrcpy(szPrinterName, pPrintersConfig->pPrinterName);
    MemFree(pPrintersConfig);

    return szPrinterName;
#else
    DWORD             cb;

    // Get the default printer
    cb = 0;
    if ((!GetDefaultPrinter(NULL, &cb)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
        // GetDefaultPrinter failed because the buffer is too small
        // cb is the size of the buffer needed, so allocate a buffer of that size
        szPrinterName = (LPTSTR) MemAlloc(cb * sizeof(TCHAR));

        // Call GetDefaultPrinter again with the correct size buffer
        if (!GetDefaultPrinter(szPrinterName, &cb)) {
            // GetDefaultPrinter failed
            MemFree(szPrinterName);

            // Return NULL pointer
            return NULL;
        }

        // Return pointer to the buffer
        return szPrinterName;
    }

    // Return NULL pointer
    return NULL;
#endif // WIN95
}

VOID
SetDefaultPrinterName(
    LPTSTR  szPrinterName
)
/*++

Routine Description:

  Set the default printer

Arguments:

  szPrinterName - name of the printer to set as the default

Return Value:

  None

--*/
{
#ifdef WIN95
    // hPrinter is the handle to the printer
    HANDLE            hPrinter;
    // pPrintersConfig is a pointer to the printers configuration
    LPPRINTER_INFO_2  pPrintersConfig;
    DWORD             cb;

    // Open the printer
    if (OpenPrinter(szPrinterName, &hPrinter, NULL)) {
        // Get the printer
        cb = 0;
        if ((!GetPrinter(hPrinter, 2, NULL, 0, &cb)) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
            // GetPrinter failed because the buffer is too small
            // cb is the size of the buffer needed, so allocate a buffer of that size
            pPrintersConfig = (LPPRINTER_INFO_2) MemAlloc(cb);

            // Call GetPrinter again with the correct size buffer
            if (!GetPrinter(hPrinter, 2, (LPBYTE) pPrintersConfig, cb, &cb)) {
                // GetPrinter failed
                MemFree(pPrintersConfig);

                // Close the printer
                ClosePrinter(hPrinter);

                // Return
                return;
            }

            // Set the default attribute
            pPrintersConfig->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;

            // Set the printer
            SetPrinter(hPrinter, 2, (LPBYTE) pPrintersConfig, 0);

            MemFree(pPrintersConfig);
        }

        // Close the printer
        ClosePrinter(hPrinter);
    }
#else
    // Set the default printer
    SetDefaultPrinter(szPrinterName);
#endif // WIN95

}

VOID
Disconnect(
)
{
    // Wait for access to these fax service routines
    WaitForSingleObject(g_hFaxSvcMutex, INFINITE);

    // Decrement the number of connections to the fax service
    g_nNumConnections--;

    // Disconnect from the fax service if no outstanding connections
    if (!g_nNumConnections) {
        FaxClose(g_hFaxSvcHandle);
        g_hFaxSvcHandle = NULL;
    }

    ReleaseMutex(g_hFaxSvcMutex);
}

BOOL
Connect(
)
{
    BOOL  bVal = FALSE;

    // Wait for access to these fax service routines
    WaitForSingleObject(g_hFaxSvcMutex, INFINITE);

    // Connect to the fax service if not already connected
    if ((g_nNumConnections) || (FaxConnectFaxServer(g_szMachineName, &g_hFaxSvcHandle))) {
        // Increment the number of connections to the fax service
        g_nNumConnections++;
        bVal = TRUE;
    }

    ReleaseMutex(g_hFaxSvcMutex);

    return bVal;
}

LPTSTR
GetColumnItemText(
    eListViewColumnIndex  eColumnIndex,
    PFAX_JOB_ENTRY        pFaxJobEntry,
    LPTSTR                szDeviceName
)
/*++

Routine Description:

  Build a string containing the text of a column item to be added to the list view

Arguments:

  eColumnIndex - indicates the column number
  pFaxJobEntry - pointer to the fax job
  szDeviceName - device name the fax job is active on

Return Value:

  LPTSTR - text of the column item

--*/
{
    // szColumnItem is the text of the column item
    LPTSTR  szColumnItem;
    // szResourceString is a resource string
    TCHAR   szResourceString[RESOURCE_STRING_LEN];
    // uResource is the id of the resource string
    UINT    uResource;
    DWORD   cb;

    switch (eColumnIndex) {
        case eDocumentName:
            // szNullString is the null resource string
            TCHAR  szNullString[RESOURCE_STRING_LEN];

            // Determine the queue status resource string
            if (pFaxJobEntry->QueueStatus & JS_DELETING) {
                uResource = IDS_QUEUE_STATUS_DELETING;
            }
            else if (pFaxJobEntry->QueueStatus & JS_PAUSED) {
                uResource = IDS_QUEUE_STATUS_PAUSED;
            }
            else if (pFaxJobEntry->QueueStatus & JS_RETRYING) {
                uResource = IDS_QUEUE_STATUS_RETRYING;
            }
            else {
                uResource = 0;
            }

            // Load the queue status resource string, if necessary, and determine its memory requirement
            cb = 0;
            if (uResource) {
                LoadString(g_hInstance, uResource, szResourceString, RESOURCE_STRING_LEN);
                cb = lstrlen(szResourceString) * sizeof(TCHAR);
            }

            if (pFaxJobEntry->DocumentName) {
                cb += (lstrlen(pFaxJobEntry->DocumentName) + 1) * sizeof(TCHAR);
            }
            else {
                // Load the null resource string
                LoadString(g_hInstance, IDS_NO_DOCUMENT_NAME, szNullString, RESOURCE_STRING_LEN);
                cb += (lstrlen(szNullString) + 1) * sizeof(TCHAR);
            }

#ifdef DEBUG
            if (WinPosInfo.bDebug) {
                cb += lstrlen(TEXT("0x00000000 ")) * sizeof(TCHAR);
            }
#endif // DEBUG

            // Allocate the memory for the document name and set the document name
            szColumnItem = (LPTSTR) MemAlloc(cb);
            if (szColumnItem) {
#ifdef DEBUG
                if (WinPosInfo.bDebug) {
                    wsprintf(szColumnItem, TEXT("0x%08x "), pFaxJobEntry->JobId);
                }

                if (pFaxJobEntry->DocumentName) {
                    lstrcat(szColumnItem, pFaxJobEntry->DocumentName);
                }
                else {
                    lstrcat(szColumnItem, szNullString);
                }
#else
                if (pFaxJobEntry->DocumentName) {
                    lstrcpy(szColumnItem, pFaxJobEntry->DocumentName);
                }
                else {
                    lstrcpy(szColumnItem, szNullString);
                }
#endif // DEBUG

                if (uResource) {
                    lstrcat(szColumnItem, szResourceString);
                }

                return szColumnItem;
            }

            return NULL;

        case eJobType:
            // Determine the job type resource string
            switch (pFaxJobEntry->JobType) {
                case JT_SEND:
                    uResource = IDS_JOB_TYPE_SEND;
                    break;

                case JT_RECEIVE:
                    uResource = IDS_JOB_TYPE_RECEIVE;
                    break;

                case JT_ROUTING:
                    uResource = IDS_JOB_TYPE_ROUTING;
                    break;

                case JT_FAIL_RECEIVE:
                    uResource = IDS_JOB_TYPE_FAIL_RECEIVE;
                    break;

                default:
                    uResource = 0;
                    break;
            }

            // Load the job type resource string, if necessary
            if (uResource) {
                LoadString(g_hInstance, uResource, szResourceString, RESOURCE_STRING_LEN);
                // Allocate the memory for the job type and set the job type
                szColumnItem = (LPTSTR) MemAlloc((lstrlen(szResourceString) + 1) * sizeof(TCHAR));
                if (szColumnItem) {
                    lstrcpy(szColumnItem, szResourceString);

                    return szColumnItem;
                }
            }

            return NULL;

        case eStatus: 
            // Determine the job status resource string
            switch (pFaxJobEntry->Status) {
                case FPS_DIALING:
                    uResource = IDS_JOB_STATUS_DIALING;
                    break;

                case FPS_SENDING:
                    uResource = IDS_JOB_STATUS_SENDING;
                    break;

                case FPS_RECEIVING:
                    uResource = IDS_JOB_STATUS_RECEIVING;
                    break;

                case FPS_COMPLETED:
                    uResource = IDS_JOB_STATUS_COMPLETED;
                    break;

                case FPS_HANDLED:
                    uResource = IDS_JOB_STATUS_HANDLED;
                    break;

                case FPS_UNAVAILABLE:
                    uResource = IDS_JOB_STATUS_UNAVAILABLE;
                    break;

                case FPS_BUSY:
                    uResource = IDS_JOB_STATUS_BUSY;
                    break;

                case FPS_NO_ANSWER:
                    uResource = IDS_JOB_STATUS_NO_ANSWER;
                    break;

                case FPS_BAD_ADDRESS:
                    uResource = IDS_JOB_STATUS_BAD_ADDRESS;
                    break;

                case FPS_NO_DIAL_TONE:
                    uResource = IDS_JOB_STATUS_NO_DIAL_TONE;
                    break;

                case FPS_DISCONNECTED:
                    uResource = IDS_JOB_STATUS_DISCONNECTED;
                    break;

                case FPS_FATAL_ERROR:
                    if (pFaxJobEntry->JobType == JT_RECEIVE) {
                        uResource = IDS_JOB_STATUS_FATAL_ERROR_RCV;
                    }
                    else {
                        uResource = IDS_JOB_STATUS_FATAL_ERROR_SND;
                    }
                    break;

                case FPS_NOT_FAX_CALL:
                    uResource = IDS_JOB_STATUS_NOT_FAX_CALL;
                    break;

                case FPS_CALL_DELAYED:
                    uResource = IDS_JOB_STATUS_CALL_DELAYED;
                    break;

                case FPS_CALL_BLACKLISTED:
                    uResource = IDS_JOB_STATUS_CALL_BLACKLISTED;
                    break;

                case FPS_INITIALIZING:
                    uResource = IDS_JOB_STATUS_INITIALIZING;
                    break;

                case FPS_OFFLINE:
                    uResource = IDS_JOB_STATUS_OFFLINE;
                    break;

                case FPS_RINGING:
                    uResource = IDS_JOB_STATUS_RINGING;
                    break;

                case FPS_AVAILABLE:
                    uResource = IDS_JOB_STATUS_AVAILABLE;
                    break;

                case FPS_ABORTING:
                    uResource = IDS_JOB_STATUS_ABORTING;
                    break;

                case FPS_ROUTING:
                    uResource = IDS_JOB_STATUS_ROUTING;
                    break;

                case FPS_ANSWERED:
                    uResource = IDS_JOB_STATUS_ANSWERED;
                    break;

                default:
                    uResource = 0;
                    break;
            }

            // Determine if retries have been exceeded
            if (((pFaxJobEntry->JobType == JT_SEND) || (pFaxJobEntry->JobType == JT_ROUTING)) && (pFaxJobEntry->QueueStatus & JS_RETRIES_EXCEEDED)) {
                uResource = IDS_QUEUE_STATUS_RETRIES_EXCEEDED;
            }

            // Load the job status resource string, if necessary
            if (uResource) {
                LoadString(g_hInstance, uResource, szResourceString, RESOURCE_STRING_LEN);
                // Allocate the memory for the job status and set the job type
                if (uResource == IDS_JOB_STATUS_DIALING) {
                    szColumnItem = (LPTSTR) MemAlloc((lstrlen(szResourceString) + lstrlen(pFaxJobEntry->RecipientNumber) + 1) * sizeof(TCHAR));
                }
                else {
                    szColumnItem = (LPTSTR) MemAlloc((lstrlen(szResourceString) + 1) * sizeof(TCHAR));
                }
                if (szColumnItem) {
                    if (uResource == IDS_JOB_STATUS_DIALING) {
                        wsprintf(szColumnItem, szResourceString, pFaxJobEntry->RecipientNumber);
                    }
                    else {
                        lstrcpy(szColumnItem, szResourceString);
                    }

                    return szColumnItem;
                }
            }

            return NULL;

        case eOwner:
            // Allocate the memory for the job owner and set the job owner, if necessary
            if (pFaxJobEntry->UserName) {
                szColumnItem = (LPTSTR) MemAlloc((lstrlen(pFaxJobEntry->UserName) + 1) * sizeof(TCHAR));
                if (szColumnItem) {
                    lstrcpy(szColumnItem, pFaxJobEntry->UserName);

                    return szColumnItem;
                }
            }

            return NULL;

        case ePages:
            // Set the job pages resource string, if necessary
            if (pFaxJobEntry->PageCount) {
                wsprintf(szResourceString, TEXT("%d"), pFaxJobEntry->PageCount);
                // Allocate the memory for the job pages and set the job pages
                szColumnItem = (LPTSTR) MemAlloc((lstrlen(szResourceString) + 1) * sizeof(TCHAR));
                if (szColumnItem) {
                    lstrcpy(szColumnItem, szResourceString);

                    return szColumnItem;
                }
            }

            return NULL;

        case eSize:
            // Determine the job size resource string
            if (pFaxJobEntry->Size) {
                // szNumberString is the number string
                LPTSTR  szNumberString;

                if (pFaxJobEntry->Size < 1024) {
                    uResource = IDS_JOB_SIZE_BYTES;
                    // Set the job size number string
                    wsprintf(szResourceString, TEXT("%u"), pFaxJobEntry->Size);
                }
                else if (pFaxJobEntry->Size < 1024 * 1024) {
                    uResource = IDS_JOB_SIZE_KBYTES;
                    // Set the job size number string
                    wsprintf(szResourceString, TEXT("%u.%2u"), pFaxJobEntry->Size / 1024, pFaxJobEntry->Size % 1024);
                }
                else if (pFaxJobEntry->Size < 1024 * 1024 * 1024) {
                    uResource = IDS_JOB_SIZE_MBYTES;
                    // Set the job size number string
                    wsprintf(szResourceString, TEXT("%u.%2u"), pFaxJobEntry->Size / (1024 * 1024), pFaxJobEntry->Size % (1024 * 1024));
                }
                else {
                    uResource = IDS_JOB_SIZE_GBYTES;
                    // Set the job size number string
                    wsprintf(szResourceString, TEXT("%u.%2u"), pFaxJobEntry->Size / (1024 * 1024 * 1024), pFaxJobEntry->Size % (1024 * 1024 * 1024));
                }

                // Format the number string
                cb = GetNumberFormat(LOCALE_USER_DEFAULT, 0, szResourceString, NULL, NULL, 0);
                szNumberString = (LPTSTR) MemAlloc((cb + 1) * sizeof(TCHAR));
                if (szNumberString) {
                    GetNumberFormat(LOCALE_USER_DEFAULT, 0, szResourceString, NULL, szNumberString, cb);

                    LoadString(g_hInstance, uResource, szResourceString, RESOURCE_STRING_LEN);
                    // Allocate the memory for the job size and set the job size
                    szColumnItem = (LPTSTR) MemAlloc((lstrlen(szNumberString) + lstrlen(szResourceString) + 1) * sizeof(TCHAR));
                    if (szColumnItem) {
                        wsprintf(szColumnItem, szResourceString, szNumberString);
                        MemFree(szNumberString);

                        return szColumnItem;
                    }

                    MemFree(szNumberString);
                }
            }

            return NULL;

        case eScheduledTime:
            // Set the job scheduled time resource string
            if (pFaxJobEntry->ScheduleAction == JSA_NOW) {
                LoadString(g_hInstance, IDS_JOB_SCHEDULED_TIME_NOW, szResourceString, RESOURCE_STRING_LEN);
            }
            else {
                // Convert the schedule time to the local time zone
                SystemTimeToTzSpecificLocalTime(NULL, &pFaxJobEntry->ScheduleTime, &pFaxJobEntry->ScheduleTime);

                GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &pFaxJobEntry->ScheduleTime, NULL, szResourceString, RESOURCE_STRING_LEN);
                lstrcat(szResourceString, TEXT(" "));
                cb = lstrlen(szResourceString);
                GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &pFaxJobEntry->ScheduleTime, NULL, &szResourceString[cb], RESOURCE_STRING_LEN - cb);
            }

            // Allocate the memory for the job schedule time and set the job schedule time
            szColumnItem = (LPTSTR) MemAlloc((lstrlen(szResourceString) + 1) * sizeof(TCHAR));
            if (szColumnItem) {
                lstrcpy(szColumnItem, szResourceString);

                return szColumnItem;
            }

            return NULL;

        case ePort:
            // Allocate the memory for the job port and set the job port, if necessary
            if (szDeviceName) {
                szColumnItem = (LPTSTR) MemAlloc((lstrlen(szDeviceName) + 1) * sizeof(TCHAR));
                if (szColumnItem) {
                    lstrcpy(szColumnItem, szDeviceName);

                    return szColumnItem;
                }
            }

            return NULL;

        case eIllegalColumnIndex:
            break;
    }

    return NULL;
}

VOID
SetColumnItem(
    HWND            hWndListView,
    BOOL            bInsert,
    INT             iItem,
    INT             iSubItem,
    LPTSTR          szColumnItem,
    UINT            uState,
    PFAX_JOB_ENTRY  pFaxJobEntry
)
/*++

Routine Description:

  Set or insert a column item in the list view

Arguments:

  hWndListView - handle to the list view window
  bInsert - indicates item is to be inserted into the list view
  iItem - index of the item
  iSubItem - index of the subitem
  szColumnItem - column item text
  uState - state of the item
  pFaxJobEntry - pointer to the fax job

Return Value:

  None

--*/
{
    // lvi specifies the attributes of a particular item in the list view
    LV_ITEM  lvi;

    // Initialize lvi
    lvi.mask = LVIF_TEXT;
    // Set the item number
    lvi.iItem = iItem;
    // Set the subitem number
    lvi.iSubItem = iSubItem;
    // Set the item text
    lvi.pszText = szColumnItem;

    if (iSubItem == (INT) eDocumentName) {
        // Include the fax job id in the lParam
        lvi.mask = lvi.mask | LVIF_PARAM | LVIF_STATE;
        // Set the lParam
        lvi.lParam = pFaxJobEntry->JobId;
        // Set the item state
        lvi.state = uState;
        if (pFaxJobEntry->JobType == JT_SEND) {
            lvi.state |= ITEM_SEND_MASK;
        }
        if (!(pFaxJobEntry->QueueStatus & JS_INPROGRESS)) {
            lvi.state |= ITEM_IDLE_MASK;
        }
        if (pFaxJobEntry->QueueStatus & JS_PAUSED) {
            lvi.state |= ITEM_PAUSED_MASK;
        }
        if ((g_szCurrentUserName) && (!lstrcmpi(g_szCurrentUserName, pFaxJobEntry->UserName))) {
            lvi.state |= ITEM_USEROWNSJOB_MASK;
        }
        // Set the item state mask
        lvi.stateMask = uState | LVIS_OVERLAYMASK;
    }

    if ((bInsert) && (iSubItem == (INT) eDocumentName)) {
        ListView_InsertItem(hWndListView, &lvi);
    }
    else {
        ListView_SetItem(hWndListView, &lvi);
    }
}