/*++ 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 XP 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 "faxext.h" #include "faxsvcrg.h" #include "faxevent.h" #include "faxevent_messages.h" #include "FaxRpcLimit.h" // // tag mapping structure for getting job parameters out of parameter string. // see GetTagsFromParam(). // typedef struct { LPTSTR lptstrTagName; LPTSTR * lpptstrValue; int nLen; } FAX_TAG_MAP_ENTRY2; // // Fax monitor name string // TCHAR faxMonitorName[CCHDEVICENAME] = FAX_MONITOR_NAME; // Name defined in faxreg.h // // DLL instance handle // HINSTANCE g_hInstance = NULL; HINSTANCE g_hResource = NULL; BOOL WriteToLog( IN DWORD dwMsgId, IN DWORD dwError, IN PFAXPORT pFaxPort, IN JOB_INFO_2 *pJobInfo ); BOOL WINAPI DllMain( HINSTANCE 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: DisableThreadLibraryCalls(hModule); g_hInstance = hModule; g_hResource = GetResInstance(hModule); if(!g_hResource) { return FALSE; } FXSEVENTInitialize(); break; case DLL_PROCESS_DETACH: FXSEVENTFree(); HeapCleanup(); FreeResInstance(); 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, // EnumPorts FaxMonOpenPort, // OpenPort NULL, // OpenPortEx (Language monitors only.) FaxMonStartDocPort, // StartDocPort FaxMonWritePort, // WritePort FaxMonReadPort, // ReadPort (Not in use) FaxMonEndDocPort, // EndDocPort FaxMonClosePort, // ClosePort FaxMonAddPort, // AddPort (Obsolete. Should be NULL.) FaxMonAddPortEx, // AddPortEx (Obsolete. Should be NULL.) FaxMonConfigurePort, // ConfigurePort (Obsolete. Should be NULL.) FaxMonDeletePort, // DeletePort (Obsolete. Should be NULL.) NULL, // GetPrinterDataFromPort NULL, // SetPortTimeOuts NULL, // XcvOpenPort NULL, // XcvDataPort NULL // XcvClosePort } }; BOOL bRes = TRUE; PREG_FAX_SERVICE pFaxReg = NULL; DEBUG_FUNCTION_NAME(TEXT("InitializePrintMonitor")); // // Initialize Fax Event Log // if (!InitializeEventLog(&pFaxReg)) { bRes = FALSE; DebugPrintEx(DEBUG_ERR, _T("InitializeEventLog() failed: %ld"), GetLastError()); } FreeFaxRegistry(pFaxReg); return bRes ? &faxmonFuncs : NULL; } 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; LPTSTR pPortNameDup = NULL; DEBUG_FUNCTION_NAME(TEXT("FaxMonOpenPort")); Assert(pHandle != NULL && pPortName != NULL && !lstrcmp(pPortName, FAX_PORT_NAME)); // // Only support one port - It's name must be FAX_PORT_NAME // if (lstrcmp(pPortName,FAX_PORT_NAME)) { *pHandle = NULL; return FALSE; } // // Get information about the specified port // if ((pFaxPort = (PFAXPORT)MemAllocZ(sizeof(FAXPORT))) && (pPortNameDup = DuplicateString(FAX_PORT_NAME)) ) { pFaxPort->startSig = pFaxPort->endSig = pFaxPort; pFaxPort->pName = pPortNameDup; pFaxPort->hFile = INVALID_HANDLE_VALUE; pFaxPort->hCoverPageFile = INVALID_HANDLE_VALUE; pFaxPort->pCoverPageFileName=NULL; } else { MemFree(pFaxPort); pFaxPort = NULL; } *pHandle = (HANDLE) pFaxPort; return (*pHandle != NULL); } // FaxMonOpenPort 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FreeFaxJobInfo")); // // Close and delete the temporary file if necessary // if (pFaxPort->hCoverPageFile != INVALID_HANDLE_VALUE) { CloseHandle(pFaxPort->hCoverPageFile); pFaxPort->hCoverPageFile = INVALID_HANDLE_VALUE; } if (pFaxPort->pCoverPageFileName) { // // If the cover page is a server based cover page it was not generated by the print monitor. // No need to delete it. // This however is a personal cover page temp file. It was generated by the print monitor and we // need to delete it. // if (!DeleteFile(pFaxPort->pCoverPageFileName)) { DebugPrintEx(DEBUG_WRN, TEXT("Failed to delete cover page file: %s (ec: %ld)"), pFaxPort->pCoverPageFileName, GetLastError()); } MemFree(pFaxPort->pCoverPageFileName); pFaxPort->pCoverPageFileName = NULL; } if (pFaxPort->hFile != INVALID_HANDLE_VALUE) { CloseHandle(pFaxPort->hFile); pFaxPort->hFile = INVALID_HANDLE_VALUE; } if (pFaxPort->pFilename) { if (!DeleteFile(pFaxPort->pFilename)) { DebugPrintEx(DEBUG_WRN, TEXT("Failed to delete body file: %s (ec: %ld)"), pFaxPort->pFilename, GetLastError()); } MemFree(pFaxPort->pFilename); pFaxPort->pFilename = NULL; } if (pFaxPort->hPrinter) { ClosePrinter(pFaxPort->hPrinter); pFaxPort->hPrinter = NULL; } MemFree(pFaxPort->pPrinterName); pFaxPort->pPrinterName = NULL; // // Note: freeing pFaxPort->pParameters effectively frees the memory pointed by the strings in // FAXPORT.JobParamsEx, FAXPORT.CoverPageEx, FAXPORT.SenderProfile and the recipients in // FAXPORT.pRecipients // MemFree(pFaxPort->pParameters); pFaxPort->pParameters = NULL; ZeroMemory(&pFaxPort->JobParamsEx, sizeof(pFaxPort->JobParamsEx)); ZeroMemory(&pFaxPort->CoverPageEx, sizeof(pFaxPort->CoverPageEx)); ZeroMemory(&pFaxPort->SenderProfile, sizeof(pFaxPort->SenderProfile)); // // Free the recipients array // MemFree(pFaxPort->pRecipients); pFaxPort->pRecipients = NULL; // // Disconnect from the fax service if necessary // if (pFaxPort->hFaxSvc) { if (!FaxClose(pFaxPort->hFaxSvc)) { DebugPrintEx(DEBUG_ERR, TEXT("FaxClose failed: %d\n"), GetLastError()); } pFaxPort->hFaxSvc = NULL; } } // FreeFaxJobInfo 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; DEBUG_FUNCTION_NAME(TEXT("FaxMonClosePort")); DEBUG_TRACE_ENTER; // // Make sure we have a valid handle // if (! ValidFaxPort(pFaxPort)) { DebugPrintEx(DEBUG_ERR,TEXT("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; } // FaxMonClosePort LPTSTR CreateTempFaxFile( LPCTSTR lpctstrPrefix ) /*++ Routine Description: Create a temporary file in the system temp directory. The file name is prefixed With the specified prefix. Arguments: lpctstrPrefix - [in] The temporary file prefix (3 characters). Return Value: Pointer to the name of the newly created temporary file NULL if there is an error. Caller should free the return value. --*/ { LPTSTR pFilename; DWORD dwRes; TCHAR TempDir[MAX_PATH]; DEBUG_FUNCTION_NAME(TEXT("CreateTempFaxFile")); // // Allocate a memory buffer for holding the temporary filename // pFilename = (LPTSTR)MemAlloc(sizeof(TCHAR) * MAX_PATH); if (!pFilename) { DebugPrintEx(DEBUG_ERR, TEXT("Failed to allocate %ld bytes"), sizeof(TCHAR) * MAX_PATH); return NULL; } dwRes = GetTempPath(sizeof(TempDir)/sizeof(TCHAR),TempDir); if (!dwRes || dwRes > sizeof(TempDir)/sizeof(TCHAR)) { DebugPrintEx(DEBUG_ERR, TEXT("GetTempPath failed with %ld"), GetLastError ()); MemFree(pFilename); return NULL; } if (!GetTempFileName(TempDir, lpctstrPrefix, 0, pFilename)) { DebugPrintEx(DEBUG_ERR, TEXT("GetTempFileName failed with %ld"), GetLastError ()); MemFree(pFilename); return NULL; } return pFilename; } // CreateTempFaxFile BOOL OpenCoverPageFile(PFAXPORT pFaxPort) { DEBUG_FUNCTION_NAME(TEXT("OpenCoverPageFile")); // //Generate a unique file name for the cover page temp file in the TEMP directory // pFaxPort->pCoverPageFileName = CreateTempFaxFile(FAX_COVER_PAGE_EXT_LETTERS); if (!pFaxPort->pCoverPageFileName) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to generate temporary file for cover page template (ec: %d).\n"),GetLastError()); return FALSE; } DebugPrintEx(DEBUG_MSG,TEXT("Cover page temporary file: %ws\n"), pFaxPort->pCoverPageFileName); // // Open the file for reading and writing // pFaxPort->hCoverPageFile = CreateFile(pFaxPort->pCoverPageFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE == pFaxPort->hCoverPageFile) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to open for WRITE temporary file for cover page template (ec: %d)"),GetLastError()); } return (pFaxPort->hCoverPageFile != INVALID_HANDLE_VALUE); } // OpenCoverPageFile 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; DEBUG_FUNCTION_NAME(TEXT("OpenTempFaxFile")); Assert(pFaxPort->pFilename && pFaxPort->hFile == INVALID_HANDLE_VALUE); DebugPrintEx(DEBUG_MSG,TEXT("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) { DebugPrintEx(DEBUG_ERR,TEXT("SetFilePointer failed: %d\n"), GetLastError()); CloseHandle(pFaxPort->hFile); pFaxPort->hFile = INVALID_HANDLE_VALUE; } return (pFaxPort->hFile != INVALID_HANDLE_VALUE); } // OpenTempFaxFile 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: valuevalue 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; } // ExtractFaxTag //********************************************************************************* //* Name: GetTagsFromParam() //* Author: Ronen Barenboim //* Date: March 23, 1999 //********************************************************************************* //* DESCRIPTION: //* Given a tagged parameter string this function populates a FAX_TAG_MAP_ENTRY2 //* array with pointers to each of the tag values and the length of each tag //* value (for those tages specified in the tag map array). //* PARAMETERS: //* lpctstrParams //* A pointer to the string containin the tagged parameters. //* lpcTags //* A pointer to a FAX_TAG_MAP_ENTRY2 array. For each element in the array //* FAX_TAG_MAP_ENTRY2.lptstrTagName must point to the name of the tag to //* look for. //* FAX_TAG_MAP_ENTRY2.lpptstrValue will be set to a pointer to the first //* char of the value string or NULL if the tag is not found. //* If the tage is found FAX_TAG_MAP_ENTRY2.nLen will be set to the its //* string value length. Otherwise its value is not defined. //* //* int nTagCount //* The number of tages in the tag map array. //* RETURN VALUE: //* NONE //* NOTE: //* The function does not allocate any memory !!! //* It returns pointers to substrings in the provided tagged paramter string. //********************************************************************************* void GetTagsFromParam( LPCTSTR lpctstrParams, FAX_TAG_MAP_ENTRY2 * lpcTags, int nTagCount) { // // Note: GetTagsFromParam DOES NOT ALLOCATE any memory for the returned tag values. // It returns pointers to location within the parameter string. // Thus, freeing the parameter string (deallocated when the port is closed) // is enough. DO NOT attempt to free the memory for each tag. // int nTag; // // Extract individual fields out of the tagged string // for (nTag=0; nTag < nTagCount; nTag++) { *(lpcTags[nTag].lpptstrValue) = (LPTSTR)ExtractFaxTag(lpcTags[nTag].lptstrTagName, lpctstrParams, &(lpcTags[nTag].nLen)); } // // Null-terminate each field // for (nTag=0; nTag < nTagCount; nTag++) { if (*(lpcTags[nTag].lpptstrValue)) { (*(lpcTags[nTag].lpptstrValue))[lpcTags[nTag].nLen] = NUL; } } } // GetTagsFromParam //********************************************************************************* //* Name: SetRecipientFromTaggedParams() //* Author: Ronen Barenboim //* Date: March 23, 1999 //********************************************************************************* //* DESCRIPTION: //* Populates a recipient FAX_PERSONAL_PROFILE with pointers to relevant //* information in in the provided tagged parameter string. //* PARAMETERS: //* pParamStr //* //* PFAX_PERSONAL_PROFILE lpProfile //* //* RETURN VALUE: //* NONE //* NOTE: //* This function does not allocate memory !!! //* The returned pointers are pointed to locations within the provided //* pParamStr string. //********************************************************************************* VOID SetRecipientFromTaggedParams( LPCTSTR pParamStr, PFAX_PERSONAL_PROFILE lpProfile) { FAX_TAG_MAP_ENTRY2 tagMap[] = { { FAXTAG_RECIPIENT_NAME, (LPTSTR *)&lpProfile->lptstrName}, { FAXTAG_RECIPIENT_NUMBER, (LPTSTR *)&lpProfile->lptstrFaxNumber }, { FAXTAG_RECIPIENT_COMPANY, (LPTSTR *)&lpProfile->lptstrCompany }, { FAXTAG_RECIPIENT_STREET, (LPTSTR *)&lpProfile->lptstrStreetAddress }, { FAXTAG_RECIPIENT_CITY, (LPTSTR *)&lpProfile->lptstrCity }, { FAXTAG_RECIPIENT_STATE, (LPTSTR *)&lpProfile->lptstrState }, { FAXTAG_RECIPIENT_ZIP, (LPTSTR *)&lpProfile->lptstrZip }, { FAXTAG_RECIPIENT_COUNTRY, (LPTSTR *)&lpProfile->lptstrCountry }, { FAXTAG_RECIPIENT_TITLE, (LPTSTR *)&lpProfile->lptstrTitle }, { FAXTAG_RECIPIENT_DEPT, (LPTSTR *)&lpProfile->lptstrDepartment }, { FAXTAG_RECIPIENT_OFFICE_LOCATION, (LPTSTR *)&lpProfile->lptstrOfficeLocation }, { FAXTAG_RECIPIENT_HOME_PHONE, (LPTSTR *)&lpProfile->lptstrHomePhone }, { FAXTAG_RECIPIENT_OFFICE_PHONE, (LPTSTR *)&lpProfile->lptstrOfficePhone }, }; ZeroMemory(lpProfile, sizeof(FAX_PERSONAL_PROFILE)); lpProfile->dwSizeOfStruct = sizeof( FAX_PERSONAL_PROFILE); GetTagsFromParam(pParamStr, tagMap, sizeof(tagMap)/sizeof(FAX_TAG_MAP_ENTRY2)); } // SetRecipientFromTaggedParams //********************************************************************************* //* Name: SetJobInfoFromTaggedParams() //* Author: Ronen Barenboim //* Date: March 23, 1999 //********************************************************************************* //* DESCRIPTION: //* Popultaes pFaxPort->JobParamsEx, CoverPageEx , SenderProfile and //* nRecipientCount using the provided tagged parameter string. //* The string must be NULL terminated. //* PARAMETERS: //* LPCTSTR pParamStr [IN\OUT] //* //* PFAXPORT pFaxPort [OUT] //* //* RETURN VALUE: //* NONE //* NOTE: //* The string pointers put into the populated pFaxPort structures are pointers //* into the provided pParamStr string. No memory is allocated by this //* function !!! //********************************************************************************* void SetJobInfoFromTaggedParams( LPCTSTR pParamStr, PFAXPORT pFaxPort) { LPTSTR lptstrServerCoverPage = NULL; LPTSTR WhenToSend = NULL; LPTSTR SendAtTime = NULL; LPTSTR lptstrPageCount=NULL; //temp for holding the page count string; LPTSTR lptstrRecipientCount=NULL; LPTSTR lptstrReceiptFlags = NULL; LPTSTR lptstrPriority = NULL; FAX_TAG_MAP_ENTRY2 tagMap[] = { { FAXTAG_SENDER_NAME, (LPTSTR *)&pFaxPort->SenderProfile.lptstrName}, { FAXTAG_SENDER_NUMBER, (LPTSTR *)&pFaxPort->SenderProfile.lptstrFaxNumber }, { FAXTAG_SENDER_COMPANY, (LPTSTR *)&pFaxPort->SenderProfile.lptstrCompany }, { FAXTAG_SENDER_TITLE, (LPTSTR *)&pFaxPort->SenderProfile.lptstrTitle }, { FAXTAG_SENDER_DEPT, (LPTSTR *)&pFaxPort->SenderProfile.lptstrDepartment }, { FAXTAG_SENDER_OFFICE_LOCATION, (LPTSTR *)&pFaxPort->SenderProfile.lptstrOfficeLocation }, { FAXTAG_SENDER_HOME_PHONE, (LPTSTR *)&pFaxPort->SenderProfile.lptstrHomePhone }, { FAXTAG_SENDER_OFFICE_PHONE, (LPTSTR *)&pFaxPort->SenderProfile.lptstrOfficePhone }, { FAXTAG_SENDER_STREET, (LPTSTR *)&pFaxPort->SenderProfile.lptstrStreetAddress }, { FAXTAG_SENDER_CITY, (LPTSTR *)&pFaxPort->SenderProfile.lptstrCity }, { FAXTAG_SENDER_STATE, (LPTSTR *)&pFaxPort->SenderProfile.lptstrState }, { FAXTAG_SENDER_ZIP, (LPTSTR *)&pFaxPort->SenderProfile.lptstrZip }, { FAXTAG_SENDER_COUNTRY, (LPTSTR *)&pFaxPort->SenderProfile.lptstrCountry }, { FAXTAG_SENDER_EMAIL, (LPTSTR *)&pFaxPort->SenderProfile.lptstrEmail }, { FAXTAG_TSID, (LPTSTR *)&pFaxPort->SenderProfile.lptstrTSID }, { FAXTAG_BILLING_CODE, (LPTSTR *)&pFaxPort->SenderProfile.lptstrBillingCode}, { FAXTAG_COVERPAGE_NAME, (LPTSTR *)&pFaxPort->CoverPageEx.lptstrCoverPageFileName }, { FAXTAG_SERVER_COVERPAGE, (LPTSTR *)&lptstrServerCoverPage }, { FAXTAG_NOTE, (LPTSTR *)&pFaxPort->CoverPageEx.lptstrNote }, { FAXTAG_SUBJECT, (LPTSTR *)&pFaxPort->CoverPageEx.lptstrSubject}, { FAXTAG_WHEN_TO_SEND, (LPTSTR *)&WhenToSend }, { FAXTAG_SEND_AT_TIME, (LPTSTR *)&SendAtTime }, { FAXTAG_PAGE_COUNT, (LPTSTR *)&lptstrPageCount }, { FAXTAG_RECEIPT_TYPE, (LPTSTR *)&lptstrReceiptFlags}, { FAXTAG_RECEIPT_ADDR, (LPTSTR *)&pFaxPort->JobParamsEx.lptstrReceiptDeliveryAddress}, { FAXTAG_PRIORITY, (LPTSTR *)&lptstrPriority}, { FAXTAG_RECIPIENT_COUNT, (LPTSTR *)&lptstrRecipientCount} }; ZeroMemory(&pFaxPort->SenderProfile, sizeof(FAX_PERSONAL_PROFILE)); pFaxPort->SenderProfile.dwSizeOfStruct = sizeof( FAX_PERSONAL_PROFILE); ZeroMemory(&pFaxPort->CoverPageEx, sizeof(FAX_COVERPAGE_INFO_EXW)); pFaxPort->CoverPageEx.dwSizeOfStruct = sizeof( FAX_COVERPAGE_INFO_EXW); ZeroMemory(&pFaxPort->JobParamsEx, sizeof(FAX_JOB_PARAM_EXW)); pFaxPort->JobParamsEx.dwSizeOfStruct = sizeof( FAX_JOB_PARAM_EXW); // // Note: GetTagsFromParam DOES NOT ALLOCATE any memory for the returned tag values. // It returns pointers to location within the parameter string. // Thus, freeing the parameter string (deallocated when the port is closed) // is enough. DO NOT attempt to free the memory for each tag. // GetTagsFromParam(pParamStr,tagMap,sizeof(tagMap)/sizeof(FAX_TAG_MAP_ENTRY2)); if (lptstrServerCoverPage) { pFaxPort->CoverPageEx.bServerBased=TRUE; } else { pFaxPort->CoverPageEx.bServerBased=FALSE; } pFaxPort->CoverPageEx.dwCoverPageFormat=FAX_COVERPAGE_FMT_COV; if (WhenToSend) { if (_tcsicmp( WhenToSend, TEXT("cheap") ) == 0) { pFaxPort->JobParamsEx.dwScheduleAction = JSA_DISCOUNT_PERIOD; } else if (_tcsicmp( WhenToSend, TEXT("at") ) == 0) { pFaxPort->JobParamsEx.dwScheduleAction = 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->JobParamsEx.tmSchedule ); } } // // Setting PageCount=0 means the server will count the number of pages in the job // pFaxPort->JobParamsEx.dwPageCount = 0; pFaxPort->nRecipientCount =_ttoi(lptstrRecipientCount); pFaxPort->JobParamsEx.Priority = (FAX_ENUM_PRIORITY_TYPE)_ttoi(lptstrPriority); pFaxPort->JobParamsEx.dwReceiptDeliveryType = _ttoi(lptstrReceiptFlags); } // SetJobInfoFromTaggedParams //********************************************************************************* //* Name: GetJobInfo() //* Author: Ronen Barenboim //* Date: March 23, 1999 //********************************************************************************* //* DESCRIPTION: //* Popultes the sender information , cover page information ,job parameters //* information and recipients information in the pointed FAXPORT structure. //* The information is retrieved from pFaxPort->pParameters tagged parameter string. //* PARAMETERS: //* pFaxPort [OUT] //* //* jobId [IN] //* //* RETURN VALUE: //* //* Notes: //* the format of the tagged parameter string is: //* The string is partitioned to "records" each record starts with the <$FAXTAG NEWREC> //* tag with a dummy value of "1" (exactly one character). //* The first record contains all the information which is not recipient related //* (cover page, sender info, etc.) and also contains the number of recipients in //* the transmission. //* This record is followed by a number of records which is equal to the number //* of specified recipients. Rach of these records contains recipient information //* which is equivalent to the content of FAX_PERSONAL_PROFILE. //********************************************************************************* BOOL GetJobInfo( PFAXPORT pFaxPort, DWORD jobId ) { JOB_INFO_2 *pJobInfo2 = NULL; LPTSTR pParameters = NULL; LPTSTR lptstrCurRecipient = NULL; LPTSTR lptstrNextRecipient = NULL; UINT nRecp; DEBUG_FUNCTION_NAME(TEXT("GetJobInfo")); pJobInfo2 = (PJOB_INFO_2)MyGetJob(pFaxPort->hPrinter, 2, jobId); if (!pJobInfo2) { // pJobInfo2 is allocated here DebugPrintEx(DEBUG_ERR, TEXT("Failed to get job information for print job: %ld"), jobId); goto Error; } if (!pJobInfo2->pParameters) { DebugPrintEx(DEBUG_ERR, TEXT("Print job %ld has NULL tagged parameter string. No op."), jobId); goto Error; } if ((pFaxPort->pParameters = DuplicateString(pJobInfo2->pParameters)) == NULL) { DebugPrintEx(DEBUG_ERR, _T("DuplicateString(pJobInfo2->pParameters) failed")); SetLastError(ERROR_NOT_ENOUGH_MEMORY); goto Error; } pParameters = pFaxPort->pParameters; // // Find the first recipient new record tag and place a NULL at its start. // This makes the first record into a NULL terminated string and // allows us to use _tcsstr (used by ExtractTag) to locate tags in the first record. // lptstrCurRecipient=_tcsstr(pParameters+1, FAXTAG_NEW_RECORD); if (lptstrCurRecipient) { *lptstrCurRecipient=TEXT('\0'); // // move past the <$FAXTAG NEWREC> dummy value so we point to the start of // the recipient info. // lptstrCurRecipient = lptstrCurRecipient + _tcslen(FAXTAG_NEW_RECORD)+1; } else { // // Bad job info e.g. LPR/LPD job // DebugPrintEx(DEBUG_ERR, TEXT("Bad job info, No recipients - pFaxPort->pParameters is %s"), pFaxPort->pParameters); goto Error; } // // Populate all but the recipient information from the tagged parameter string (1st record) // SetJobInfoFromTaggedParams(pParameters,pFaxPort); // // Allocate the recipient list (Note that only after calling SetJobInfoFromTaggedParams() // we know how many recipients there are). // if (0 == pFaxPort->nRecipientCount || pFaxPort->nRecipientCount > FAX_MAX_RECIPIENTS) { // // Recipients count is greater than the limit. This can be an attack that will cause the spooler to allocate alot of memory. // DebugPrintEx(DEBUG_ERR, TEXT("Recipient limit exceeded, or no recipients. #of recipients: %ld"), pFaxPort->nRecipientCount); goto Error; } pFaxPort->pRecipients = (PFAX_PERSONAL_PROFILE)MemAlloc(sizeof(FAX_PERSONAL_PROFILE)*pFaxPort->nRecipientCount); if (!pFaxPort->pRecipients) { DebugPrintEx( DEBUG_ERR, TEXT("Failed to allocate %ld bytes for recipient array.(ec: 0x%0X)"), sizeof(FAX_PERSONAL_PROFILE)*pFaxPort->nRecipientCount, GetLastError()); goto Error; } // // Go over the recipients array and populate each recipient from the parameter string. // for (nRecp=0; nRecpnRecipientCount; nRecp++) { // // At each stage we must first turn the string into null terminated string // by locating the next new record tag and replacing its first char with NULL. // This allows us to use ExtractTag on the current recipient record alone (without // crossing over into the content of the next recipient record). // lptstrCurRecipient allways points to the first char past the new record tag and // dummy value. // lptstrNextRecipient=_tcsstr(lptstrCurRecipient,FAXTAG_NEW_RECORD); if (lptstrNextRecipient) { *lptstrNextRecipient=TEXT('\0'); // // Before being assigned into lptstrCurRecipient we make sure lptstrNextRecipient // points to the data following the next recipient new record tag and dummy value. // lptstrNextRecipient=lptstrNextRecipient+_tcslen(FAXTAG_NEW_RECORD); } else { if (nRecp != (pFaxPort->nRecipientCount-1)) { // // only the last recipient does not have a following recipient // We have a mismach between the number of recipients in the recipients array // to the number of recipients reported. // DebugPrintEx( DEBUG_ERR, TEXT("Number of recipients mismatch.")); goto Error; } } SetRecipientFromTaggedParams( lptstrCurRecipient,&pFaxPort->pRecipients[nRecp]); // // Move to the next record in the parameter string // lptstrCurRecipient=lptstrNextRecipient; } MemFree(pJobInfo2); return TRUE; Error: MemFree(pJobInfo2); return FALSE; } // GetJobInfo 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 --*/ { DWORD dwErr = ERROR_SUCCESS; JOB_INFO_3 *pJobInfo; PFAXPORT pFaxPort = (PFAXPORT) hPort; DEBUG_FUNCTION_NAME(TEXT("FaxMonStartDocPort")); DebugPrintEx(DEBUG_MSG,TEXT("Entering StartDocPort: %d ...\n"), JobId); // // Make sure we have a valid handle // if (! ValidFaxPort(pFaxPort)) { DebugPrintEx(DEBUG_ERR,TEXT("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 // pFaxPort->bCoverPageJob = FALSE; if (INVALID_HANDLE_VALUE != pFaxPort->hCoverPageFile) { // // Cover page file exists. // We already written to it (since this startdoc event is for the body of the fax) // so lets close the file. // CloseHandle(pFaxPort->hCoverPageFile); pFaxPort->hCoverPageFile = INVALID_HANDLE_VALUE; } if (pFaxPort->hFaxSvc) { // // If pFaxPort->hFaxSvc is not NULL then we are in the job following the cover page print job. // FaxMonEndDocPort() that was called after the cover page print job updated pFaxPort->NextJobId // to the next job id in the chain. Thus, the job id we got as a parameter must be // the same is pFaxPort->JobId. // Assert(pFaxPort->jobId == JobId); return TRUE; } // // If we are not connected to the fax server yet. // This means that this is the FIRST job we handle since the port was last opened. // (This means it is the cover page job). // Assert(pFaxPort->pPrinterName == NULL && pFaxPort->hPrinter == NULL && pFaxPort->pParameters == NULL && pFaxPort->pFilename == NULL && pFaxPort->hFile == INVALID_HANDLE_VALUE); if (!OpenPrinter(pPrinterName, &pFaxPort->hPrinter, NULL)) { pFaxPort->hPrinter = NULL; DebugPrintEx(DEBUG_ERR,TEXT("Failed to open printer %s (ec: %d)"), pPrinterName, GetLastError()); goto Error; } // // Connect to the fax service and obtain a session handle // if (!FaxConnectFaxServer(NULL, &pFaxPort->hFaxSvc)) { dwErr = GetLastError(); DebugPrintEx(DEBUG_ERR, _T("FaxConnectFaxServer failed: %d\n"), dwErr); pFaxPort->hFaxSvc = NULL; goto Error; } // // Remember the printer name because we'll need it at EndDocPort time. // pFaxPort->pPrinterName = DuplicateString(pPrinterName); if (!pFaxPort->pPrinterName) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to duplicate printer name (ec: %d)"),GetLastError()); goto Error; } // // All Jobs are chained, with first job being Cover Page and // second one being the Body. // // The only case where only one Job is arrived, is when the Job // is created by FaxStartPrintJob(). In this case, the Job // contains both the Cover Page Info and the Body together. // // To check whether the Job is chained or not, MyGetJob() is called. // // If NextJob is NOT zero ==> there is chained job ==> // ==> so the current job is the Cover Page job. // if (pJobInfo = (PJOB_INFO_3)MyGetJob(pFaxPort->hPrinter, 3, JobId)) { pFaxPort->bCoverPageJob = (pJobInfo->NextJobId != 0); MemFree(pJobInfo); } else { DebugPrintEx(DEBUG_ERR, _T("MyGetJob() for JobId = %ld failed, ec = %ld"), JobId, GetLastError()); goto Error; } // // Get the job parameters from the string in JOB_INFO_2:pParameters. // if (!GetJobInfo(pFaxPort, JobId)) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to get job info for job id : %d"),JobId); goto Error; } // // CreateTempFaxFile() creates a temporray files into which the fax body // data written by FaxMonWritePort() will be saved. // if (!(pFaxPort->pFilename = CreateTempFaxFile(TEXT("fax")))) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to Create temp file for fax body. (ec: %d)"), GetLastError()); goto Error; } // // Open the temporary file we just created for write operation. // if (!OpenTempFaxFile(pFaxPort, FALSE)) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to Open temp file for fax body. (ec: %d)"), GetLastError()); goto Error; } if (pFaxPort->CoverPageEx.lptstrCoverPageFileName && !pFaxPort->CoverPageEx.bServerBased) { // // A cover page is specified and it is a personal cover page. // The cover page (template) is in the chained print job. // We create a file to which the cover page will be written (by FaxMonWriteDocPort). // DebugPrintEx(DEBUG_MSG,TEXT("Personal cover page detected.")); if (!OpenCoverPageFile(pFaxPort)) { DebugPrintEx(DEBUG_ERR,TEXT("Failed to open temp file for fax cover page. (ec: %d)"), GetLastError()); goto Error; } } else { // // The specified cover page is a server based cover page or no cover page is specified. // In both cases there is no cover page data in the print job body so we do not create // the file to hold it. // DebugPrintEx(DEBUG_MSG,TEXT("Server cover page detected or no cover page specified.")); pFaxPort->hCoverPageFile=INVALID_HANDLE_VALUE; } // // If we got here there were no errors. Keep the job id. // pFaxPort->jobId = JobId; return TRUE; Error: if (NULL == pFaxPort->hFaxSvc && pFaxPort->hPrinter) { // // pFaxPort->hFaxSvc == NULL // i.e. FaxConnectFaxServer failed // // So, we need to Write to Fax Log // if (GetJobInfo(pFaxPort, JobId)) { JOB_INFO_2 *pJobInfo2 = NULL; pJobInfo2 = (PJOB_INFO_2)MyGetJob( pFaxPort->hPrinter, 2, JobId ); if (pJobInfo2) { WriteToLog(MSG_FAX_MON_CONNECT_FAILED, dwErr, pFaxPort, pJobInfo2); MemFree(pJobInfo2); } } } if(pFaxPort->hPrinter) { // // Delete print job // if (!SetJob(pFaxPort->hPrinter, JobId, 0, NULL, JOB_CONTROL_DELETE)) { DebugPrintEx(DEBUG_ERR, _T("Failed to delete job with id: %d"), JobId); } } FreeFaxJobInfo(pFaxPort); return FALSE; } // FaxMonStartDocPort 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. // DEBUG_FUNCTION_NAME(TEXT("CheckJobRestart")); DebugPrintEx(DEBUG_MSG,TEXT("Job chain: id = %d\n"), pFaxPort->nextJobId); if (pJobInfo3 = (PJOB_INFO_3)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 = (PJOB_INFO_2)MyGetJob(pFaxPort->hPrinter, 2, pFaxPort->jobId)) { if (pJobInfo2->Status & (JOB_STATUS_RESTART | JOB_STATUS_DELETING)) { status = FAXERR_RESTART; } MemFree(pJobInfo2); } return status; } // CheckJobRestart 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; BOOL Rslt; JOB_INFO_2 *pJobInfo2 = NULL; FAX_COVERPAGE_INFO_EX * pCovInfo; BOOL bBodyFileIsEmpty=FALSE; DWORD dwFileSize; DWORDLONG dwlParentJobId; // Receives teh parent job id after job submittion DWORDLONG* lpdwlRecipientJobIds = NULL; // Receives the recipient job ids after job submittion DEBUG_FUNCTION_NAME(TEXT("FaxMonEndDocPort")); // // Make sure we have a valid handle // if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) { DebugPrintEx(DEBUG_ERR,TEXT("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. // // // set pFaxPort->nextJobId to the next job id reported by JOB_INFO_3 // set to 0 if no more chained jobs. // if ((status = CheckJobRestart(pFaxPort)) != FAXERR_NONE) { goto ExitEndDocPort; } // // Check if we're at the end of a job chain // // The job chain starts with a cover page job and ends with a body job. // The cover page job has JOB_INFO_2:pParametes which is not NULL. This string // is copied to pFaxPort->pParameters when GetJobInfo() was called at FaxMonStartDocPort(). // This is pParameters is not NULL it means that the current job is a cover page job. // In case the current job is a cover page job we report that we sent the job to the printer // and do nothing more. Since we do not close the temp file to which FaxMonWriteDoc() is writing // the next job (body) will continue to write to the same file. This effectively merges the cover // page with the body. // if (pFaxPort->nextJobId != 0 && pFaxPort->pParameters != NULL) { SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER); return TRUE; } // // If we are here then we are at the end of writing the body (the last job in the chain). // In the temporaty file we have a TIFF file with the cover page followed by the body. // FlushFileBuffers(pFaxPort->hFile); if ((dwFileSize = GetFileSize(pFaxPort->hFile, NULL)) == 0) { DebugPrintEx(DEBUG_WRN, TEXT("Body TIFF file is empty.")); bBodyFileIsEmpty = TRUE; status = FAXERR_IGNORE; } if (INVALID_FILE_SIZE == dwFileSize) { status = GetLastError (); DebugPrintEx(DEBUG_ERR, TEXT("Can't get file size (ec = %ld)"), status); goto ExitEndDocPort; } CloseHandle(pFaxPort->hFile); pFaxPort->hFile = INVALID_HANDLE_VALUE; // // Call the fax service to send the TIFF file // pJobInfo2 = (PJOB_INFO_2)MyGetJob( pFaxPort->hPrinter, 2, pFaxPort->jobId ); if (pJobInfo2) { pFaxPort->JobParamsEx.lptstrDocumentName = pJobInfo2->pDocument; } else { DebugPrintEx(DEBUG_WRN, TEXT("MyGetJob failed for JobId: %d. Setting document name to NULL."), pFaxPort->jobId); pFaxPort->JobParamsEx.lptstrDocumentName = NULL; } pFaxPort->JobParamsEx.dwReserved[0] = 0xffffffff; pFaxPort->JobParamsEx.dwReserved[1] = pFaxPort->jobId; if (pFaxPort->CoverPageEx.lptstrCoverPageFileName) { // // If a cover page is specified at all. // if (pFaxPort->CoverPageEx.bServerBased) { // // Server cover page. Use the user specified path. // pCovInfo=&(pFaxPort->CoverPageEx); DebugPrintEx(DEBUG_MSG, TEXT("Using server based cover page: %s"), pFaxPort->CoverPageEx.lptstrCoverPageFileName); } else { // // Personal cover page. Use the cover page file created from the print job. // Note that there is no cleanup issue here. pCoverPageFileName is deallocated on cleanup. // and pFaxPort->CoverPageEx.lptstrCoverPageFileName is never deallocated directly. It points // to a location within pFaxPort->pParameters which is freed at cleanup. // pFaxPort->CoverPageEx.lptstrCoverPageFileName = pFaxPort->pCoverPageFileName; pCovInfo=&(pFaxPort->CoverPageEx); DebugPrintEx(DEBUG_MSG, TEXT("Using personal cover page copied to : %s"), pFaxPort->CoverPageEx.lptstrCoverPageFileName); } } else { // // No cover page specified by the user. Nullify the cover page info sent to FaxSendDocument. // pCovInfo=NULL; } if (!pCovInfo && bBodyFileIsEmpty) { DebugPrintEx(DEBUG_WRN,TEXT("Body file is empty and cover page is not specified. Job is ignored.")); status = FAXERR_IGNORE; goto ExitEndDocPort; } // // Allocate array of recipient job ids // lpdwlRecipientJobIds=(DWORDLONG*)MemAlloc(sizeof(DWORDLONG)*pFaxPort->nRecipientCount); if (!lpdwlRecipientJobIds) { DebugPrintEx(DEBUG_ERR, TEXT("Failed to allocate array of size %ld for recipient job ids (ec: 0x%0X)."), sizeof(DWORD)*pFaxPort->nRecipientCount, GetLastError()); goto ExitEndDocPort; } if (bBodyFileIsEmpty) { DebugPrintEx(DEBUG_MSG, TEXT("Sending fax with EMPTY body (cover page is available)")); Rslt = FaxSendDocumentEx( pFaxPort->hFaxSvc, NULL, // NO BODY pCovInfo, &pFaxPort->SenderProfile, pFaxPort->nRecipientCount, pFaxPort->pRecipients, &pFaxPort->JobParamsEx, &dwlParentJobId, lpdwlRecipientJobIds); } else { DebugPrintEx(DEBUG_MSG, TEXT("Sending fax with body")); Rslt = FaxSendDocumentEx( pFaxPort->hFaxSvc, pFaxPort->pFilename, pCovInfo, &pFaxPort->SenderProfile, pFaxPort->nRecipientCount, pFaxPort->pRecipients, &pFaxPort->JobParamsEx, &dwlParentJobId, lpdwlRecipientJobIds); } if (Rslt) { DebugPrintEx(DEBUG_MSG, TEXT("Successfuly submitted job. Parent Job Id = 0x%I64x"), dwlParentJobId); status = FAXERR_NONE; SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER); // // pFaxPort>pFileName will be deleted on exit by FreeFaxJobInfo() // } else { status = GetLastError(); DebugPrintEx(DEBUG_ERR, _T("FaxSendDocument failed: ec = %d, job id = %ld\n"), GetLastError(), pFaxPort->jobId); if (pJobInfo2) { WriteToLog(MSG_FAX_MON_SEND_FAILED, status, pFaxPort, pJobInfo2); } SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_DELETE); status = FAXERR_NONE; } ExitEndDocPort: // // 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_NONE: break; case FAXERR_RESTART: DebugPrintEx(DEBUG_WRN,TEXT("Job restarted or deleted: id = %d\n"), pFaxPort->jobId); // // Deliberate fall through // case FAXERR_IGNORE: SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_SENT_TO_PRINTER); break; default: DebugPrintEx(DEBUG_ERR,TEXT("Error sending fax job: id = %d\n"), pFaxPort->jobId); SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_DELETE); break; } if (pJobInfo2) { MemFree( pJobInfo2 ); pFaxPort->JobParamsEx.lptstrDocumentName = NULL; // It was set to point into pJobInfo2 } if (lpdwlRecipientJobIds) { MemFree(lpdwlRecipientJobIds); lpdwlRecipientJobIds=NULL; } FreeFaxJobInfo(pFaxPort); return (status < FAXERR_SPECIAL); } // FaxMonEndDocPort 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; BOOL bRet = TRUE; // // Make sure we have a valid handle // DEBUG_FUNCTION_NAME(TEXT("FaxMonWritePort")); if (! ValidFaxPort(pFaxPort) || ! pFaxPort->hFaxSvc) { DebugPrintEx(DEBUG_ERR,TEXT("WritePort is given an invalid fax port handle\n")); SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (pFaxPort->bCoverPageJob) { // // If pFaxPort->bCoverPageJob is on it means that the print job that writes is the cover page print job. // If the cover page is a personal cover page then it is embedded (the template itself) in this print job // and we write it to the temp cover page file we created earlier (pFaxPort->hCoverPageFile). // If the cover page is a server cover page then it is NOT embedded in the print job (since it can be found // directly on the server) and we do not create a temp cover page file and do not write the print job content // into it. // if (!pFaxPort->CoverPageEx.bServerBased) { // // Personal cover page // Assert(pFaxPort->hCoverPageFile != INVALID_HANDLE_VALUE); if(!WriteFile(pFaxPort->hCoverPageFile, pBuffer, cbBuf, pcbWritten, NULL)) { bRet = FALSE; DebugPrintEx(DEBUG_ERR,TEXT("WriteFile failed (ec: %d)"), GetLastError()); } } else { // // Server cover page - the print job body is empty and the port shoult not be written to. // This should never execute. // Assert(FALSE); *pcbWritten = cbBuf; } } else { Assert(pFaxPort->hFile != INVALID_HANDLE_VALUE); if(!WriteFile(pFaxPort->hFile, pBuffer, cbBuf, pcbWritten, NULL)) { bRet = FALSE; DebugPrintEx(DEBUG_ERR,TEXT("WriteFile failed (ec: %d)"), GetLastError()); } } if(!bRet) { // // Operation failed // Delete print job // if (!SetJob(pFaxPort->hPrinter, pFaxPort->jobId, 0, NULL, JOB_CONTROL_DELETE)) { DebugPrintEx(DEBUG_ERR, _T("Failed to delete job with id: %d"), pFaxPort->jobId); } } return bRet; } // FaxMonWritePort 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxMonReadPort")); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // FaxMonReadPort 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; DEBUG_FUNCTION_NAME(TEXT("FaxMonEnumPorts")); DEBUG_TRACE_ENTER; if (pcbNeeded == NULL || pReturned == NULL || (pPorts == NULL && cbBuf != 0)) { DebugPrintEx(DEBUG_ERR,TEXT("Invalid input parameters\n")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } // // Load the fax port description string // if (!LoadString(g_hResource, 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; default: ASSERT_FALSE; cbNeeded = 0xffffffff; 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 screw up royally. // 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; DebugPrintEx(DEBUG_MSG, TEXT("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; DebugPrintEx(DEBUG_MSG, TEXT("Port info 2: %ws, %ws, %ws\n"), pPortInfo2->pPortName, pPortInfo2->pMonitorName, pPortInfo2->pDescription); pPortInfo2++; break; default: ASSERT_FALSE; status = FALSE; break; } } return status; } // FaxMonEnumPorts 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] = {0}; TCHAR message[256] = {0}; LoadString(g_hResource, titleId, title, 128); LoadString(g_hResource, IDS_CONFIG_ERROR, message, 256); AlignedMessageBox(hwnd, message, title, MB_OK|MB_ICONERROR); SetLastError(ERROR_SUCCESS); return FALSE; } // DisplayErrorNotImplemented 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxMonAddPort")); return DisplayErrorNotImplemented(hwnd, IDS_ADD_PORT); } // FaxMonAddPort 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxMonAddPortEx")); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // FaxMonAddPortEx 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxMonDeletePort")); return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT); } // FaxMonDeletePort 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 --*/ { DEBUG_FUNCTION_NAME(TEXT("FaxMonConfigurePort")); return DisplayErrorNotImplemented(hwnd, IDS_CONFIGURE_PORT); } // FaxMonConfigurePort 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; DEBUG_FUNCTION_NAME(TEXT("MyGetJob")); if (!GetJob(hPrinter, jobId, level, NULL, 0, &cbNeeded) && GetLastError() == ERROR_INSUFFICIENT_BUFFER && (pJobInfo = (PBYTE)MemAlloc(cbNeeded)) && GetJob(hPrinter, jobId, level, pJobInfo, cbNeeded, &cbNeeded)) { return pJobInfo; } DebugPrintEx(DEBUG_ERR,TEXT("GetJob failed: %d\n"), GetLastError()); MemFree(pJobInfo); return NULL; } // MyGetJob BOOL WriteToLog( IN DWORD dwMsgId, IN DWORD dwError, IN PFAXPORT pFaxPort, IN JOB_INFO_2* pJobInfo ) /*++ Routine name : WriteToLog Routine description: Write to the Event Log of Fax Service Author: Iv Garber (IvG), Sep, 2000 Arguments: dwError [in] - error code pFaxPort [in] - data about the fax pJobInfo [in] - data about the fax job Return Value: TRUE if succeded to write to the event log, FALSE otherwise. --*/ { DWORD dwBufferSize = MAX_PATH - 1; TCHAR tszBuffer[MAX_PATH] = {0}; BOOL bRes = FALSE; DEBUG_FUNCTION_NAME(_T("WriteToLog")); if (FAX_ERR_RECIPIENTS_LIMIT == dwError) { DWORD dwRecipientsLimit = 0; if (!FaxGetRecipientsLimit( pFaxPort->hFaxSvc, &dwRecipientsLimit)) { DebugPrintEx(DEBUG_ERR, _T("FaxGetRecipientsLimit() failed: %ld"), GetLastError()); } // // Write to the Event Log // bRes = FaxLog(FAXLOG_CATEGORY_OUTBOUND, FAXLOG_LEVEL_MIN, 5, MSG_FAX_MON_SEND_RECIPIENT_LIMIT, pJobInfo->pMachineName, pJobInfo->pUserName, pFaxPort->SenderProfile.lptstrName, DWORD2DECIMAL(pFaxPort->nRecipientCount), DWORD2DECIMAL(dwRecipientsLimit)); } else { // // Write to the Event Log // bRes = FaxLog(FAXLOG_CATEGORY_OUTBOUND, FAXLOG_LEVEL_MIN, 5, dwMsgId, DWORD2DECIMAL(dwError), pJobInfo->pMachineName, pJobInfo->pUserName, pFaxPort->SenderProfile.lptstrName, DWORD2DECIMAL(pFaxPort->nRecipientCount)); } if (!bRes) { DebugPrintEx(DEBUG_ERR, _T("FaxLog() failed, ec = %ld"), GetLastError()); } return bRes; } // WriteToLog 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 NOTICE: We're not using the utility function StringDup on purpose. StringDup uses the utility MemAlloc / MemFree heap management routines. However, in this module, MemAlloc / MemFree are remapped (in faxmon.h) to LocalAlloc / LocalFree. --*/ { LPTSTR pDestStr; INT strSize; DEBUG_FUNCTION_NAME(TEXT("DuplicateString")); if (pSrcStr != NULL) { strSize = SizeOfString(pSrcStr); if (pDestStr = (LPTSTR)MemAlloc(strSize)) { CopyMemory(pDestStr, pSrcStr, strSize); } else { DebugPrintEx(DEBUG_ERR,TEXT("Memory allocation failed\n")); } } else { pDestStr = NULL; } return pDestStr; } // DuplicateString