Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

728 lines
15 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
faxitg.cpp
Abstract:
This file implements the itg routing extension.
The purpose of this routing extension is to create
a queue of faxes that are processed by network
client applications. The clients are manual routers
that simply display the fax and allow the operator
to send the fax via email.
This code has 2 main parts, routing extension that
is called by the fax service and the client service
threads.
The routing extension part simply copies the fax file
into a disk directory that becomes the queue.
The client service threads service requests from the
clients for retrieving queued faxes. The clients
communicate through the use of sockets.
Author:
Wesley Witt (wesw) 13-May-1997
Environment:
User Mode
--*/
#include <windows.h>
#include <winsock2.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <lm.h>
#include "faxutil.h"
#include "faxroute.h"
#include "faxsvr.h"
#define MAX_THREADS 1
#define FAX_RECEIVE_DIR TEXT("%systemroot%\\faxreceive")
#define FILE_QUEUE_DIR TEXT("%systemroot%\\faxreceive\\itg")
#define ITG_SHARE_NAME TEXT("Itg")
#define ITG_SHARE_COMMENT TEXT("ITG Fax Queue")
typedef struct _CLIENT_DATA {
SOCKET Socket;
OVERLAPPED Overlapped;
FAX_QUEUE_MESSAGE Message;
} CLIENT_DATA, *PCLIENT_DATA;
PFAXROUTEGETFILE FaxRouteGetFile;
HINSTANCE MyhInstance;
BOOL ServiceDebug;
LPWSTR FaxReceiveDir;
LPWSTR FileQueueDir;
PSID ServiceSid;
WSADATA WsaData;
CLIENT_DATA ClientData[MAX_CLIENTS];
DWORD ClientCount;
DWORD
ServerWorkerThread(
HANDLE ServerCompletionPort
)
/*++
Routine Description:
This is the worker thread for servicing client
requests for retrieving faxes.
Arguments:
ServerCompletionPort - Completion port handle
Return Value:
Thread return value.
--*/
{
BOOL Rval;
DWORD Bytes;
WCHAR FileName[MAX_PATH];
WIN32_FIND_DATA FindData;
HANDLE hFind;
DWORD ClientIndex;
LPOVERLAPPED Overlapped;
PCLIENT_DATA ThisClient;
while( TRUE ) {
//
// get the next packet
//
Rval = GetQueuedCompletionStatus(
ServerCompletionPort,
&Bytes,
&ClientIndex,
&Overlapped,
INFINITE
);
if (!Rval) {
Rval = GetLastError();
continue;
}
ThisClient = &ClientData[ClientIndex];
if (ThisClient->Message.Request == REQ_NEXT_FAX) {
//
// get the next tif file in the queue
//
wcscpy( FileName, FileQueueDir );
wcscat( FileName, L"\\*.tif" );
hFind = FindFirstFile( FileName, &FindData );
if (hFind == INVALID_HANDLE_VALUE) {
FindClose( hFind );
ThisClient->Message.Response = RSP_BAD;
} else {
FindClose( hFind );
ThisClient->Message.Response = RSP_GOOD;
wcscpy( (LPWSTR) ThisClient->Message.Buffer, FindData.cFileName );
}
//
// send the file name to the client
//
Bytes = send( ThisClient->Socket, (char*)&ThisClient->Message, sizeof(FAX_QUEUE_MESSAGE), 0 );
if (Bytes == (DWORD)SOCKET_ERROR) {
DebugPrint(( L"Windows Sockets error %d: Couldn't send data to client\n", WSAGetLastError() ));
}
goto next_packet;
}
if (ThisClient->Message.Request == REQ_ACK) {
if (ThisClient->Message.Response == RSP_GOOD) {
//
// if the client successfully copied the file
// then delete it from the queue
//
wcscpy( FileName, FileQueueDir );
wcscat( FileName, L"\\" );
wcscat( FileName, (LPWSTR) ThisClient->Message.Buffer );
DeleteFile( FileName );
}
goto next_packet;
}
next_packet:
//
// re-prime the pump for this client by queuing another read
//
ReadFile(
(HANDLE) ThisClient->Socket,
(LPVOID) &ThisClient->Message,
sizeof(FAX_QUEUE_MESSAGE),
&Bytes,
&ThisClient->Overlapped
);
}
return 0;
}
DWORD
ServerNetworkThread(
LPVOID lpv
)
/*++
Routine Description:
This is the worker thread for servicing initial
client connections.
Arguments:
Not used.
Return Value:
Thread return value.
--*/
{
SOCKET srv_sock, cli_sock;
SOCKADDR_IN srv_addr;
HANDLE ServerCompletionPort;
HANDLE hThread;
DWORD ThreadId;
HANDLE CompletionPort;
DWORD Bytes;
//
// create the single completion port that
// is used to service client requests
//
ServerCompletionPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
0,
1
);
if (!ServerCompletionPort) {
return FALSE;
}
//
// create the thread to service client requests
//
hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) ServerWorkerThread,
(LPVOID) ServerCompletionPort,
0,
&ThreadId
);
if (!hThread) {
return FALSE;
}
CloseHandle( hThread );
//
// Create the server-side socket
//
srv_sock = socket( AF_INET, SOCK_STREAM, 0 );
if (srv_sock == INVALID_SOCKET){
DebugPrint(( L"Windows Sockets error %d: Couldn't create socket.", WSAGetLastError() ));
return FALSE;
}
//
// create the server address
//
ZeroMemory( &srv_addr, sizeof(srv_addr) );
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons( SERVICE_PORT );
//
// Bind socket to the appropriate port and interface
//
if (bind(srv_sock, (LPSOCKADDR)&srv_addr, sizeof(srv_addr) ) == SOCKET_ERROR){
DebugPrint(( L"Windows Sockets error %d: Couldn't bind socket.", WSAGetLastError() ));
return FALSE;
}
//
// Listen for incoming connections
//
if (listen( srv_sock, MAX_CLIENTS ) == SOCKET_ERROR){
DebugPrint(( L"Windows Sockets error %d: Couldn't set up listen on socket.", WSAGetLastError() ));
return FALSE;
}
//
// Accept and service incoming connection requests indefinitely
//
while (TRUE) {
//
// get a new client connection
//
cli_sock = accept( srv_sock, NULL, NULL );
if (cli_sock==INVALID_SOCKET){
DebugPrint(( L"Windows Sockets error %d: Couldn't accept incoming connection on socket.",WSAGetLastError() ));
return FALSE;
}
//
// setup the client data struct
//
ClientData[ClientCount].Socket = cli_sock;
//
// add the client to the completion port for reads
//
CompletionPort = CreateIoCompletionPort(
(HANDLE) ClientData[ClientCount].Socket,
ServerCompletionPort,
ClientCount,
MAX_THREADS
);
if (CompletionPort == NULL) {
DebugPrint(( L"Failed to add a socket to the completion port, ec=%d", GetLastError() ));
closesocket( cli_sock );
continue;
}
//
// prime the pump by queueing up a read
//
ReadFile(
(HANDLE) ClientData[ClientCount].Socket,
(LPVOID) &ClientData[ClientCount].Message,
sizeof(FAX_QUEUE_MESSAGE),
&Bytes,
&ClientData[ClientCount].Overlapped
);
//
// next client
//
ClientCount += 1;
}
return TRUE;
}
extern "C"
DWORD
FaxRouteDllInit(
HINSTANCE hInstance,
DWORD Reason,
LPVOID Context
)
/*++
Routine Description:
This is the dll entrypoint for this routing extension.
Arguments:
hInstance - Module handle
Reason - Reason for being called
Context - Register context
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
if (Reason == DLL_PROCESS_ATTACH) {
MyhInstance = hInstance;
DisableThreadLibraryCalls( hInstance );
}
return TRUE;
}
BOOL
CreateNetworkShare(
LPTSTR Path,
LPTSTR ShareName,
LPTSTR Comment
)
/*++
Routine Description:
This functions creates a network share,
shares out a local disk directory.
Arguments:
Path - Local disk directory to share
ShareName - Name of the share
Comment - Comments
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
SHARE_INFO_2 ShareInfo;
NET_API_STATUS rVal;
TCHAR ExpandedPath[MAX_PATH*2];
ExpandEnvironmentStrings( Path, ExpandedPath, sizeof(ExpandedPath) );
ShareInfo.shi2_netname = ShareName;
ShareInfo.shi2_type = STYPE_DISKTREE;
ShareInfo.shi2_remark = Comment;
ShareInfo.shi2_permissions = ACCESS_ALL;
ShareInfo.shi2_max_uses = (DWORD) -1,
ShareInfo.shi2_current_uses = (DWORD) -1;
ShareInfo.shi2_path = ExpandedPath;
ShareInfo.shi2_passwd = NULL;
rVal = NetShareAdd(
NULL,
2,
(LPBYTE) &ShareInfo,
NULL
);
return rVal == 0;
}
BOOL WINAPI
FaxRouteInitialize(
IN HANDLE HeapHandle,
IN PFAXROUTEADDFILE pFaxRouteAddFile,
IN PFAXROUTEDELETEFILE pFaxRouteDeleteFile,
IN PFAXROUTEGETFILE pFaxRouteGetFile,
IN PFAXROUTEENUMFILES pFaxRouteEnumFiles
)
/*++
Routine Description:
This functions is called by the fax service to
initialize the routing extension.
Arguments:
HeapHandle - Heap handle for memory all allocations
pFaxRouteAddFile - Support function for adding files to routing file list
pFaxRouteDeleteFile - Support function for deleting files from the routing file list
pFaxRouteGetFile - Support function for geting files from the routing file list
pFaxRouteEnumFiles - Support function for enumerating files in the routing file list
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
HANDLE hThread;
DWORD ThreadId;
DWORD Size;
FaxRouteGetFile = pFaxRouteGetFile;
HeapInitialize(HeapHandle,NULL,NULL,0);
Size = ExpandEnvironmentStrings( FAX_RECEIVE_DIR, FaxReceiveDir, 0 );
if (Size) {
FaxReceiveDir = (LPWSTR) MemAlloc( Size * sizeof(WCHAR) );
if (FaxReceiveDir) {
ExpandEnvironmentStrings( FAX_RECEIVE_DIR, FaxReceiveDir, Size );
}
}
Size = ExpandEnvironmentStrings( FILE_QUEUE_DIR, FileQueueDir, 0 );
if (Size) {
FileQueueDir = (LPWSTR) MemAlloc( Size * sizeof(WCHAR) );
if (FileQueueDir) {
ExpandEnvironmentStrings( FILE_QUEUE_DIR, FileQueueDir, Size );
}
}
if (FaxReceiveDir == NULL || FileQueueDir == NULL) {
return FALSE;
}
CreateNetworkShare( FileQueueDir, ITG_SHARE_NAME, ITG_SHARE_COMMENT );
//
// initialize winsock
//
if (WSAStartup( 0x0101, &WsaData )) {
return FALSE;
}
hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) ServerNetworkThread,
NULL,
0,
&ThreadId
);
if (!hThread) {
return FALSE;
}
return TRUE;
}
BOOL WINAPI
FaxRouteGetRoutingInfo(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LPBYTE RoutingInfo,
OUT LPDWORD RoutingInfoSize
)
/*++
Routine Description:
This functions is called by the fax service to
get routing configuration data.
Arguments:
RoutingGuid - Unique identifier for the requested routing method
DeviceId - Device that is being configured
RoutingInfo - Routing info buffer
RoutingInfoSize - Size of the buffer
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
RoutingInfo = NULL;
RoutingInfoSize = 0;
return TRUE;
}
BOOL WINAPI
FaxRouteSetRoutingInfo(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LPBYTE RoutingInfo,
IN DWORD RoutingInfoSize
)
/*++
Routine Description:
This functions is called by the fax service to
set routing configuration data.
Arguments:
RoutingGuid - Unique identifier for the requested routing method
DeviceId - Device that is being configured
RoutingInfo - Routing info buffer
RoutingInfoSize - Size of the buffer
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
return TRUE;
}
BOOL WINAPI
FaxRouteConfigure(
OUT HPROPSHEETPAGE *PropSheetPage
)
/*++
Routine Description:
This functions is called by the fax service to
get a property sheet for user configuration of
the routing extension.
Arguments:
PropSheetPage - Handle to a property sheet
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
return TRUE;
}
BOOL WINAPI
FaxRouteItg(
PFAX_ROUTE FaxRoute,
PVOID *FailureData,
LPDWORD FailureDataSize
)
/*++
Routine Description:
This functions is called by the fax service to
route a received fax.
Arguments:
FaxRoute - Routing information
FailureData - Failure data buffer
FailureDataSize - Size of failure data buffer
Return Value:
TRUE for success, otherwise FALSE.
--*/
{
DWORD Size;
WCHAR TiffFileName[MAX_PATH];
LPWSTR SrcFName = NULL;
WCHAR DstFName[MAX_PATH];
LPTSTR FBaseName;
WCHAR FName[_MAX_FNAME];
WCHAR Ext[_MAX_EXT];
Size = sizeof(TiffFileName) / sizeof(WCHAR);
if (!FaxRouteGetFile(
FaxRoute->JobId,
0,
TiffFileName,
&Size))
{
return FALSE;
}
Size = GetFullPathName(
TiffFileName,
0,
SrcFName,
&FBaseName
);
SrcFName = (LPWSTR) MemAlloc( (Size + 1) * sizeof(WCHAR) );
if (!SrcFName) {
return FALSE;
}
GetFullPathName(
TiffFileName,
Size,
SrcFName,
&FBaseName
);
_wsplitpath( SrcFName, NULL, NULL, FName, Ext );
wcscpy( DstFName, FileQueueDir );
wcscat( DstFName, L"\\" );
wcscat( DstFName, FName );
wcscat( DstFName, Ext );
if (!CopyFile( SrcFName, DstFName, TRUE )) {
MemFree( SrcFName );
return FALSE;
}
MemFree( SrcFName );
return TRUE;
}
BOOL WINAPI
FaxRouteDeviceEnable(
IN LPWSTR RoutingGuid,
IN DWORD DeviceId,
IN LONG Enabled
)
{
return TRUE;
}