/*++ Copyright (c) 1996 Microsoft Corporation Module Name: util.c Abstract: This module contains various utility functions. Author: Wesley Witt (wesw) 16-Jan-1996 Revision History: --*/ #include "faxsvc.h" #pragma hdrstop #if DBG extern HANDLE hLogFile; extern LIST_ENTRY CritSecListHead; #endif typedef struct _STRING_TABLE { DWORD ResourceId; DWORD InternalId; LPTSTR String; } STRING_TABLE, *PSTRING_TABLE; static STRING_TABLE StringTable[] = { { IDS_DIALING, FPS_DIALING, NULL }, { IDS_SENDING, FPS_SENDING, NULL }, { IDS_RECEIVING, FPS_RECEIVING, NULL }, { IDS_COMPLETED, FPS_COMPLETED, NULL }, { IDS_HANDLED, FPS_HANDLED, NULL }, { IDS_BUSY, FPS_BUSY, NULL }, { IDS_NO_ANSWER, FPS_NO_ANSWER, NULL }, { IDS_BAD_ADDRESS, FPS_BAD_ADDRESS, NULL }, { IDS_NO_DIAL_TONE, FPS_NO_DIAL_TONE, NULL }, { IDS_DISCONNECTED, FPS_DISCONNECTED, NULL }, { IDS_FATAL_ERROR, FPS_FATAL_ERROR, NULL }, { IDS_NOT_FAX_CALL, FPS_NOT_FAX_CALL, NULL }, { IDS_CALL_DELAYED, FPS_CALL_DELAYED, NULL }, { IDS_CALL_BLACKLISTED, FPS_CALL_BLACKLISTED, NULL }, { IDS_UNAVAILABLE, FPS_UNAVAILABLE, NULL }, { IDS_AVAILABLE, FPS_AVAILABLE, NULL }, { IDS_ABORTING, FPS_ABORTING, NULL }, { IDS_ROUTING, FPS_ROUTING, NULL }, { IDS_INITIALIZING, FPS_INITIALIZING, NULL }, { IDS_SENDFAILED, FPS_SENDFAILED, NULL }, { IDS_SENDRETRY, FPS_SENDRETRY, NULL }, { IDS_BLANKSTR, FPS_BLANKSTR, NULL }, { IDS_ROUTERETRY, FPS_ROUTERETRY, NULL }, { IDS_ANSWERED, FPS_ANSWERED, NULL }, { IDS_DR_SUBJECT, IDS_DR_SUBJECT, NULL }, { IDS_DR_FILENAME, IDS_DR_FILENAME, NULL }, { IDS_NDR_SUBJECT, IDS_NDR_SUBJECT, NULL }, { IDS_NDR_FILENAME, IDS_NDR_FILENAME, NULL }, { IDS_POWERED_OFF_MODEM, IDS_POWERED_OFF_MODEM, NULL }, { IDS_SERVICE_NAME, IDS_SERVICE_NAME, NULL }, { IDS_NO_MAPI_LOGON, IDS_NO_MAPI_LOGON, NULL }, { IDS_DEFAULT, IDS_DEFAULT, NULL }, { IDS_SERVER_NAME, IDS_SERVER_NAME, NULL }, { IDS_FAX_LOG_CATEGORY_INIT_TERM, IDS_FAX_LOG_CATEGORY_INIT_TERM, NULL }, { IDS_FAX_LOG_CATEGORY_OUTBOUND, IDS_FAX_LOG_CATEGORY_OUTBOUND, NULL }, { IDS_FAX_LOG_CATEGORY_INBOUND, IDS_FAX_LOG_CATEGORY_INBOUND, NULL }, { IDS_FAX_LOG_CATEGORY_UNKNOWN, IDS_FAX_LOG_CATEGORY_UNKNOWN, NULL }, { IDS_SET_CONFIG, IDS_SET_CONFIG, NULL }, { IDS_NO_SEND_DEVICES, IDS_NO_SEND_DEVICES, NULL}, { IDS_MODEM_PROVIDER_NAME, IDS_MODEM_PROVIDER_NAME, NULL} }; #define CountStringTable (sizeof(StringTable)/sizeof(STRING_TABLE)) VOID InitializeStringTable( VOID ) { DWORD i; HINSTANCE hInstance; TCHAR Buffer[256]; hInstance = GetModuleHandle(NULL); for (i=0; i1) && (ErrorBuf[Count-2] == TEXT('\r'))) { ErrorBuf[Count-2] = 0; } } return ErrorBuf; } LPTSTR GetString( DWORD InternalId ) /*++ Routine Description: Loads a resource string and returns a pointer to the string. The caller must free the memory. Arguments: ResourceId - resource string id Return Value: pointer to the string --*/ { DWORD i; for (i=0; i FileNameSize) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } for (i=0; i<256; i++) { HANDLE hFile = INVALID_HANDLE_VALUE; _stprintf( FileName, TEXT("%s\\%04x%04x%02x.%s"), Directory, FatTime, FatDate, i, Extension ); hFile = CreateFile( FileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { DWORD Error = GetLastError(); if (Error == ERROR_ALREADY_EXISTS || Error == ERROR_FILE_EXISTS) { continue; } else { return 0; } } else { CloseHandle( hFile ); break; } } if (i == 256) { SetLastError( ERROR_TOO_MANY_OPEN_FILES ); return 0; } return MAKELONGLONG( MAKELONG( FatDate, FatTime ), i ); } DWORD MessageBoxThread( IN PMESSAGEBOX_DATA MsgBox ) { DWORD Answer = (DWORD) MessageBox( NULL, MsgBox->Text, GetString( IDS_SERVICE_NAME ), MsgBox->Type | MB_SERVICE_NOTIFICATION ); if (MsgBox->Response) { *MsgBox->Response = Answer; } MemFree( MsgBox->Text ); MemFree( MsgBox ); return 0; } BOOL ServiceMessageBox( IN LPCTSTR MsgString, IN DWORD Type, IN BOOL UseThread, IN LPDWORD Response, IN ... ) { #define BUFSIZE 1024 PMESSAGEBOX_DATA MsgBox; DWORD ThreadId; HANDLE hThread; DWORD Answer; LPTSTR buf; va_list arg_ptr; buf = (LPTSTR) MemAlloc( BUFSIZE ); if (!buf) { return FALSE; } va_start( arg_ptr, Response ); _vsntprintf( buf, BUFSIZE, MsgString, arg_ptr ); va_end( arg_ptr ); if (UseThread) { MsgBox = MemAlloc( sizeof(MESSAGEBOX_DATA) ); if (!MsgBox) { MemFree( buf ); return FALSE; } MsgBox->Text = buf; MsgBox->Response = Response; MsgBox->Type = Type; hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) MessageBoxThread, (LPVOID) MsgBox, 0, &ThreadId ); if (!hThread) { MemFree( buf ); MemFree( MsgBox ); return FALSE; } CloseHandle( hThread ); return TRUE; } Answer = MessageBox( NULL, buf, GetString( IDS_SERVICE_NAME ), Type | MB_SERVICE_NOTIFICATION ); if (Response) { *Response = Answer; } MemFree( buf ); return TRUE; } BOOL CreateFaxEvent( DWORD DeviceId, DWORD EventId, DWORD JobId ) { PFAX_EVENT FaxEvent; Assert(EventId != 0); FaxEvent = MemAlloc( sizeof(FAX_EVENT) ); if (!FaxEvent) { return FALSE; } FaxEvent->SizeOfStruct = sizeof(FAX_EVENT); GetSystemTimeAsFileTime( &FaxEvent->TimeStamp ); FaxEvent->EventId = EventId; FaxEvent->DeviceId = DeviceId; FaxEvent->JobId = JobId; PostQueuedCompletionStatus( StatusCompletionPortHandle, sizeof(FAX_EVENT), EVENT_COMPLETION_KEY, (LPOVERLAPPED) FaxEvent ); return TRUE; } DWORD MapStatusIdToEventId( DWORD StatusId ) { DWORD EventId = 0; switch( StatusId ) { case FS_INITIALIZING: EventId = FEI_INITIALIZING; break; case FS_DIALING: EventId = FEI_DIALING; break; case FS_TRANSMITTING: EventId = FEI_SENDING; break; case FS_RECEIVING: EventId = FEI_RECEIVING; break; case FS_COMPLETED: EventId = FEI_COMPLETED; break; case FS_HANDLED: EventId = FEI_HANDLED; break; case FS_LINE_UNAVAILABLE: EventId = FEI_LINE_UNAVAILABLE; break; case FS_BUSY: EventId = FEI_BUSY; break; case FS_NO_ANSWER: EventId = FEI_NO_ANSWER; break; case FS_BAD_ADDRESS: EventId = FEI_BAD_ADDRESS; break; case FS_NO_DIAL_TONE: EventId = FEI_NO_DIAL_TONE; break; case FS_DISCONNECTED: EventId = FEI_DISCONNECTED; break; case FS_FATAL_ERROR: EventId = FEI_FATAL_ERROR; break; case FS_NOT_FAX_CALL: EventId = FEI_NOT_FAX_CALL; break; case FS_CALL_DELAYED: EventId = FEI_CALL_DELAYED; break; case FS_CALL_BLACKLISTED: EventId = FEI_CALL_BLACKLISTED; break; case FS_USER_ABORT: EventId = FEI_ABORTING; break; case FS_ANSWERED: EventId = FEI_ANSWERED; break; } return EventId; } VOID FaxLogSend( PFAX_SEND_ITEM FaxSendItem, BOOL Rslt, PFAX_DEV_STATUS FaxStatus, BOOL Retrying ) /*++ Routine Description: Log a fax send event. Arguments: FaxSendItem - Pointer to FAX_SEND_ITEM structure for this fax. Rslt - BOOL returned from device provider. TRUE means fax was sent. FaxStatus - Pointer to FAX_DEV_STATUS for this fax. PrinterName - Name of fax printer. Retrying - TRUE if another send attempt will be made. Return Value: VOID --*/ { DWORD Level; DWORD FormatId; TCHAR PageCountStr[64]; TCHAR TimeStr[128]; BOOL fLog = TRUE; FormatElapsedTimeStr( (FILETIME*)&FaxSendItem->JobEntry->ElapsedTime, TimeStr, 128 ); _ltot((LONG) FaxStatus->PageCount, PageCountStr, 10); if (Rslt) { FaxLog( FAXLOG_CATEGORY_OUTBOUND, FAXLOG_LEVEL_MED, 10, MSG_FAX_SEND_SUCCESS, FaxSendItem->SenderName, FaxSendItem->BillingCode, FaxSendItem->SenderCompany, FaxSendItem->SenderDept, FaxSendItem->RecipientName, FaxSendItem->JobEntry->PhoneNumber, FaxStatus->CSI, PageCountStr, TimeStr, FaxSendItem->JobEntry->LineInfo->DeviceName ); } else { switch (FaxStatus->StatusId) { case FS_FATAL_ERROR: Level = Retrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_FATAL_RETRY : MSG_FAX_SEND_FATAL_ABORT; break; case FS_NO_DIAL_TONE: Level = Retrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_NDT_RETRY : MSG_FAX_SEND_NDT_ABORT; break; case FS_NO_ANSWER: Level = Retrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_NA_RETRY : MSG_FAX_SEND_NA_ABORT; break; case FS_DISCONNECTED: Level = Retrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_INTERRUPT_RETRY : MSG_FAX_SEND_INTERRUPT_ABORT; break; case FS_NOT_FAX_CALL: Level = Retrying ? FAXLOG_LEVEL_MED : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_NOTFAX_RETRY : MSG_FAX_SEND_NOTFAX_ABORT; break; case FS_BUSY: Level = Retrying ? FAXLOG_LEVEL_MAX : FAXLOG_LEVEL_MIN; FormatId = Retrying ? MSG_FAX_SEND_BUSY_RETRY : MSG_FAX_SEND_BUSY_ABORT; break; case FS_USER_ABORT: Level = FAXLOG_LEVEL_MED; FormatId = MSG_FAX_SEND_USER_ABORT; break; default: fLog = FALSE; } if(fLog) { FaxLog( FAXLOG_CATEGORY_OUTBOUND, Level, 7, FormatId, FaxSendItem->SenderName, FaxSendItem->BillingCode, FaxSendItem->SenderCompany, FaxSendItem->SenderDept, FaxSendItem->RecipientName, FaxSendItem->JobEntry->PhoneNumber, FaxSendItem->JobEntry->LineInfo->DeviceName ); } } } BOOL SetServiceStart( LPTSTR ServiceName, DWORD StartType ) { BOOL rVal = FALSE; SC_HANDLE hSvcMgr; SC_HANDLE hService; hSvcMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if (!hSvcMgr) { DebugPrint(( TEXT("could not open service manager: error code = %u"), GetLastError() )); goto exit; } hService = OpenService( hSvcMgr, ServiceName, SERVICE_ALL_ACCESS ); if (!hService) { DebugPrint(( TEXT("could not open the %s service: error code = %u"), ServiceName, GetLastError() )); goto exit; } if (!ChangeServiceConfig( hService, // handle to service SERVICE_NO_CHANGE, // type of service StartType, // when to start service SERVICE_NO_CHANGE, // severity if service fails to start NULL, // pointer to service binary file name NULL, // pointer to load ordering group name NULL, // pointer to variable to get tag identifier NULL, // pointer to array of dependency names NULL, // pointer to account name of service NULL, // pointer to password for service account NULL // pointer to display name )) { DebugPrint(( TEXT("could not open change service configuration, ec=%d"), GetLastError() )); goto exit; } rVal = TRUE; exit: CloseServiceHandle( hService ); CloseServiceHandle( hSvcMgr ); return rVal; } DWORD MyGetFileSize(LPCTSTR FileName) { HANDLE hFile = INVALID_HANDLE_VALUE; DWORD sizelow=0, sizehigh=0; hFile = CreateFile( FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); if (hFile == INVALID_HANDLE_VALUE) { return 0; } sizelow = GetFileSize(hFile,&sizehigh); if (sizehigh == 0xFFFFFFFFF) { sizelow = 0; } else if (sizehigh!=0) { sizelow=0xFFFFFFFF; } CloseHandle(hFile); return sizelow; } extern CRITICAL_SECTION CsClients; extern CRITICAL_SECTION CsHandleTable; extern CRITICAL_SECTION CsJob; extern CRITICAL_SECTION CsLine; extern CRITICAL_SECTION CsPerfCounters; extern CRITICAL_SECTION CsQueue; extern CRITICAL_SECTION CsRouting; LPCWSTR szCsClients = L"CsClients"; LPCWSTR szCsHandleTable = L"CsHandleTable"; LPCWSTR szCsJob = L"CsJob"; LPCWSTR szCsLine = L"CsLine"; LPCWSTR szCsPerfCounters = L"CsPerfCounters"; LPCWSTR szCsQueue = L"CsQueue"; LPCWSTR szCsRouting = L"CsRouting"; LPCWSTR szCsUnknown = L"Other CS"; LPCWSTR GetSzCs( LPCRITICAL_SECTION cs ) { if (cs == &CsClients) { return szCsClients; } else if (cs == &CsHandleTable) { return szCsHandleTable; } else if (cs == &CsJob) { return szCsJob; } else if (cs == &CsLine) { return szCsLine; } else if (cs == &CsPerfCounters) { return szCsPerfCounters; } else if (cs == &CsQueue) { return szCsQueue; } else if (cs == &CsRouting) { return szCsRouting; } return szCsUnknown; } #if DBG VOID AppendToLogFile( LPWSTR String ) { DWORD BytesWritten; LPSTR AnsiBuffer = UnicodeStringToAnsiString( String ); if (hLogFile != INVALID_HANDLE_VALUE) { WriteFile(hLogFile,(LPBYTE)AnsiBuffer,strlen(AnsiBuffer) * sizeof(CHAR),&BytesWritten,NULL); } MemFree(AnsiBuffer); } VOID AppendFuncToLogFile( LPCRITICAL_SECTION cs, LPTSTR szFunc, DWORD line, LPTSTR file, PDBGCRITSEC CritSec ) { WCHAR Buffer[300]; LPWSTR FileName; LPCWSTR szcs = GetSzCs(cs); FileName = wcsrchr(file,'\\'); if (!FileName) { FileName = TEXT("Unknown "); } else { FileName += 1; } if (CritSec) { wsprintf(Buffer,TEXT("%d\t%x\t%s\t%s\t%s\t%d\t%d\r\n"), GetTickCount(), (PULONG_PTR)cs, szcs, szFunc, FileName, line, CritSec->ReleasedTime - CritSec->AquiredTime); } else { wsprintf(Buffer,TEXT("%d\t%x\t%s\t%s\t%s\t%d\r\n"),GetTickCount(),(PULONG_PTR)cs,szcs,szFunc, FileName,line); } AppendToLogFile( Buffer ); return; } VOID pEnterCriticalSection( LPCRITICAL_SECTION cs, DWORD line, LPTSTR file ) { extern CRITICAL_SECTION CsJob; extern CRITICAL_SECTION CsQueue; //PDBGCRITSEC CritSec; PDBGCRITSEC pCritSec = MemAlloc(sizeof(DBGCRITSEC)); pCritSec->CritSecAddr = (ULONG_PTR) cs; pCritSec->AquiredTime = GetTickCount(); pCritSec->ThreadId = GetCurrentThreadId(); InsertHeadList( &CritSecListHead, &pCritSec->ListEntry ); AppendFuncToLogFile(cs,TEXT("EnterCriticalSection"), line, file, NULL ); #ifdef EnterCriticalSection #undef EnterCriticalSection // // check ordering of threads. ALWAYS aquire CsJob before aquiring CsQueue!!! // if ((LPCRITICAL_SECTION)cs == (LPCRITICAL_SECTION)&CsQueue) { if ((DWORD)GetCurrentThreadId() != PtrToUlong(CsJob.OwningThread)) { WCHAR DebugBuf[300]; wsprintf(DebugBuf, TEXT("%d : Attempting to aquire CsQueue (thread %x) without aquiring CsJob (thread %x, lock count %x) first, possible deadlock!\r\n"), GetTickCount(), GetCurrentThreadId(), CsJob.OwningThread, CsJob.LockCount ); AppendToLogFile( DebugBuf ); } } EnterCriticalSection(cs); #endif } VOID pLeaveCriticalSection( LPCRITICAL_SECTION cs, DWORD line, LPTSTR file ) { PDBGCRITSEC CritSec; PLIST_ENTRY Next = CritSecListHead.Flink; while ((ULONG_PTR)Next != (ULONG_PTR) &CritSecListHead) { CritSec = CONTAINING_RECORD( Next, DBGCRITSEC, ListEntry ); if ((ULONG_PTR)CritSec->CritSecAddr == (ULONG_PTR) cs && ( GetCurrentThreadId() == CritSec->ThreadId ) ) { CritSec->ReleasedTime = GetTickCount(); break; } Next = Next->Flink; } AppendFuncToLogFile(cs,TEXT("LeaveCriticalSection"),line, file, CritSec ); if (CritSec) { RemoveEntryList( &CritSec->ListEntry ); MemFree( CritSec ); } #ifdef LeaveCriticalSection #undef LeaveCriticalSection LeaveCriticalSection(cs); #endif } VOID pInitializeCriticalSection( LPCRITICAL_SECTION cs, DWORD line, LPTSTR file ) { AppendFuncToLogFile(cs,TEXT("InitializeCriticalSection"),line, file, NULL); #ifdef InitializeCriticalSection #undef InitializeCriticalSection InitializeCriticalSection(cs); #endif } #endif DWORD ValidateTiffFile( LPCWSTR TifFileName ) { HANDLE hTiff; DWORD rc = ERROR_SUCCESS; TIFF_INFO TiffInfo; // // impersonate the client // if (RpcImpersonateClient(NULL) != RPC_S_OK) { rc = GetLastError(); goto e0; } // // make sure the client can see the file // if (GetFileAttributes(TifFileName) == 0xFFFFFFFF) { rc = GetLastError(); goto e1; } // // make sure the client has read-write access to the file // hTiff = TiffOpen( (LPWSTR)TifFileName, &TiffInfo, FALSE, FILLORDER_MSB2LSB ); if (!hTiff) { rc = GetLastError(); goto e1; } TiffClose( hTiff ); e1: RpcRevertToSelf(); e0: return rc; }