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.
3106 lines
93 KiB
3106 lines
93 KiB
// Defines the entry point for the DLL application.
|
|
//
|
|
#include "stdafx.h"
|
|
#include <winver.h>
|
|
#include <shlwapi.h>
|
|
#include <mapix.h>
|
|
#include <routemapi.h>
|
|
#include <faxsetup.h>
|
|
#include "Aclapi.h"
|
|
|
|
#define STRSAFE_NO_DEPRECATE
|
|
#include <strsafe.h>
|
|
|
|
|
|
HINSTANCE g_hModule = NULL;
|
|
|
|
BOOL SetDefaultPrinter(LPTSTR pPrinterName);
|
|
DWORD CreateFaxPrinterName(IN LPCTSTR tzPortName, OUT LPTSTR* ptzFaxPrinterName);
|
|
|
|
|
|
BOOL APIENTRY DllMain( HINSTANCE hModule,
|
|
DWORD ul_reason_for_call,
|
|
LPVOID lpReserved
|
|
)
|
|
{
|
|
SET_DEBUG_MASK(DBG_ALL);
|
|
|
|
g_hModule = hModule;
|
|
switch (ul_reason_for_call)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
OPEN_DEBUG_LOG_FILE(SHARED_FAX_SERVICE_SETUP_LOG_FILE);
|
|
DBG_ENTER(TEXT("DllMain called reason DLL_PROCESS_ATTACH."));
|
|
if (!DisableThreadLibraryCalls(hModule))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("DisableThreadLibraryCalls failed (ec=%d)"),
|
|
GetLastError());
|
|
}
|
|
break;
|
|
}
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
DBG_ENTER(TEXT("DllMain called reason DLL_PROCESS_DETACH."));
|
|
CLOSE_DEBUG_LOG_FILE;
|
|
break;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
///////////////////////////////
|
|
// VerifySpoolerIsRunning
|
|
//
|
|
// Start the Spooler service on NT4 & NT5
|
|
//
|
|
// Returns:
|
|
// - NO_ERROR on success
|
|
// - error code otherwise.
|
|
//
|
|
DWORD VerifySpoolerIsRunning()
|
|
{
|
|
OSVERSIONINFO osv;
|
|
BOOL bSuccess = FALSE;
|
|
DWORD dwReturn = NO_ERROR;
|
|
SC_HANDLE hSvcMgr = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
DWORD i = 0;
|
|
SERVICE_STATUS Status;
|
|
LPCTSTR lpctstrSpoolerServiceName = _T("Spooler");
|
|
|
|
DBG_ENTER(_T("VerifySpoolerIsRunning"),dwReturn);
|
|
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
dwReturn = GetLastError();
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetVersionEx failed: (ec=%d)"),
|
|
dwReturn);
|
|
goto exit;
|
|
}
|
|
|
|
// If Windows NT, use WriteProfileString for version 4.0 and earlier...
|
|
if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("W9X OS, Skipping Spooler verification"));
|
|
goto exit;
|
|
}
|
|
|
|
// open the service manager
|
|
hSvcMgr = ::OpenSCManager(NULL,
|
|
NULL,
|
|
SC_MANAGER_CONNECT);
|
|
|
|
if (hSvcMgr == NULL)
|
|
{
|
|
dwReturn = ::GetLastError();
|
|
VERBOSE(SETUP_ERR,
|
|
_T("Failed to open the service manager, rc = 0x%lx"),
|
|
dwReturn);
|
|
goto exit;
|
|
}
|
|
|
|
hService = ::OpenService(hSvcMgr,
|
|
lpctstrSpoolerServiceName,
|
|
SERVICE_QUERY_STATUS|SERVICE_START);
|
|
|
|
if (hService == NULL)
|
|
{
|
|
dwReturn = ::GetLastError();
|
|
VERBOSE(SETUP_ERR,
|
|
_T("Failed to open service '%s', rc = 0x%lx"),
|
|
lpctstrSpoolerServiceName,
|
|
dwReturn);
|
|
goto exit;
|
|
}
|
|
|
|
// Start the fax service.
|
|
bSuccess = StartService(hService, 0, NULL);
|
|
if (!bSuccess)
|
|
{
|
|
dwReturn = ::GetLastError();
|
|
if (dwReturn == ERROR_SERVICE_ALREADY_RUNNING)
|
|
{
|
|
dwReturn = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE(SETUP_ERR,
|
|
_T("Failed to start service '%s', rc = 0x%lx"),
|
|
lpctstrSpoolerServiceName,
|
|
dwReturn);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
QueryServiceStatus(hService, &Status);
|
|
i++;
|
|
|
|
if (Status.dwCurrentState != SERVICE_RUNNING)
|
|
{
|
|
Sleep(1000);
|
|
}
|
|
|
|
} while ((i < 60) && (Status.dwCurrentState != SERVICE_RUNNING));
|
|
|
|
if (Status.dwCurrentState != SERVICE_RUNNING)
|
|
{
|
|
VERBOSE(SETUP_ERR,
|
|
_T("Failed to start '%s' service"),
|
|
lpctstrSpoolerServiceName);
|
|
dwReturn = ERROR_SERVICE_REQUEST_TIMEOUT;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
exit:
|
|
if (hService)
|
|
{
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
if (hSvcMgr)
|
|
{
|
|
CloseServiceHandle(hSvcMgr);
|
|
}
|
|
|
|
return dwReturn;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: ConnectW9XToRemotePrinter
|
|
// Platform: This function intended to run on Win9X platforms
|
|
// Description: Add fax printer connection (driver + printer connection)
|
|
// This function is exported by the DLL for use by the MSI as custom action to add printer connection.
|
|
// In case of failure , returns ERROR_INSTALL_FAILURE
|
|
// In case of success , returns ERROR_SUCCESS
|
|
// GetLastError() to get the error code in case of failure.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args: hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: AsafS
|
|
|
|
DLL_API UINT __stdcall ConnectW9XToRemotePrinter(MSIHANDLE hInstall)
|
|
{
|
|
UINT rc = ERROR_INSTALL_FAILURE;
|
|
DBG_ENTER(TEXT("ConnectW9XToRemotePrinter"), rc);
|
|
|
|
TCHAR szFaxPortName[MAX_PATH] = {0};
|
|
TCHAR szPrinterDriverFolder[MAX_PATH] = {0};
|
|
DWORD dwNeededSize = 0;
|
|
|
|
PRINTER_INFO_2 pi2 = {0};
|
|
DRIVER_INFO_3 di3 = {0};
|
|
HANDLE hPrinter = NULL;
|
|
|
|
if (!GetPrinterDriverDirectory(
|
|
NULL,
|
|
TEXT("Windows 4.0"),
|
|
1,
|
|
(LPBYTE) szPrinterDriverFolder,
|
|
sizeof(szPrinterDriverFolder)/sizeof(TCHAR),
|
|
&dwNeededSize
|
|
))
|
|
{
|
|
VERBOSE (PRINT_ERR,
|
|
TEXT("GetPrinterDriverDirectory failed or not enough space dwNeededSize %ld (ec: %ld)"),
|
|
dwNeededSize,
|
|
GetLastError ());
|
|
goto exit;
|
|
}
|
|
|
|
// Get the remote printer path
|
|
|
|
if (!PrivateMsiGetProperty(hInstall,_T("CustomActionData"),szFaxPortName))
|
|
{
|
|
VERBOSE (SETUP_ERR, _T("PrivateMsiGetProperty failed (ec: %ld)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
if (!FillDriverInfo(&di3,W9X_PRINT_ENV))
|
|
{
|
|
VERBOSE (PRINT_ERR, _T("FillDriverInfo failed (ec: %ld)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
if (!AddPrinterDriver(NULL, 3, (LPBYTE)&di3))
|
|
{
|
|
VERBOSE (PRINT_ERR, _T("AddPrinterDriver failed (ec: %ld)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
pi2.pPortName = szFaxPortName;
|
|
pi2.pDriverName = FAX_DRIVER_NAME;
|
|
pi2.pPrintProcessor = TEXT("WinPrint");
|
|
pi2.pDatatype = TEXT("RAW");
|
|
|
|
rc = CreateFaxPrinterName(szFaxPortName, &(pi2.pPrinterName));
|
|
if (rc != NO_ERROR)
|
|
{
|
|
VERBOSE (PRINT_ERR, _T("CreateFaxPrinterName() is failed, rc= %d. Use default."), rc);
|
|
|
|
pi2.pPrinterName = FAX_PRINTER_NAME;
|
|
}
|
|
|
|
VERBOSE(DBG_MSG, _T("PrinterName is : '%s'."), pi2.pPrinterName);
|
|
|
|
hPrinter = AddPrinter(NULL, 2, (LPBYTE)&pi2);
|
|
|
|
if (!hPrinter)
|
|
{
|
|
rc = GetLastError();
|
|
if (rc==ERROR_PRINTER_ALREADY_EXISTS)
|
|
{
|
|
VERBOSE (DBG_MSG,TEXT("Printer already exists, continue..."));
|
|
rc = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (PRINT_ERR, _T("AddPrinter failed (ec: %ld)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (hPrinter)
|
|
{
|
|
ClosePrinter(hPrinter);
|
|
hPrinter = NULL;
|
|
}
|
|
|
|
rc = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
|
|
if (pi2.pPrinterName)
|
|
{
|
|
MemFree(pi2.pPrinterName);
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (GENERAL_ERR, _T("CustomAction ConnectW9XToRemotePrinter() failed !"));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
// Function: RemoveW9XPrinterConnection
|
|
// Platform: This function intended to run on Win9X platforms
|
|
// Description: Remove the fax printer connection from the current machine.
|
|
|
|
// This function is exported by the DLL for use by the MSI as custom action to delete printer connection.
|
|
// In case of failure , returns ERROR_INSTALL_FAILURE
|
|
// In case of success , returns ERROR_SUCCESS
|
|
// GetLastError() to get the error code in case of failure, the error is of the first error that occured.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: AsafS
|
|
|
|
|
|
DLL_API UINT __stdcall RemoveW9XPrinterConnection(MSIHANDLE hInstall)
|
|
{
|
|
UINT retVal = ERROR_INSTALL_FAILURE;
|
|
PPRINTER_INFO_2 PrinterInfo = NULL;
|
|
DWORD dwCount = 0;
|
|
HANDLE hPrinter = NULL;
|
|
|
|
DBG_ENTER(TEXT("RemoveW9XPrinterConnection"), retVal);
|
|
|
|
PrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters(NULL, 2, &dwCount, 0);
|
|
if (!PrinterInfo)
|
|
{
|
|
VERBOSE(PRINT_ERR, _T("MyEnumPrinters failed : %d."), GetLastError());
|
|
goto error;
|
|
}
|
|
|
|
VERBOSE(DBG_MSG, _T("MyEnumPrinters found %d printers installed."), dwCount);
|
|
|
|
for (DWORD i=0 ; i<dwCount ; i++ )
|
|
{
|
|
if (_tcscmp(PrinterInfo[i].pDriverName, FAX_DRIVER_NAME) == 0)
|
|
{
|
|
VERBOSE(DBG_MSG, _T("Found Fax Printer : %s."), PrinterInfo[i].pPrinterName);
|
|
|
|
if (!OpenPrinter(PrinterInfo[i].pPrinterName, &hPrinter, NULL))
|
|
{
|
|
VERBOSE(PRINT_ERR, _T("OpenPrinter() failed ! (ec: %ld)"), GetLastError());
|
|
continue;
|
|
}
|
|
|
|
if (!DeletePrinter(hPrinter))
|
|
{
|
|
VERBOSE(PRINT_ERR, _T("DeletePrinter() failed ! (ec: %ld)"), GetLastError());
|
|
}
|
|
|
|
if (hPrinter)
|
|
{
|
|
ClosePrinter(hPrinter);
|
|
hPrinter = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE(DBG_MSG, _T("This is not Fax Printer : %s."), PrinterInfo[i].pPrinterName);
|
|
}
|
|
}
|
|
|
|
retVal = ERROR_SUCCESS;
|
|
|
|
error:
|
|
|
|
if (PrinterInfo)
|
|
{
|
|
MemFree( PrinterInfo );
|
|
PrinterInfo = NULL;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
|
|
class CSignalSetupInProgress
|
|
{
|
|
public:
|
|
CSignalSetupInProgress();
|
|
~CSignalSetupInProgress();
|
|
};
|
|
|
|
CSignalSetupInProgress::CSignalSetupInProgress()
|
|
{
|
|
HKEY hFaxKey = NULL;
|
|
|
|
DBG_ENTER(TEXT("CSignalSetupInProgress::CSignalSetupInProgress"));
|
|
|
|
// write 'setup in progress' to the registry, to be used by the point & print
|
|
// mechanism to skip doing a client installation during a setup that is
|
|
// user driven
|
|
hFaxKey = OpenRegistryKey(HKEY_LOCAL_MACHINE,REGKEY_SBS2000_FAX_SETUP,TRUE,KEY_WRITE);
|
|
if (hFaxKey)
|
|
{
|
|
if (!SetRegistryDword(hFaxKey,REGVAL_SETUP_IN_PROGRESS,1))
|
|
{
|
|
VERBOSE(GENERAL_ERR,_T("SetRegistryDword failed: (ec=%d)"),GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE(GENERAL_ERR,_T("OpenRegistryKey failed: (ec=%d)"),GetLastError());
|
|
}
|
|
|
|
if (hFaxKey)
|
|
{
|
|
RegCloseKey(hFaxKey);
|
|
}
|
|
}
|
|
|
|
CSignalSetupInProgress::~CSignalSetupInProgress()
|
|
{
|
|
HKEY hFaxKey = NULL;
|
|
|
|
DBG_ENTER(TEXT("CSignalSetupInProgress::~CSignalSetupInProgress"));
|
|
|
|
hFaxKey = OpenRegistryKey(HKEY_LOCAL_MACHINE,REGKEY_SBS2000_FAX_SETUP,FALSE,KEY_WRITE);
|
|
if (hFaxKey)
|
|
{
|
|
if (RegDeleteValue(hFaxKey,REGVAL_SETUP_IN_PROGRESS)!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(GENERAL_ERR, TEXT("RegDeleteValue failed with %ld"), GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE(DBG_MSG, TEXT("down leve client setup is not in progress"));
|
|
}
|
|
|
|
if (hFaxKey)
|
|
{
|
|
RegCloseKey(hFaxKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: AddFaxPrinterConnection
|
|
// Platform: This function intended to run on NT platforms (NT4 and Win2K)
|
|
// Description: Add fax printer connection
|
|
// This function is exported by the DLL for use by the MSI as custom action to add printer connection.
|
|
// In case of failure , returns ERROR_INSTALL_FAILURE
|
|
// In case of success , returns ERROR_SUCCESS
|
|
// GetLastError() to get the error code in case of failure.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args: hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: AsafS
|
|
|
|
|
|
DLL_API UINT __stdcall AddFaxPrinterConnection(MSIHANDLE hInstall)
|
|
{
|
|
UINT rc = ERROR_SUCCESS;
|
|
DBG_ENTER(TEXT("AddFaxPrinterConnection"), rc);
|
|
|
|
BOOL fFaxPrinterConnectionAdded = FALSE;
|
|
|
|
TCHAR szFaxPrinterName[MAX_PATH] = {0};
|
|
|
|
if (!PrivateMsiGetProperty(hInstall,_T("PRINTER_NAME"),szFaxPrinterName))
|
|
{
|
|
VERBOSE (SETUP_ERR,
|
|
TEXT("PrivateMsiGetProperty() failed ! (ec: %ld)"),
|
|
GetLastError ());
|
|
goto error;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////
|
|
// Add the printer connection on client //
|
|
//////////////////////////////////////////
|
|
|
|
{
|
|
CSignalSetupInProgress SignalSetupInProgress;
|
|
fFaxPrinterConnectionAdded = AddPrinterConnection(szFaxPrinterName);
|
|
if (!fFaxPrinterConnectionAdded)
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
VERBOSE (PRINT_ERR,
|
|
TEXT("AddPrinterConnection() failed ! (ec: %ld)"),
|
|
dwLastError);
|
|
|
|
goto error;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("Successfully added fax printer connection to %s"),
|
|
szFaxPrinterName);
|
|
}
|
|
}
|
|
|
|
|
|
if (!SetDefaultPrinter(szFaxPrinterName))
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
VERBOSE (PRINT_ERR,
|
|
TEXT("SetDefaultPrinter() failed ! (ec: %ld)"),
|
|
dwLastError);
|
|
goto error;
|
|
}
|
|
|
|
return rc;
|
|
|
|
error:
|
|
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("CustomAction AddFaxPrinterConnection() failed !"));
|
|
rc = ERROR_INSTALL_FAILURE;
|
|
return rc;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: RemoveFaxPrinterConnection
|
|
// Platform: This function intended to run on NT platforms (NT4 and Win2K)
|
|
// Description: Remove the fax printer connection from the current machine.
|
|
// This function is exported by the DLL for use by the MSI as custom action to delete printer connection.
|
|
// In case of failure , returns ERROR_INSTALL_FAILURE
|
|
// In case of success , returns ERROR_SUCCESS
|
|
// GetLastError() to get the error code in case of failure, the error is of the first error that occured.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: AsafS
|
|
|
|
|
|
DLL_API UINT __stdcall RemoveFaxPrinterConnection(MSIHANDLE hInstall)
|
|
{
|
|
PPRINTER_INFO_2 pPrinterInfo = NULL;
|
|
DWORD dwNumPrinters = 0;
|
|
DWORD dwPrinter = 0;
|
|
DWORD ec = ERROR_SUCCESS;
|
|
|
|
DBG_ENTER(TEXT("RemoveFaxPrinterConnection"), ec);
|
|
|
|
pPrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters(NULL,
|
|
2,
|
|
&dwNumPrinters,
|
|
PRINTER_ENUM_CONNECTIONS
|
|
);
|
|
if (!pPrinterInfo)
|
|
{
|
|
ec = GetLastError();
|
|
if (ERROR_SUCCESS == ec)
|
|
{
|
|
ec = ERROR_PRINTER_NOT_FOUND;
|
|
}
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MyEnumPrinters() failed (ec: %ld)"),
|
|
ec);
|
|
goto error;
|
|
}
|
|
|
|
for (dwPrinter=0; dwPrinter < dwNumPrinters; dwPrinter++)
|
|
{
|
|
if (IsPrinterFaxPrinter(pPrinterInfo[dwPrinter].pPrinterName))
|
|
{
|
|
if (!DeletePrinterConnection(pPrinterInfo[dwPrinter].pPrinterName))
|
|
{
|
|
VERBOSE (PRINT_ERR,
|
|
TEXT("DeletePrinterConnection() %s failed ! (ec: %ld)"),
|
|
pPrinterInfo[dwPrinter].pPrinterName,
|
|
GetLastError ());
|
|
goto error;
|
|
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("fax printer connection %s was deleted successfully"),
|
|
pPrinterInfo[dwPrinter].pPrinterName);
|
|
}
|
|
}
|
|
}
|
|
|
|
error:
|
|
|
|
if (pPrinterInfo)
|
|
{
|
|
MemFree(pPrinterInfo);
|
|
}
|
|
|
|
if (ec!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("CustomAction RemoveFaxPrinterConnection() failed !"));
|
|
}
|
|
return ec;
|
|
}
|
|
|
|
|
|
#define FXSEXTENSION _T("FXSEXT32.DLL")
|
|
|
|
//
|
|
//
|
|
// Function: Create_FXSEXT_ECF_File
|
|
// Description: Creates FxsExt.ecf in <WindowsFolder>\addins
|
|
// a default file will be installed there by Windows Installer
|
|
// to enable it to keep track of install/remove
|
|
// GetLastError() to get the error code in case of failure, the error is of the first error that occured.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall Create_FXSEXT_ECF_File(MSIHANDLE hInstall)
|
|
{
|
|
// CustomActionData has the following format <WindowsFolder>;<INSTALLDIR>
|
|
TCHAR szCustomActionData[2*MAX_PATH] = {0};
|
|
TCHAR szWindowsFolder[2*MAX_PATH] = {0};
|
|
TCHAR szExtensionPath[MAX_PATH] = {0};
|
|
TCHAR* tpInstallDir = NULL;
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
|
|
DBG_ENTER(_T("Create_FXSEXT_ECF_File"));
|
|
|
|
// get the custom action data from Windows Installer (deffered action)
|
|
if (!PrivateMsiGetProperty(hInstall,_T("CustomActionData"),szCustomActionData))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("PrivateMsiGetProperty:CustomActionData failed (ec: %ld)."),
|
|
uiRet);
|
|
goto error;
|
|
}
|
|
|
|
if (_tcstok(szCustomActionData,_T(";"))==NULL)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("_tcstok failed on first token."));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto error;
|
|
}
|
|
|
|
if ((tpInstallDir=_tcstok(NULL,_T(";\0")))==NULL)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("_tcstok failed on second token."));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto error;
|
|
}
|
|
_tcscpy(szWindowsFolder,szCustomActionData);
|
|
|
|
// construct the full path to the file
|
|
if (_tcslen(szWindowsFolder)+_tcslen(ADDINS_DIRECTORY)+_tcslen(FXSEXT_ECF_FILE)>=MAX_PATH)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("Path to <WindowsFolder>\\Addins\\fxsext.ecf is too long"));
|
|
goto error;
|
|
}
|
|
_tcscat(szWindowsFolder,ADDINS_DIRECTORY);
|
|
_tcscat(szWindowsFolder,FXSEXT_ECF_FILE);
|
|
|
|
VERBOSE (DBG_MSG,
|
|
_T("Filename to create is: %s."),
|
|
szWindowsFolder);
|
|
|
|
if (_tcslen(tpInstallDir)+_tcslen(FXSEXTENSION)+2>=MAX_PATH)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("Path to <INSTALLDIR>\\Bin\\fxsext32.dll is too long"));
|
|
goto error;
|
|
}
|
|
|
|
_tcscpy(szExtensionPath,_T("\""));
|
|
_tcscat(szExtensionPath,tpInstallDir);
|
|
_tcscat(szExtensionPath,FXSEXTENSION);
|
|
_tcscat(szExtensionPath,_T("\""));
|
|
|
|
VERBOSE (DBG_MSG,
|
|
_T("MAPI Extension dll path dir is: %s."),
|
|
szExtensionPath);
|
|
|
|
if (!WritePrivateProfileString( _T("General"),
|
|
_T("Path"),
|
|
szExtensionPath,
|
|
szWindowsFolder))
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("WritePrivateProfileString failed (ec: %ld)."),
|
|
uiRet);
|
|
goto error;
|
|
}
|
|
|
|
Assert(uiRet==ERROR_SUCCESS);
|
|
return uiRet;
|
|
|
|
error:
|
|
|
|
Assert(uiRet!=ERROR_SUCCESS);
|
|
return uiRet;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: ValidatePrinter
|
|
// Description: Validates that the printer name which was entered is a legitimate
|
|
// Fax Printer, and that the server is available.
|
|
// Uses the MSI Property 'ValidPrinterFormat' to notify MSI if the
|
|
// name is valid or not.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall ValidatePrinter(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szPrinterName[MAX_PATH] = {0};
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
HANDLE hPrinterHandle = INVALID_HANDLE_VALUE;
|
|
BOOL bValidPrinter = TRUE;
|
|
DBG_ENTER(_T("ValidatePrinter"));
|
|
|
|
// first get the PRINTER_NAME proterty from Windows Installer
|
|
if (!PrivateMsiGetProperty(hInstall,_T("PRINTER_NAME"),szPrinterName))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("PrivateMsiGetProperty:PRINTER_NAME failed (ec: %ld)."),
|
|
uiRet);
|
|
goto error;
|
|
}
|
|
|
|
if (VerifySpoolerIsRunning()!=NO_ERROR)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("VerifySpoolerIsRunning (ec:%d)"),
|
|
uiRet);
|
|
goto error;
|
|
}
|
|
|
|
// we have a string with the PRINTER_NAME, let's try to open it...
|
|
if (bValidPrinter=IsPrinterFaxPrinter(szPrinterName))
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
_T("IsPrinterFaxPrinter: %s succeeded."),
|
|
szPrinterName);
|
|
}
|
|
else
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("IsPrinterFaxPrinter: %s failed (ec: %ld)."),
|
|
szPrinterName,
|
|
uiRet);
|
|
}
|
|
|
|
|
|
uiRet = MsiSetProperty( hInstall,
|
|
_T("ValidPrinterFormat"),
|
|
bValidPrinter ? _T("TRUE") : _T("FALSE"));
|
|
if (uiRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("MsiSetProperty failed."));
|
|
goto error;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
error:
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: GuessPrinterName
|
|
// Description: Tries to understand whether the installation is performed from the
|
|
// server's FaxClients share, and if it is tries to establish a default
|
|
// printer to be used
|
|
//
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall GuessPrinterName(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
TCHAR szSourceDir[MAX_PATH] = {0};
|
|
TCHAR szPrinterName[MAX_PATH] = {0};
|
|
TCHAR* tpClientShare = NULL;
|
|
PPRINTER_INFO_2 pPrinterInfo = NULL;
|
|
DWORD dwNumPrinters = 0;
|
|
DWORD dwPrinter = 0;
|
|
|
|
DBG_ENTER(_T("GuessPrinterName"),uiRet);
|
|
|
|
// get the source directory from Windows Installer
|
|
if (!PrivateMsiGetProperty(hInstall,_T("SourceDir"),szSourceDir))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("PrivateMsiGetProperty:SourceDir failed (ec: %ld)."),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
// check if we have a UNC path
|
|
if (_tcsncmp(szSourceDir,_T("\\\\"),2))
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
_T("SourceDir doesn't start with \\\\"));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
// find drive name (skip server name)
|
|
if ((tpClientShare=_tcschr(_tcsninc(szSourceDir,2),_T('\\')))==NULL)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("_tcschr failed"));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
if (VerifySpoolerIsRunning()!=NO_ERROR)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("VerifySpoolerIsRunning (ec:%d)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
// extract the server's name
|
|
*tpClientShare = 0;
|
|
// szSourceDir now holds the server's name
|
|
// enumerate the printers on the server
|
|
pPrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters(szSourceDir,
|
|
2,
|
|
&dwNumPrinters,
|
|
PRINTER_ENUM_NAME
|
|
);
|
|
|
|
if (!pPrinterInfo)
|
|
{
|
|
uiRet = GetLastError();
|
|
if (uiRet == ERROR_SUCCESS)
|
|
{
|
|
uiRet = ERROR_PRINTER_NOT_FOUND;
|
|
}
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MyEnumPrinters() failed (ec: %ld)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
for (dwPrinter=0; dwPrinter < dwNumPrinters; dwPrinter++)
|
|
{
|
|
// check if we have a valid fax printer driver name
|
|
if (_tcscmp(pPrinterInfo[dwPrinter].pDriverName,FAX_DRIVER_NAME ) == 0)
|
|
{
|
|
if ( (pPrinterInfo[dwPrinter].pServerName==NULL) ||
|
|
(_tcslen(pPrinterInfo[dwPrinter].pServerName)==0) ||
|
|
(pPrinterInfo[dwPrinter].pShareName==NULL) ||
|
|
(_tcslen(pPrinterInfo[dwPrinter].pShareName)==0) )
|
|
{
|
|
// on win9x the printer name lives in the Port name field
|
|
_tcscpy(szPrinterName,pPrinterInfo[dwPrinter].pPortName);
|
|
}
|
|
else
|
|
{
|
|
_tcscpy(szPrinterName,pPrinterInfo[dwPrinter].pServerName);
|
|
_tcscat(szPrinterName,_T("\\"));
|
|
_tcscat(szPrinterName,pPrinterInfo[dwPrinter].pShareName);
|
|
}
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("Setting PRINTER_NAME to %s."),
|
|
szPrinterName);
|
|
// set property to Installer
|
|
uiRet = MsiSetProperty(hInstall,_T("PRINTER_NAME"),szPrinterName);
|
|
if (uiRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MsiSetProperty failed."));
|
|
goto exit;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("%s is not a Fax printer - driver name is %s."),
|
|
pPrinterInfo[dwPrinter].pPrinterName,
|
|
pPrinterInfo[dwPrinter].pDriverName);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
if (pPrinterInfo)
|
|
{
|
|
MemFree(pPrinterInfo);
|
|
}
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: Remove_FXSEXT_ECF_File
|
|
// Description: Removes FxsExt.ecf from <WindowsFolder>\addins
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall Remove_FXSEXT_ECF_File(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szWindowsFolder[MAX_PATH] = {0};
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
|
|
DBG_ENTER(_T("Remove_FXSEXT_ECF_File"));
|
|
|
|
|
|
// check if the service is installed on this machine
|
|
INSTALLSTATE currentInstallState = MsiQueryProductState(PRODCODE_SBS5_SERVER);
|
|
|
|
if (currentInstallState != INSTALLSTATE_UNKNOWN)
|
|
{
|
|
VERBOSE (DBG_MSG, _T("The Microsoft Shared Fax Service is installed. Returning without removing file."));
|
|
return uiRet;
|
|
}
|
|
|
|
// get the <WindowsFolder> from Windows Installer
|
|
if (!PrivateMsiGetProperty(hInstall,_T("WindowsFolder"),szWindowsFolder))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("PrivateMsiGetProperty:WindowsFolder failed (ec: %ld)."),
|
|
uiRet);
|
|
goto error;
|
|
}
|
|
|
|
// construct the full path to the file
|
|
if (_tcslen(szWindowsFolder)+_tcslen(ADDINS_DIRECTORY)+_tcslen(FXSEXT_ECF_FILE)>=MAX_PATH)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("Path to <WindowsFolder>\\Addins\\fxsext.ecf is too long"));
|
|
goto error;
|
|
}
|
|
_tcscat(szWindowsFolder,ADDINS_DIRECTORY);
|
|
_tcscat(szWindowsFolder,FXSEXT_ECF_FILE);
|
|
|
|
VERBOSE (DBG_MSG,
|
|
_T("Filename to delete is: %s."),
|
|
szWindowsFolder);
|
|
|
|
if (DeleteFile(szWindowsFolder))
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
_T("File %s was deleted successfully."),
|
|
szWindowsFolder);
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
_T("DeleteFile %s failed (ec=%d)."),
|
|
szWindowsFolder,
|
|
GetLastError());
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
error:
|
|
|
|
return ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: RemoveTrasportProviderFromProfile
|
|
// Description: removes the Trasnport Provider from a MAPI profile
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
HRESULT RemoveTrasportProviderFromProfile(LPSERVICEADMIN lpServiceAdmin)
|
|
{
|
|
static SRestriction sres;
|
|
static SizedSPropTagArray(2, Columns) = {2,{PR_DISPLAY_NAME_A,PR_SERVICE_UID}};
|
|
|
|
HRESULT hr = S_OK;
|
|
LPMAPITABLE lpMapiTable = NULL;
|
|
LPSRowSet lpSRowSet = NULL;
|
|
LPSPropValue lpProp = NULL;
|
|
ULONG Count = 0;
|
|
BOOL bMapiInitialized = FALSE;
|
|
SPropValue spv;
|
|
MAPIUID ServiceUID;
|
|
|
|
DBG_ENTER(TEXT("RemoveTrasportProviderFromProfile"), hr);
|
|
// get message service table
|
|
hr = lpServiceAdmin->GetMsgServiceTable(0,&lpMapiTable);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("GetMsgServiceTable failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
// notify MAPI that we want PR_DISPLAY_NAME_A & PR_SERVICE_UID
|
|
hr = lpMapiTable->SetColumns((LPSPropTagArray)&Columns, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("SetColumns failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
// restrict the search to our service provider
|
|
sres.rt = RES_PROPERTY;
|
|
sres.res.resProperty.relop = RELOP_EQ;
|
|
sres.res.resProperty.ulPropTag = PR_SERVICE_NAME_A;
|
|
sres.res.resProperty.lpProp = &spv;
|
|
|
|
spv.ulPropTag = PR_SERVICE_NAME_A;
|
|
spv.Value.lpszA = FAX_MESSAGE_SERVICE_NAME_SBS50;
|
|
|
|
// find it
|
|
hr = lpMapiTable->FindRow(&sres, BOOKMARK_BEGINNING, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("FindRow failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
// get our service provider's row
|
|
hr = lpMapiTable->QueryRows(1, 0, &lpSRowSet);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("QueryRows failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
if (lpSRowSet->cRows != 1)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("QueryRows returned %d rows, there should be only one."),
|
|
lpSRowSet->cRows);
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
goto exit;
|
|
}
|
|
|
|
// get the MAPIUID of our service
|
|
lpProp = &lpSRowSet->aRow[0].lpProps[1];
|
|
|
|
if (lpProp->ulPropTag != PR_SERVICE_UID)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("Property is %d, should be PR_SERVICE_UID."),
|
|
lpProp->ulPropTag);
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
goto exit;
|
|
}
|
|
|
|
// Copy the UID into our member.
|
|
memcpy(&ServiceUID.ab, lpProp->Value.bin.lpb,lpProp->Value.bin.cb);
|
|
|
|
// finally, delete our service provider
|
|
hr = lpServiceAdmin->DeleteMsgService(&ServiceUID);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("DeleteMsgService failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: RemoveTrasportProvider
|
|
// Description: delete FXSXP32.DLL from mapisvc.inf
|
|
// and removes the Trasnport Provider from MAPI
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
|
|
DLL_API UINT __stdcall RemoveTrasportProvider(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szMapisvcFile[2 * MAX_PATH] = {0};
|
|
DWORD err = 0;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
HRESULT hr = S_OK;
|
|
LPSERVICEADMIN lpServiceAdmin = NULL;
|
|
LPMAPITABLE lpMapiTable = NULL;
|
|
LPPROFADMIN lpProfAdmin = NULL;
|
|
LPMAPITABLE lpTable = NULL;
|
|
LPSRowSet lpSRowSet = NULL;
|
|
LPSPropValue lpProp = NULL;
|
|
ULONG Count = 0;
|
|
int iIndex = 0;
|
|
BOOL bMapiInitialized = FALSE;
|
|
HINSTANCE hMapiDll = NULL;
|
|
|
|
LPMAPIINITIALIZE fnMapiInitialize = NULL;
|
|
LPMAPIADMINPROFILES fnMapiAdminProfiles = NULL;
|
|
LPMAPIUNINITIALIZE fnMapiUninitialize = NULL;
|
|
|
|
DBG_ENTER(TEXT("RemoveTrasportProvider"), rc);
|
|
|
|
CRouteMAPICalls rmcRouteMapiCalls;
|
|
|
|
// first remove ourselves from MAPISVC.INF
|
|
if(!GetSystemDirectory(szMapisvcFile, sizeof(szMapisvcFile)/sizeof(TCHAR)))
|
|
{
|
|
rc = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("GetSystemDirectory failed (ec: %ld)."),
|
|
rc);
|
|
goto exit;
|
|
}
|
|
_tcscat(szMapisvcFile, TEXT("\\mapisvc.inf"));
|
|
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("The mapi file is %s."),
|
|
szMapisvcFile);
|
|
|
|
if (!WritePrivateProfileString( TEXT("Default Services"),
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
NULL,
|
|
szMapisvcFile
|
|
))
|
|
{
|
|
rc = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("WritePrivateProfileString failed (ec: %ld)."),
|
|
rc);
|
|
goto exit;
|
|
}
|
|
|
|
if (!WritePrivateProfileString( TEXT("Services"),
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
NULL,
|
|
szMapisvcFile
|
|
))
|
|
{
|
|
rc = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("WritePrivateProfileString failed (ec: %ld)."),
|
|
rc);
|
|
goto exit;
|
|
}
|
|
|
|
if (!WritePrivateProfileString( FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
NULL,
|
|
NULL,
|
|
szMapisvcFile
|
|
))
|
|
{
|
|
rc = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("WritePrivateProfileString failed (ec: %ld)."),
|
|
rc);
|
|
goto exit;
|
|
}
|
|
|
|
if (!WritePrivateProfileString( FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
NULL,
|
|
NULL,
|
|
szMapisvcFile
|
|
))
|
|
{
|
|
rc = GetLastError();
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("WritePrivateProfileString failed (ec: %ld)."),
|
|
rc);
|
|
goto exit;
|
|
}
|
|
|
|
// now remove the MAPI Service provider
|
|
rc = rmcRouteMapiCalls.Init(_T("msiexec.exe"));
|
|
if (rc!=ERROR_SUCCESS)
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("CRouteMAPICalls::Init failed (ec: %ld)."), rc);
|
|
goto exit;
|
|
}
|
|
|
|
hMapiDll = LoadLibrary(_T("MAPI32.DLL"));
|
|
if (NULL == hMapiDll)
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("LoadLibrary"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
fnMapiInitialize = (LPMAPIINITIALIZE)GetProcAddress(hMapiDll, "MAPIInitialize");
|
|
if (NULL == fnMapiInitialize)
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("GetProcAddress(MAPIInitialize)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
fnMapiAdminProfiles = (LPMAPIADMINPROFILES)GetProcAddress(hMapiDll, "MAPIAdminProfiles");
|
|
if (NULL == fnMapiAdminProfiles)
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("GetProcAddress(fnMapiAdminProfiles)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
fnMapiUninitialize = (LPMAPIUNINITIALIZE)GetProcAddress(hMapiDll, "MAPIUninitialize");
|
|
if (NULL == fnMapiUninitialize)
|
|
{
|
|
CALL_FAIL(GENERAL_ERR, TEXT("GetProcAddress(MAPIUninitialize)"), GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
// get access to MAPI functinality
|
|
hr = fnMapiInitialize(NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MAPIInitialize failed (ec: %ld)."),
|
|
rc = hr);
|
|
goto exit;
|
|
}
|
|
|
|
bMapiInitialized = TRUE;
|
|
|
|
// get admin profile object
|
|
hr = fnMapiAdminProfiles(0,&lpProfAdmin);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MAPIAdminProfiles failed (ec: %ld)."),
|
|
rc = hr);
|
|
goto exit;
|
|
}
|
|
|
|
// get profile table
|
|
hr = lpProfAdmin->GetProfileTable(0,&lpTable);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("GetProfileTable failed (ec: %ld)."),
|
|
rc = hr);
|
|
goto exit;
|
|
}
|
|
|
|
// get profile rows
|
|
hr = lpTable->QueryRows(4000, 0, &lpSRowSet);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("QueryRows failed (ec: %ld)."),
|
|
hr);
|
|
goto exit;
|
|
}
|
|
|
|
for (iIndex=0; iIndex<(int)lpSRowSet->cRows; iIndex++)
|
|
{
|
|
lpProp = &lpSRowSet->aRow[iIndex].lpProps[0];
|
|
|
|
if (lpProp->ulPropTag != PR_DISPLAY_NAME_A)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("Property is %d, should be PR_DISPLAY_NAME_A."),
|
|
lpProp->ulPropTag);
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_TABLE);
|
|
goto exit;
|
|
}
|
|
|
|
hr = lpProfAdmin->AdminServices(LPTSTR(lpProp->Value.lpszA),NULL,0,0,&lpServiceAdmin);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("AdminServices failed (ec: %ld)."),
|
|
rc = hr);
|
|
goto exit;
|
|
}
|
|
|
|
hr = RemoveTrasportProviderFromProfile(lpServiceAdmin);
|
|
if (FAILED(hr))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("RemoveTrasportProviderFromProfile failed (ec: %ld)."),
|
|
rc = hr);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
if (bMapiInitialized)
|
|
{
|
|
fnMapiUninitialize();
|
|
}
|
|
|
|
if (hMapiDll)
|
|
{
|
|
FreeLibrary(hMapiDll);
|
|
hMapiDll = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
// Function: AddOutlookExtension
|
|
// Description: Add fax as outlook provider. Write into the MAPI file: 'mapisvc.inf'
|
|
// This function is exported by the DLL for use by the MSI as custom action.
|
|
// In case of failure , returns ERROR_INSTALL_FAILURE
|
|
// In case of success , returns ERROR_SUCCESS
|
|
// GetLastError() to get the error code in case of failure, the error is of the first error that occured.
|
|
//
|
|
// Remarks:
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: AsafS
|
|
|
|
|
|
DLL_API UINT __stdcall AddOutlookExtension(MSIHANDLE hInstall)
|
|
{
|
|
TCHAR szMapisvcFile[2 * MAX_PATH] = {0};
|
|
TCHAR szDisplayName[MAX_PATH] = {0};
|
|
|
|
DWORD err = 0;
|
|
DWORD rc = ERROR_SUCCESS;
|
|
DBG_ENTER(TEXT("AddOutlookExtension"), rc);
|
|
|
|
|
|
if(!GetSystemDirectory(szMapisvcFile, sizeof(szMapisvcFile)/sizeof(TCHAR)))
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("GetSystemDirectory failed (ec: %ld)."),
|
|
GetLastError ());
|
|
goto error;
|
|
}
|
|
_tcscat(szMapisvcFile, TEXT("\\mapisvc.inf"));
|
|
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("The mapi file is %s."),
|
|
szMapisvcFile);
|
|
if (!LoadString(
|
|
g_hModule,
|
|
IDS_FAXXP_DISPLAY_NAME,
|
|
szDisplayName,
|
|
sizeof(szDisplayName)/sizeof(TCHAR)
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
TEXT("Default Services"),
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
szDisplayName,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
TEXT("Services"),
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
szDisplayName,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("PR_DISPLAY_NAME"),
|
|
szDisplayName,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("Providers"),
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("PR_SERVICE_DLL_NAME"),
|
|
FAX_MESSAGE_TRANSPORT_IMAGE_NAME_T,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("PR_SERVICE_SUPPORT_FILES"),
|
|
FAX_MESSAGE_TRANSPORT_IMAGE_NAME_T,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("PR_SERVICE_ENTRY_NAME"),
|
|
TEXT("ServiceEntry"),
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_SERVICE_NAME_SBS50_T,
|
|
TEXT("PR_RESOURCE_FLAGS"),
|
|
TEXT("SERVICE_SINGLE_COPY|SERVICE_NO_PRIMARY_IDENTITY"),
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
TEXT("PR_PROVIDER_DLL_NAME"),
|
|
FAX_MESSAGE_TRANSPORT_IMAGE_NAME_T,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
TEXT("PR_RESOURCE_TYPE"),
|
|
TEXT("MAPI_TRANSPORT_PROVIDER"),
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
TEXT("PR_RESOURCE_FLAGS"),
|
|
TEXT("STATUS_NO_DEFAULT_STORE"),
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
TEXT("PR_DISPLAY_NAME"),
|
|
szDisplayName,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
if (!WritePrivateProfileString(
|
|
FAX_MESSAGE_PROVIDER_NAME_SBS50_T,
|
|
TEXT("PR_PROVIDER_DISPLAY"),
|
|
szDisplayName,
|
|
szMapisvcFile
|
|
)) goto error;
|
|
err++;
|
|
|
|
return rc;
|
|
|
|
error:
|
|
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("CustomAction AddOutlookExtension() failed ! (ec: %ld) (err = %ld)"),
|
|
GetLastError(),
|
|
err
|
|
);
|
|
rc = ERROR_INSTALL_FAILURE;
|
|
return rc;
|
|
}
|
|
|
|
#define COMCTL32_401 PACKVERSION (4,72)
|
|
|
|
DLL_API UINT __stdcall IsComctlRequiresUpdate(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
BOOL bRes = FALSE;
|
|
DWORD dwVer = 0;
|
|
|
|
DBG_ENTER(TEXT("IsComctlRequiresUpdate"), uiRet);
|
|
|
|
dwVer = GetDllVersion(TEXT("comctl32.dll"));
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("Current COMCTL32 version is 0x%08X."),
|
|
dwVer);
|
|
|
|
if (COMCTL32_401 > dwVer)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("COMCTL32.DLL requires update."));
|
|
bRes = TRUE;
|
|
}
|
|
|
|
uiRet = MsiSetProperty( hInstall,
|
|
_T("IsComctlRequiresUpdate"),
|
|
bRes ? _T("TRUE") : _T("FALSE"));
|
|
if (uiRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("MsiSetProperty IsComctlRequiresUpdate failed."));
|
|
}
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
typedef struct _TypeCommand
|
|
{
|
|
LPCTSTR lpctstrType;
|
|
LPCTSTR lpctstrFolder;
|
|
LPCTSTR lpctstrCommand;
|
|
} TypeCommand;
|
|
|
|
static TypeCommand tcWin9XCommand[] =
|
|
{
|
|
// Win9X PrintTo verbs
|
|
{ _T("txtfile"), _T("WindowsFolder"), _T("write.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
{ _T("jpegfile"), _T("WindowsFolder"), _T("pbrush.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
};
|
|
|
|
static TypeCommand tcWinMECommand[] =
|
|
{
|
|
// WinME PrintTo verbs
|
|
{ _T("txtfile"), _T("WindowsFolder"), _T("write.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
{ _T("jpegfile"), _T("WindowsFolder"), _T("pbrush.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
{ _T("giffile"), _T("WindowsFolder"), _T("pbrush.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
{ _T("Paint.Picture"), _T("WindowsFolder"), _T("pbrush.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
};
|
|
|
|
static TypeCommand tcWin2KCommand[] =
|
|
{
|
|
// NT4 PrintTo verbs
|
|
{ _T("txtfile"), _T("SystemFolder"), _T("write.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
{ _T("jpegfile"), _T("SystemFolder"), _T("mspaint.exe /pt \"%1\" \"%2\" \"%3\" \"%4") },
|
|
};
|
|
|
|
static int iCountWin9XCommands = sizeof(tcWin9XCommand)/sizeof(tcWin9XCommand[0]);
|
|
static int iCountWinMECommands = sizeof(tcWinMECommand)/sizeof(tcWinMECommand[0]);
|
|
static int iCountWin2KCommands = sizeof(tcWin2KCommand)/sizeof(tcWin2KCommand[0]);
|
|
|
|
//
|
|
//
|
|
// Function: CrearePrintToVerb
|
|
//
|
|
// Description: Creates the PrintTo verb for text files to associate it with wordpad
|
|
// if the PrintTo verb already exists, this function does nothing.
|
|
//
|
|
// Remarks:
|
|
// on Win9x
|
|
// txtfile - PrintTo = <WindowsFolder>\write.exe /pt "%1" "%2" "%3" "%4"
|
|
// jpegfile - PrintTo = <WindowsFolder>\pbrush.exe /pt "%1" "%2" "%3" "%4"
|
|
//
|
|
// on WinME
|
|
// txtfile - PrintTo = <WindowsFolder>\write.exe /pt "%1" "%2" "%3" "%4"
|
|
// jpegfile - PrintTo = <WindowsFolder>\pbrush.exe /pt "%1" "%2" "%3" "%4"
|
|
// giffile - PrintTo = <WindowsFolder>\pbrush.exe /pt "%1" "%2" "%3" "%4"
|
|
// Paint.Picture - PrintTo = <WindowsFolder>\pbrush.exe /pt "%1" "%2" "%3" "%4"
|
|
//
|
|
// on NT4
|
|
// txtfile - PrintTo = <SystemFolder>\write.exe /pt "%1" "%2" "%3" "%4"
|
|
// jpegfile - PrintTo = <SystemFolder>\mspaint.exe /pt "%1" "%2" "%3" "%4"
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall CreatePrintToVerb(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
LPCTSTR lpctstrPrintToCommand = _T("\\shell\\printto\\command");
|
|
int iCount = 0;
|
|
DWORD cchValue = MAX_PATH;
|
|
TCHAR szValueBuf[MAX_PATH] = {0};
|
|
TCHAR szKeyBuf[MAX_PATH] = {0};
|
|
BOOL bOverwriteExisting = FALSE;
|
|
LONG rVal = 0;
|
|
HKEY hKey = NULL;
|
|
HKEY hCommandKey = NULL;
|
|
TypeCommand* pTypeCommand = NULL;
|
|
int iCommandCount = 0;
|
|
OSVERSIONINFO osv;
|
|
|
|
DBG_ENTER(TEXT("CreatePrintToVerb"),uiRet);
|
|
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetVersionEx failed: (ec=%d)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (osv.dwPlatformId==VER_PLATFORM_WIN32_NT)
|
|
{
|
|
VERBOSE (DBG_MSG, _T("This is NT4/NT5"));
|
|
pTypeCommand = tcWin2KCommand;
|
|
iCommandCount = iCountWin2KCommands;
|
|
}
|
|
else if (osv.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
|
|
{
|
|
if (osv.dwMinorVersion>=90)
|
|
{
|
|
VERBOSE (DBG_MSG, _T("This is WinME"));
|
|
pTypeCommand = tcWinMECommand;
|
|
iCommandCount = iCountWinMECommands;
|
|
bOverwriteExisting = TRUE;
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (DBG_MSG, _T("This is Win9X"));
|
|
pTypeCommand = tcWin9XCommand;
|
|
iCommandCount = iCountWin9XCommands;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (GENERAL_ERR, _T("This is an illegal OS"));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
for (iCount=0; iCount<iCommandCount; iCount++)
|
|
{
|
|
_tcscpy(szKeyBuf,pTypeCommand[iCount].lpctstrType);
|
|
_tcscat(szKeyBuf,lpctstrPrintToCommand);
|
|
|
|
// go get the appropriate folder from Windows Installer
|
|
if (!PrivateMsiGetProperty( hInstall,
|
|
pTypeCommand[iCount].lpctstrFolder,
|
|
szValueBuf))
|
|
{
|
|
VERBOSE (SETUP_ERR,
|
|
TEXT("PrivateMsiGetProperty failed (ec: %ld)"),
|
|
GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
if (_tcslen(szValueBuf)+_tcslen(pTypeCommand[iCount].lpctstrCommand)>=MAX_PATH-1)
|
|
{
|
|
VERBOSE (SETUP_ERR,
|
|
TEXT("command to create is too long"));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
_tcscat(szValueBuf,pTypeCommand[iCount].lpctstrCommand);
|
|
|
|
// if we should not replace existing keys, let's check if it exists
|
|
if (!bOverwriteExisting)
|
|
{
|
|
uiRet = RegOpenKey( HKEY_CLASSES_ROOT,
|
|
szKeyBuf,
|
|
&hKey);
|
|
if (uiRet==ERROR_SUCCESS)
|
|
{
|
|
// this means we should skip this key
|
|
RegCloseKey(hKey);
|
|
VERBOSE(DBG_MSG,
|
|
_T("RegOpenKey:PrintTo succedded, no change in PrintTo verb for %s"),
|
|
pTypeCommand[iCount].lpctstrType);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (uiRet==ERROR_FILE_NOT_FOUND)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("PrintTo verb does not exist for %s, creating..."),
|
|
pTypeCommand[iCount].lpctstrType);
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (REGISTRY_ERR,
|
|
TEXT("Could not open registry key %s (ec=0x%08x)"),
|
|
szKeyBuf,
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
// if we're here, we should create the key
|
|
uiRet = RegCreateKey( HKEY_CLASSES_ROOT,
|
|
szKeyBuf,
|
|
&hCommandKey);
|
|
if (uiRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE (REGISTRY_ERR,
|
|
TEXT("Could not create registry key %s (ec=0x%08x)"),
|
|
szKeyBuf,
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
uiRet = RegSetValue(hCommandKey,
|
|
NULL,
|
|
REG_SZ,
|
|
szValueBuf,
|
|
sizeof(szValueBuf));
|
|
if (uiRet==ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("RegSetValue success: %s "),
|
|
szValueBuf);
|
|
}
|
|
else
|
|
{
|
|
VERBOSE (REGISTRY_ERR,
|
|
TEXT("Could not set value registry key %s\\shell\\printto\\command to %s (ec=0x%08x)"),
|
|
pTypeCommand[iCount].lpctstrType,
|
|
szValueBuf,
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
if (hCommandKey)
|
|
{
|
|
RegCloseKey(hCommandKey);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
if (hCommandKey)
|
|
{
|
|
RegCloseKey(hCommandKey);
|
|
}
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* DPSetDefaultPrinter */
|
|
/* */
|
|
/* Parameters: */
|
|
/* pPrinterName: Valid name of existing printer to make default. */
|
|
/* */
|
|
/* Returns: TRUE for success, FALSE for failure. */
|
|
/*-----------------------------------------------------------------*/
|
|
BOOL SetDefaultPrinter(LPTSTR pPrinterName)
|
|
{
|
|
OSVERSIONINFO osv;
|
|
DWORD dwNeeded = 0;
|
|
HANDLE hPrinter = NULL;
|
|
PPRINTER_INFO_2 ppi2 = NULL;
|
|
LPTSTR pBuffer = NULL;
|
|
BOOL bRes = TRUE;
|
|
PPRINTER_INFO_2 pPrinterInfo = NULL;
|
|
DWORD dwNumPrinters = 0;
|
|
|
|
DBG_ENTER(TEXT("SetDefaultPrinter"),bRes);
|
|
|
|
// What version of Windows are you running?
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetVersionEx failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// If Windows NT, use WriteProfileString for version 4.0 and earlier...
|
|
if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("W9X OS, not setting default printer"));
|
|
goto exit;
|
|
}
|
|
|
|
if (osv.dwMajorVersion >= 5) // Windows 2000 or later...
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("W2K OS, not setting default printer"));
|
|
goto exit;
|
|
}
|
|
|
|
// are we the only printer installed?
|
|
pPrinterInfo = (PPRINTER_INFO_2) MyEnumPrinters(NULL,
|
|
2,
|
|
&dwNumPrinters,
|
|
PRINTER_ENUM_CONNECTIONS | PRINTER_ENUM_LOCAL
|
|
);
|
|
if (!pPrinterInfo)
|
|
{
|
|
VERBOSE (GENERAL_ERR,
|
|
TEXT("MyEnumPrinters() failed (ec: %ld)"),
|
|
GetLastError());
|
|
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
if (dwNumPrinters!=1)
|
|
{
|
|
VERBOSE (DBG_MSG,
|
|
TEXT("More than one printer installed on NT4, not setting default printer"));
|
|
goto exit;
|
|
}
|
|
// Open this printer so you can get information about it...
|
|
if (!OpenPrinter(pPrinterName, &hPrinter, NULL))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("OpenPrinter failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
// The first GetPrinter() tells you how big our buffer should
|
|
// be in order to hold ALL of PRINTER_INFO_2. Note that this will
|
|
// usually return FALSE. This only means that the buffer (the 3rd
|
|
// parameter) was not filled in. You don't want it filled in here...
|
|
if (!GetPrinter(hPrinter, 2, 0, 0, &dwNeeded))
|
|
{
|
|
if (GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetPrinter failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Allocate enough space for PRINTER_INFO_2...
|
|
ppi2 = (PRINTER_INFO_2 *)MemAlloc(dwNeeded);
|
|
if (!ppi2)
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("MemAlloc failed"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// The second GetPrinter() fills in all the current<BR/>
|
|
// information...
|
|
if (!GetPrinter(hPrinter, 2, (LPBYTE)ppi2, dwNeeded, &dwNeeded))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetPrinter failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
if ((!ppi2->pDriverName) || (!ppi2->pPortName))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("pDriverName or pPortNameare NULL"));
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// Allocate buffer big enough for concatenated string.
|
|
// String will be in form "printername,drivername,portname"...
|
|
pBuffer = (LPTSTR)MemAlloc( ( _tcslen(pPrinterName) +
|
|
_tcslen(ppi2->pDriverName) +
|
|
_tcslen(ppi2->pPortName) + 3) *
|
|
sizeof(TCHAR) );
|
|
if (!pBuffer)
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("MemAlloc failed"));
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// Build string in form "printername,drivername,portname"...
|
|
_tcscpy(pBuffer, pPrinterName);
|
|
_tcscat(pBuffer, _T(","));
|
|
_tcscat(pBuffer, ppi2->pDriverName);
|
|
_tcscat(pBuffer, _T(","));
|
|
_tcscat(pBuffer, ppi2->pPortName);
|
|
|
|
// Set the default printer in Win.ini and registry...
|
|
if (!WriteProfileString(_T("windows"), _T("device"), pBuffer))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("WriteProfileString failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// Tell all open applications that this change occurred.
|
|
// Allow each app 1 second to handle this message.
|
|
if (!SendMessageTimeout( HWND_BROADCAST,
|
|
WM_SETTINGCHANGE,
|
|
0L,
|
|
0L,
|
|
SMTO_NORMAL,
|
|
1000,
|
|
NULL))
|
|
{
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("SendMessageTimeout failed: (ec=%d)"),
|
|
GetLastError());
|
|
bRes = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
// Cleanup...
|
|
if (pPrinterInfo)
|
|
{
|
|
MemFree(pPrinterInfo);
|
|
}
|
|
if (hPrinter)
|
|
{
|
|
ClosePrinter(hPrinter);
|
|
}
|
|
if (ppi2)
|
|
{
|
|
MemFree(ppi2);
|
|
}
|
|
if (pBuffer)
|
|
{
|
|
MemFree(pBuffer);
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
|
|
//
|
|
//
|
|
// Function: CheckForceReboot
|
|
//
|
|
// Description: This function checks if the ForceReboot flag is set in the registry
|
|
// if it is, signals WindowsInstaller that a reboot is needed
|
|
//
|
|
// Remarks:
|
|
// this is due to a bug in the Install Shield bootstrap which doesn't
|
|
// force a reboot after initial installation of WindowsIsntaller.
|
|
// this flag is set by our custom bootstrap before running the
|
|
// Install Shield bootstrap
|
|
// if we are run from the Application Launcher then we need to leave
|
|
// this registry entry for the Launcher to reboot, we know this by
|
|
// using the property APPLAUNCHER=TRUE
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall CheckForceReboot(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
TCHAR szPropBuffer[MAX_PATH] = {0};
|
|
HKEY hKey = NULL;
|
|
DWORD Size = sizeof(DWORD);
|
|
DWORD Value = 0;
|
|
LONG Rslt;
|
|
DWORD Type;
|
|
|
|
DBG_ENTER(TEXT("CheckForceReboot"),uiRet);
|
|
|
|
// check if we're running from the AppLauncher
|
|
if (!PrivateMsiGetProperty(hInstall,_T("APPLAUNCHER"),szPropBuffer))
|
|
{
|
|
VERBOSE (SETUP_ERR,
|
|
TEXT("PrivateMsiGetProperty failed (ec: %ld)"),
|
|
GetLastError());
|
|
goto exit;
|
|
}
|
|
if (_tcscmp(szPropBuffer,_T("TRUE"))==0)
|
|
{
|
|
// we're running from the Application Launcher, the registry entry DeferredReboot
|
|
// is sufficient.
|
|
VERBOSE(DBG_MSG,
|
|
_T("AppLauncher will take care of any needed boot"));
|
|
goto exit;
|
|
}
|
|
// open HKLM\\Software\\Microsoft\\SharedFax
|
|
Rslt = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
REGKEY_SBS2000_FAX_SETUP,
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
);
|
|
if (Rslt != ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("RegOpenKeyEx failed: (ec=%d)"),
|
|
GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
// check if ForceReboot flag exists
|
|
Rslt = RegQueryValueEx(
|
|
hKey,
|
|
DEFERRED_BOOT,
|
|
NULL,
|
|
&Type,
|
|
(LPBYTE) &Value,
|
|
&Size
|
|
);
|
|
if (Rslt!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("RegQueryValueEx failed: (ec=%d)"),
|
|
GetLastError());
|
|
goto exit;
|
|
}
|
|
|
|
// tell WindowsInstaller a reboot is needed
|
|
uiRet = MsiSetProperty(hInstall,_T("REBOOT"),_T("Force"));
|
|
if (uiRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("MsiSetProperty failed: (ec=%d)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
// delete ForceReboot flag
|
|
Rslt = RegDeleteValue(hKey,DEFERRED_BOOT);
|
|
if (Rslt!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("MsiSetMode failed: (ec=%d)"),
|
|
Rslt);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
return uiRet;
|
|
}
|
|
|
|
|
|
#define KODAKPRV_EXE_NAME _T("\\KODAKPRV.EXE")
|
|
#define TIFIMAGE_COMMAND_KEY _T("TIFImage.Document\\shell\\open\\command")
|
|
#define TIFIMAGE_DDEEXEC_KEY _T("TIFImage.Document\\shell\\open\\ddeexec")
|
|
//
|
|
//
|
|
// Function: ChangeTifAssociation
|
|
//
|
|
// Description: This function changes the open verb for TIF files
|
|
// on WinME from Image Preview to Kodak Imaging
|
|
//
|
|
// Remarks:
|
|
// this is due to bad quality of viewing TIF faxes in the Image Preview tool
|
|
//
|
|
// Args:
|
|
//
|
|
// hInstall : Handle from MSI, can get state of the current setup
|
|
//
|
|
// Author: MoolyB
|
|
DLL_API UINT __stdcall ChangeTifAssociation(MSIHANDLE hInstall)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
TCHAR szWindowsDirectory[MAX_PATH] = {0};
|
|
HANDLE hFind = INVALID_HANDLE_VALUE;
|
|
HKEY hKey = NULL;
|
|
LONG lRet = 0;
|
|
OSVERSIONINFO viVersionInfo;
|
|
WIN32_FIND_DATA FindFileData;
|
|
|
|
DBG_ENTER(TEXT("ChangeTifAssociation"),uiRet);
|
|
|
|
viVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&viVersionInfo))
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("GetVersionEx failed (ec: %ld)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Is this millennium?
|
|
if (!
|
|
( (viVersionInfo.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) &&
|
|
(viVersionInfo.dwMajorVersion==4) &&
|
|
(viVersionInfo.dwMinorVersion>=90)
|
|
)
|
|
)
|
|
{
|
|
VERBOSE(DBG_MSG,
|
|
_T("This is not Windows Millenium, exit fucntion"));
|
|
goto exit;
|
|
}
|
|
|
|
// find <WindowsFolder>\KODAKPRV.EXE
|
|
if (GetWindowsDirectory(szWindowsDirectory,MAX_PATH)==0)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("GetWindowsDirectory failed (ec: %ld)"),
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (_tcslen(KODAKPRV_EXE_NAME)+_tcslen(szWindowsDirectory)>=MAX_PATH-4)
|
|
{
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("Path to Kodak Imaging too long"));
|
|
uiRet = ERROR_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
_tcscat(szWindowsDirectory,KODAKPRV_EXE_NAME);
|
|
|
|
hFind = FindFirstFile(szWindowsDirectory, &FindFileData);
|
|
|
|
if (hFind==INVALID_HANDLE_VALUE)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("FindFirstFile %s failed (ec: %ld)"),
|
|
szWindowsDirectory,
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
FindClose(hFind);
|
|
|
|
_tcscat(szWindowsDirectory,_T(" \"%1\""));
|
|
|
|
// set open verb
|
|
lRet = RegOpenKey( HKEY_CLASSES_ROOT,
|
|
TIFIMAGE_COMMAND_KEY,
|
|
&hKey);
|
|
if (lRet!=ERROR_SUCCESS)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("RegOpenKey %s failed (ec: %ld)"),
|
|
TIFIMAGE_COMMAND_KEY,
|
|
uiRet);
|
|
goto exit;
|
|
}
|
|
|
|
lRet = RegSetValueEx( hKey,
|
|
NULL,
|
|
0,
|
|
REG_EXPAND_SZ,
|
|
(LPBYTE) szWindowsDirectory,
|
|
(_tcslen(szWindowsDirectory) + 1) * sizeof (TCHAR)
|
|
);
|
|
if (lRet!=ERROR_SUCCESS)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("RegSetValueEx %s failed (ec: %ld)"),
|
|
szWindowsDirectory,
|
|
uiRet);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
lRet = RegDeleteKey(HKEY_CLASSES_ROOT,TIFIMAGE_DDEEXEC_KEY);
|
|
if (lRet!=ERROR_SUCCESS)
|
|
{
|
|
uiRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,
|
|
TEXT("RegDeleteKey %s failed (ec: %ld)"),
|
|
TIFIMAGE_DDEEXEC_KEY,
|
|
uiRet);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
|
|
exit:
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
return uiRet;
|
|
}
|
|
|
|
|
|
#define MAKE_RELATIVE(pMember,pBase) (pMember ? (((UINT)pMember)-UINT(pBase)) : NULL)
|
|
#define MAKE_ABSOLUTE(pMember,pBase) (pMember ? (((UINT)pMember)+UINT(pBase)) : NULL)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
// Function:
|
|
// FindExistingPrinters
|
|
//
|
|
// Purpose:
|
|
// This function enumerates the existing printers to SBS/BOS 2000
|
|
// Fax servers.
|
|
// The found printers are stored in the registry to be restored
|
|
// after the RemoveExistingProducts is run.
|
|
// Since the upgrade from SBS/BOS2000 requires uninstalling the existing
|
|
// Shared fax service client, the printer connection will be lost unless we
|
|
// save it prior to the removal of the client and restore afterwards.
|
|
//
|
|
// Params:
|
|
// MSIHANDLE hInstall - handle to the instllation package
|
|
//
|
|
// Return Value:
|
|
// NO_ERROR - everything was ok.
|
|
// Win32 Error code in case if failure.
|
|
//
|
|
// Author:
|
|
// Mooly Beery (MoolyB) 28-Oct-2001
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
DLL_API UINT __stdcall FindExistingPrinters(MSIHANDLE hInstall)
|
|
{
|
|
BYTE* pbPrinterInfo = NULL;
|
|
DWORD cb = 0;
|
|
DWORD dwNumPrinters = 0;
|
|
DWORD dwIndex = 0;
|
|
HKEY hKey = NULL;
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
|
|
DBG_ENTER(TEXT("FindExistingPrinters"), dwRet);
|
|
|
|
// this call should fail due to lack of space...
|
|
if (EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,NULL,2,NULL,0,&cb,&dwNumPrinters))
|
|
{
|
|
VERBOSE( SETUP_ERR,TEXT("EnumPrinters succeeded with zero buffer, probably no printers."));
|
|
goto exit;
|
|
}
|
|
|
|
dwRet = GetLastError();
|
|
if (dwRet!=ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
VERBOSE( SETUP_ERR,TEXT("EnumPrinters failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
dwRet = ERROR_SUCCESS;
|
|
|
|
pbPrinterInfo = (BYTE*)MemAlloc(cb);
|
|
if (!pbPrinterInfo)
|
|
{
|
|
dwRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
VERBOSE( SETUP_ERR,TEXT("MemAlloc failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Get all existing printers into pbPrinterInfo
|
|
if (!EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,NULL,2,pbPrinterInfo,cb,&cb,&dwNumPrinters))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("EnumPrinters failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (dwNumPrinters==0)
|
|
{
|
|
VERBOSE( SETUP_ERR,TEXT("No printers to store"));
|
|
goto exit;
|
|
}
|
|
|
|
// fix up pointers in the PRINTER_INFO_2 structure to become relative.
|
|
for ( dwIndex=0 ; dwIndex<dwNumPrinters ; dwIndex++ )
|
|
{
|
|
PPRINTER_INFO_2 pInfo = &((PPRINTER_INFO_2)pbPrinterInfo)[dwIndex];
|
|
|
|
VERBOSE(DBG_MSG,_T("Printer ' %s ' will be saved"), pInfo->pPrinterName);
|
|
|
|
pInfo->pServerName = LPTSTR(MAKE_RELATIVE(pInfo->pServerName,pInfo));
|
|
pInfo->pPrinterName = LPTSTR(MAKE_RELATIVE(pInfo->pPrinterName,pInfo));
|
|
pInfo->pShareName = LPTSTR(MAKE_RELATIVE(pInfo->pShareName,pInfo));
|
|
pInfo->pPortName = LPTSTR(MAKE_RELATIVE(pInfo->pPortName,pInfo));
|
|
pInfo->pDriverName = LPTSTR(MAKE_RELATIVE(pInfo->pDriverName,pInfo));
|
|
pInfo->pComment = LPTSTR(MAKE_RELATIVE(pInfo->pComment,pInfo));
|
|
pInfo->pLocation = LPTSTR(MAKE_RELATIVE(pInfo->pLocation,pInfo));
|
|
pInfo->pSepFile = LPTSTR(MAKE_RELATIVE(pInfo->pSepFile,pInfo));
|
|
pInfo->pPrintProcessor = LPTSTR(MAKE_RELATIVE(pInfo->pPrintProcessor,pInfo));
|
|
pInfo->pDatatype = LPTSTR(MAKE_RELATIVE(pInfo->pDatatype,pInfo));
|
|
pInfo->pParameters = LPTSTR(MAKE_RELATIVE(pInfo->pParameters,pInfo));
|
|
pInfo->pDevMode = LPDEVMODE(MAKE_RELATIVE(pInfo->pDevMode,pInfo));
|
|
pInfo->pSecurityDescriptor = PSECURITY_DESCRIPTOR(MAKE_RELATIVE(pInfo->pSecurityDescriptor,pInfo));
|
|
}
|
|
|
|
// open HKLM\\Software\\Microsoft\\SharedFax\\Setup\\Upgrade
|
|
hKey = OpenRegistryKey(HKEY_LOCAL_MACHINE,REGKEY_SBS2000_FAX_SETUP_UPGRADE,TRUE,KEY_WRITE);
|
|
if (!hKey)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("OpenRegistryKey failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// store pbPrinterInfo to the registry.
|
|
if (!SetRegistryBinary(hKey,REGVAL_STORED_PRINTERS,pbPrinterInfo,cb))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("SetRegistryBinary failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (!SetRegistryDword(hKey,REGVAL_STORED_PRINTERS_COUNT,dwNumPrinters))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("SetRegistryDword failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
if (pbPrinterInfo)
|
|
{
|
|
MemFree(pbPrinterInfo);
|
|
}
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
// Function:
|
|
// RestorePrinters
|
|
//
|
|
// Purpose:
|
|
// This function reads the list of printers from the registry and restores them
|
|
// the list was stored by a prior call to FindExistingPrinters and what was
|
|
// stored was the the result of a call to EnumPrinters which is an array of
|
|
// PRINTER_INFO_2. this array is scanned now for fax printers and they are
|
|
// restored. this data is kept in the registry during fax client setup since
|
|
// it's practically impossible to transfer large chunks of binary data between
|
|
// two deferred custom actions.
|
|
//
|
|
// Params:
|
|
// MSIHANDLE hInstall - handle to the instllation package
|
|
//
|
|
// Return Value:
|
|
// NO_ERROR - everything was ok.
|
|
// Win32 Error code in case if failure.
|
|
//
|
|
// Author:
|
|
// Mooly Beery (MoolyB) 28-Oct-2001
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
DLL_API UINT __stdcall RestorePrinters(MSIHANDLE hInstall)
|
|
{
|
|
HKEY hKey = NULL;
|
|
BYTE* pPrinterInfo = NULL;
|
|
DWORD cb = 0;
|
|
DWORD dwIndex = 0;
|
|
DWORD dwNumPrinters = 0;
|
|
HANDLE hPrinter = NULL;
|
|
DWORD dwRet = ERROR_SUCCESS;
|
|
BOOL fIsW9X = FALSE;
|
|
OSVERSIONINFO osv;
|
|
|
|
DBG_ENTER(TEXT("RestorePrinters"), dwRet);
|
|
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("GetVersionEx failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// If NT4/W2K, use AddPrinterConnection. if W9X, use AddPrinter.
|
|
if (osv.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
fIsW9X = TRUE;
|
|
}
|
|
|
|
// open HKLM\\Software\\Microsoft\\SharedFax\\Setup\\Upgrade
|
|
hKey = OpenRegistryKey(HKEY_LOCAL_MACHINE,REGKEY_SBS2000_FAX_SETUP_UPGRADE,TRUE,KEY_READ);
|
|
if (!hKey)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("OpenRegistryKey failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// get the array of PRINTER_INFO_2
|
|
pPrinterInfo = GetRegistryBinary(hKey,REGVAL_STORED_PRINTERS,&cb);
|
|
if (!pPrinterInfo)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("GetRegistryBinary failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
if (cb==1)
|
|
{
|
|
// Data wasn't found in the registry.
|
|
// Current implementation of GetRegistryBinary returns a 1-byte buffer of 0 in that case.
|
|
// We know for sure that data must be longer than 10 bytes.
|
|
//
|
|
dwRet = ERROR_FILE_NOT_FOUND;
|
|
VERBOSE( SETUP_ERR,TEXT("GetRegistryBinary failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// get the number of stored printers
|
|
dwNumPrinters = GetRegistryDword(hKey,REGVAL_STORED_PRINTERS_COUNT);
|
|
if (dwNumPrinters==0)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("GetRegistryDword failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// for each printer check if this is a fax printer
|
|
for (dwIndex=0;dwIndex<dwNumPrinters;dwIndex++)
|
|
{
|
|
PPRINTER_INFO_2 pInfo = &((PPRINTER_INFO_2)pPrinterInfo)[dwIndex];
|
|
|
|
// fixup pointers to become absulute again.
|
|
pInfo->pServerName = LPTSTR(MAKE_ABSOLUTE(pInfo->pServerName,pInfo));
|
|
pInfo->pPrinterName = LPTSTR(MAKE_ABSOLUTE(pInfo->pPrinterName,pInfo));
|
|
pInfo->pShareName = LPTSTR(MAKE_ABSOLUTE(pInfo->pShareName,pInfo));
|
|
pInfo->pPortName = LPTSTR(MAKE_ABSOLUTE(pInfo->pPortName,pInfo));
|
|
pInfo->pDriverName = LPTSTR(MAKE_ABSOLUTE(pInfo->pDriverName,pInfo));
|
|
pInfo->pComment = LPTSTR(MAKE_ABSOLUTE(pInfo->pComment,pInfo));
|
|
pInfo->pLocation = LPTSTR(MAKE_ABSOLUTE(pInfo->pLocation,pInfo));
|
|
pInfo->pSepFile = LPTSTR(MAKE_ABSOLUTE(pInfo->pSepFile,pInfo));
|
|
pInfo->pPrintProcessor = LPTSTR(MAKE_ABSOLUTE(pInfo->pPrintProcessor,pInfo));
|
|
pInfo->pDatatype = LPTSTR(MAKE_ABSOLUTE(pInfo->pDatatype,pInfo));
|
|
pInfo->pParameters = LPTSTR(MAKE_ABSOLUTE(pInfo->pParameters,pInfo));
|
|
pInfo->pDevMode = LPDEVMODE(MAKE_ABSOLUTE(pInfo->pDevMode,pInfo));
|
|
pInfo->pSecurityDescriptor = PSECURITY_DESCRIPTOR(MAKE_ABSOLUTE(pInfo->pSecurityDescriptor,pInfo));
|
|
|
|
if ( _tcsicmp(pInfo->pDriverName,FAX_DRIVER_NAME))
|
|
{
|
|
VERBOSE( DBG_MSG,TEXT("Printer %s is not a fax printer "),pInfo->pDriverName);
|
|
continue;
|
|
}
|
|
|
|
// This is SBS 5.0 or .NET SB3/RC1 Server Fax Printer Connections.
|
|
// During the upgrade, Uninstall removed them from the system.
|
|
// We need to put them back.
|
|
//
|
|
if (fIsW9X)
|
|
{
|
|
hPrinter = AddPrinter(NULL,2,LPBYTE(pInfo));
|
|
if (!hPrinter)
|
|
{
|
|
// Failed to add printer
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AddPrinter failed (ec: %ld)"),dwRet);
|
|
continue;
|
|
}
|
|
ClosePrinter(hPrinter);
|
|
hPrinter = NULL;
|
|
|
|
VERBOSE(DBG_MSG, _T("Printer ' %s ' is restored"), pInfo->pPrinterName);
|
|
}
|
|
else
|
|
{
|
|
if (!AddPrinterConnection(pInfo->pPrinterName))
|
|
{
|
|
// Failed to add printer connection
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AddPrinterConnection failed (ec: %ld)"),dwRet);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (pPrinterInfo)
|
|
{
|
|
MemFree(pPrinterInfo);
|
|
}
|
|
if (hKey)
|
|
{
|
|
RegCloseKey(hKey);
|
|
hKey = NULL;
|
|
}
|
|
|
|
// finally, remove the stored printers key from the registry
|
|
if (!DeleteRegistryKey(HKEY_LOCAL_MACHINE,REGKEY_SBS2000_FAX_SETUP_UPGRADE))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("DeleteRegistryKey failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
return dwRet;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
// Function:
|
|
// DetectSBSServer
|
|
//
|
|
// Purpose:
|
|
// This function detects if SBS2000 Fax service is installed
|
|
// If it is, it sets a property in the MSI installation and
|
|
// causes the LaunchCondition to block the installation of
|
|
// the client on such machines.
|
|
//
|
|
// Params:
|
|
// MSIHANDLE hInstall - handle to the instllation package
|
|
//
|
|
// Return Value:
|
|
// NO_ERROR - everything was ok.
|
|
// Win32 Error code in case if failure.
|
|
//
|
|
// Author:
|
|
// Mooly Beery (MoolyB) 23-Jan-2002
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
DLL_API UINT __stdcall DetectSBSServer(MSIHANDLE hInstall)
|
|
{
|
|
DWORD dwRet = NO_ERROR;
|
|
DWORD dwFaxInstalled = FXSTATE_NONE;
|
|
|
|
DBG_ENTER(TEXT("DetectSBSServer"),dwRet);
|
|
|
|
if (CheckInstalledFax(FXSTATE_SBS5_SERVER, &dwFaxInstalled) != ERROR_SUCCESS)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("CheckInstalledFaxClient failed (ec: %ld)"),dwRet);
|
|
return dwRet;
|
|
}
|
|
|
|
if (dwFaxInstalled != FXSTATE_NONE)
|
|
{
|
|
VERBOSE( DBG_MSG,TEXT("SBS2000 Fax service is installed, set SBSSERVERDETECTED in MSI"));
|
|
if (MsiSetProperty(hInstall,_T("SBSSERVERDETECTED"),_T("1"))!=ERROR_SUCCESS)
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("MsiSetProperty failed (ec: %ld)"),dwRet);
|
|
return dwRet;
|
|
}
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
// Function:
|
|
// SecureFxsTmpFolder
|
|
//
|
|
// Purpose:
|
|
// This function secures the FxsTmp folder we create under
|
|
// %systemroot%\system32.
|
|
// This folder needs special security since it holds the preview
|
|
// file of the sent TIFF and can potentially expose all
|
|
// outgoing faxes.
|
|
// The security applied to this folder is as follows:
|
|
//
|
|
// BUILTIN\Administrators:(OI)(CI)F - Full control, folder and files
|
|
// NT AUTHORITY\SYSTEM:(OI)(CI)F - Full control, folder and files
|
|
// CREATOR OWNER:(OI)(CI)(IO)F - Full control, files only
|
|
// BUILTIN\Users:(special access:) - SYNCHRONIZE
|
|
// - FILE_READ_DATA
|
|
// - FILE_WRITE_DATA
|
|
//
|
|
// Params:
|
|
// MSIHANDLE hInstall - handle to the instllation package
|
|
//
|
|
// Return Value:
|
|
// NO_ERROR - everything was ok.
|
|
// Win32 Error code in case if failure.
|
|
//
|
|
// Author:
|
|
// Mooly Beery (MoolyB) 09-Dec-2001
|
|
///////////////////////////////////////////////////////////////////////////////////////
|
|
DLL_API UINT __stdcall SecureFxsTmpFolder(MSIHANDLE hInstall)
|
|
{
|
|
DWORD dwRet = 0;
|
|
DWORD dwFileAttributes = 0;
|
|
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
|
|
PSID pSidAliasAdmins = NULL;
|
|
PSID pSidAliasUsers = NULL;
|
|
PSID pSidAliasSystem = NULL;
|
|
PSID pSidCreatorOwner = NULL;
|
|
TCHAR szFolderToSecure[MAX_PATH] = {0};
|
|
PACL pNewAcl = NULL;
|
|
EXPLICIT_ACCESS ExplicitAccess[4];
|
|
SECURITY_DESCRIPTOR NewSecurityDescriptor;
|
|
BOOL bNT4OS;
|
|
OSVERSIONINFO osv;
|
|
|
|
DBG_ENTER(TEXT("SecureFxsTmpFolder"), dwRet);
|
|
|
|
// What version of Windows are you running?
|
|
osv.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
if (!GetVersionEx(&osv))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE(GENERAL_ERR,
|
|
_T("GetVersionEx failed: (ec=%d)"),
|
|
dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (osv.dwMajorVersion >= 5) // Windows 2000 or later...
|
|
{
|
|
bNT4OS = FALSE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// On NT4, SetEntriesInAcl() does not seem to work with CREATOR OWNER SID.
|
|
// Adding the CREATOR OWNER ACE using AddAccessAllowedAce()
|
|
//
|
|
bNT4OS = TRUE;
|
|
}
|
|
|
|
if (GetSystemDirectory(szFolderToSecure,MAX_PATH-_tcslen(FAX_PREVIEW_TMP_DIR)-2))
|
|
{
|
|
VERBOSE( DBG_MSG,TEXT("GetSystemDirectory succeeded (%s)"),szFolderToSecure);
|
|
}
|
|
else
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("GetSystemDirectory failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
_tcscat(szFolderToSecure,FAX_PREVIEW_TMP_DIR);
|
|
VERBOSE( DBG_MSG,TEXT("Folder to secure is %s"),szFolderToSecure);
|
|
|
|
// Allocate and initialize the local admins SID
|
|
if (!AllocateAndInitializeSid( &NtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS,
|
|
0,0,0,0,0,0,
|
|
&pSidAliasAdmins
|
|
))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AllocateAndInitializeSid failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Allocate and initialize the local users SID
|
|
if (!AllocateAndInitializeSid( &NtAuthority,
|
|
2,
|
|
SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_USERS,
|
|
0,0,0,0,0,0,
|
|
&pSidAliasUsers
|
|
))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AllocateAndInitializeSid failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Allocate and initialize the system SID
|
|
if (!AllocateAndInitializeSid( &NtAuthority,
|
|
1,
|
|
SECURITY_LOCAL_SYSTEM_RID,
|
|
0,0,0,0,0,0,0,
|
|
&pSidAliasSystem
|
|
))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AllocateAndInitializeSid failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Allocate and initialize the creator owner SID
|
|
if (!AllocateAndInitializeSid( &CreatorSidAuthority,
|
|
1,
|
|
SECURITY_CREATOR_OWNER_RID,
|
|
0,0,0,0,0,0,0,
|
|
&pSidCreatorOwner
|
|
))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("AllocateAndInitializeSid failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// Admins have full control
|
|
ExplicitAccess[0].grfAccessPermissions = GENERIC_ALL;
|
|
ExplicitAccess[0].grfAccessMode = SET_ACCESS;
|
|
ExplicitAccess[0].grfInheritance= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
ExplicitAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[0].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ExplicitAccess[0].Trustee.ptstrName = (LPTSTR) pSidAliasAdmins;
|
|
|
|
// System has full control
|
|
ExplicitAccess[1].grfAccessPermissions = GENERIC_ALL;
|
|
ExplicitAccess[1].grfAccessMode = SET_ACCESS;
|
|
ExplicitAccess[1].grfInheritance= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
|
|
ExplicitAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ExplicitAccess[1].Trustee.ptstrName = (LPTSTR) pSidAliasSystem;
|
|
|
|
// Users have SYNCHRONIZE, FILE_READ_DATA, FILE_WRITE_DATA - this folder only
|
|
ExplicitAccess[2].grfAccessPermissions = FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE;
|
|
ExplicitAccess[2].grfAccessMode = SET_ACCESS;
|
|
ExplicitAccess[2].grfInheritance= NO_INHERITANCE;
|
|
ExplicitAccess[2].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[2].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
|
|
ExplicitAccess[2].Trustee.ptstrName = (LPTSTR) pSidAliasUsers;
|
|
|
|
if (FALSE == bNT4OS)
|
|
{
|
|
//
|
|
// SetEntriesInAcl works fine with CREATOR OWNER
|
|
//
|
|
|
|
// Creator Owner - full control - subfolders and files only
|
|
ExplicitAccess[3].grfAccessPermissions = GENERIC_ALL;
|
|
ExplicitAccess[3].grfAccessMode = SET_ACCESS;
|
|
ExplicitAccess[3].grfInheritance= INHERIT_ONLY_ACE | SUB_OBJECTS_ONLY_INHERIT | SUB_CONTAINERS_ONLY_INHERIT;
|
|
ExplicitAccess[3].Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicitAccess[3].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
|
ExplicitAccess[3].Trustee.ptstrName = (LPTSTR) pSidCreatorOwner;
|
|
}
|
|
|
|
// make an ACL from the admins only
|
|
dwRet = SetEntriesInAcl(
|
|
bNT4OS ? 3 : 4,
|
|
ExplicitAccess,
|
|
NULL,
|
|
&pNewAcl);
|
|
if (dwRet!=ERROR_SUCCESS)
|
|
{
|
|
VERBOSE( SETUP_ERR,TEXT("SetEntriesInAcl failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
if (TRUE == bNT4OS)
|
|
{
|
|
//
|
|
// We are running on NT4, add the CREATOR OWNER ACE using AddAccessAllowedAce()
|
|
//
|
|
ACL_SIZE_INFORMATION AclSizeInfo;
|
|
PACL pFullNewAcl = NULL;
|
|
WORD wFullAclSize = 0;
|
|
ACCESS_ALLOWED_ACE* pAce = NULL;
|
|
|
|
//
|
|
// Get the current ACL size
|
|
//
|
|
if (!GetAclInformation(pNewAcl, &AclSizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR, TEXT("GetAclInformation failed (ec: %ld)"), dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
wFullAclSize = (WORD)(AclSizeInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
|
|
+ GetLengthSid(pSidCreatorOwner));
|
|
|
|
//
|
|
// Re-allocate big enough ACL
|
|
//
|
|
pFullNewAcl = (PACL)LocalAlloc(0, wFullAclSize);
|
|
if (NULL == pFullNewAcl)
|
|
{
|
|
VERBOSE( SETUP_ERR,TEXT("LocalAlloc failed (ec: %ld)"),GetLastError());
|
|
goto exit;
|
|
}
|
|
CopyMemory(pFullNewAcl, pNewAcl, AclSizeInfo.AclBytesInUse);
|
|
LocalFree(pNewAcl);
|
|
pNewAcl = pFullNewAcl;
|
|
|
|
//
|
|
// Set the correct ACL size
|
|
//
|
|
pNewAcl->AclSize = wFullAclSize;
|
|
if (!AddAccessAllowedAce(
|
|
pNewAcl,
|
|
ACL_REVISION,
|
|
GENERIC_ALL,
|
|
pSidCreatorOwner))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR, TEXT("AddAccessAllowedAce failed (ec: %ld)"), dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Change the last ACE flags, so it will be inherited to child objects.
|
|
//
|
|
if (!GetAce(
|
|
pNewAcl,
|
|
3,
|
|
(VOID**)&pAce
|
|
))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR, TEXT("GetAce failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
pAce->Header.AceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE | INHERIT_ONLY_ACE;
|
|
}
|
|
|
|
if (!InitializeSecurityDescriptor(&NewSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("InitializeSecurityDescriptor failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Add the ACL to the security descriptor.
|
|
//
|
|
if (!SetSecurityDescriptorDacl(&NewSecurityDescriptor, TRUE, pNewAcl, FALSE))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("SetSecurityDescriptorDacl failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
// set security so only admins can access
|
|
if (!SetFileSecurity( szFolderToSecure,
|
|
DACL_SECURITY_INFORMATION,
|
|
&NewSecurityDescriptor))
|
|
{
|
|
dwRet = GetLastError();
|
|
VERBOSE( SETUP_ERR,TEXT("SetFileSecurity failed (ec: %ld)"),dwRet);
|
|
goto exit;
|
|
}
|
|
|
|
|
|
exit:
|
|
|
|
if (pSidAliasUsers)
|
|
{
|
|
FreeSid(pSidAliasUsers);
|
|
}
|
|
if (pSidAliasAdmins)
|
|
{
|
|
FreeSid(pSidAliasAdmins);
|
|
}
|
|
if (pSidAliasSystem)
|
|
{
|
|
FreeSid(pSidAliasSystem);
|
|
}
|
|
if (pSidCreatorOwner)
|
|
{
|
|
FreeSid(pSidCreatorOwner);
|
|
}
|
|
if (pNewAcl)
|
|
{
|
|
LocalFree(pNewAcl);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
/*
|
|
Function:
|
|
CreateFaxPrinterName
|
|
Purpose:
|
|
This function extracts Server Name from the Port Name and concatenates to it
|
|
the FAX_PRINTER_NAME. This is used when adding a fax printer connection in
|
|
the W9x systems.
|
|
|
|
This function takes the '\\server-name\fax-printer-name' port name and returns
|
|
'fax-printer-name (server-name)' fax printer name.
|
|
|
|
This is done to prevent clashing between fax printer names for different servers.
|
|
|
|
The caller must free the *ptzFaxPrinterName.
|
|
Params:
|
|
|
|
IN LPCTSTR tzPortName - the port name, of format "\\server-name\fax-printer-name"
|
|
OUT LPTSTR* ptzFaxPrinterName - the resulting buffer
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - everything was ok.
|
|
Win32 Error code in case if failure.
|
|
|
|
Author:
|
|
Iv Vakaluk, 28-May-2002
|
|
*/
|
|
|
|
DWORD CreateFaxPrinterName(
|
|
IN LPCTSTR tzPortName,
|
|
OUT LPTSTR* ptzPrinterName
|
|
)
|
|
{
|
|
DWORD dwRet = NO_ERROR;
|
|
TCHAR tzFaxServerName[MAX_PATH];
|
|
TCHAR tzFaxPrinterName[MAX_PATH];
|
|
LPTSTR lptstrResult = NULL;
|
|
DWORD dwSize = 0;
|
|
|
|
DBG_ENTER(_T("CreateFaxPrinterName"), dwRet);
|
|
|
|
if ((!tzPortName) || ((_tcslen(tzPortName)) == 0))
|
|
{
|
|
VERBOSE(SETUP_ERR, _T("Port Name is empty."));
|
|
dwRet = ERROR_INVALID_PARAMETER;
|
|
return dwRet;
|
|
}
|
|
|
|
//
|
|
// delimiter scanf uses by default is white-space characters ( ' ', '\t', '\n' )
|
|
// i need that '\\' will be delimiter.
|
|
// this is done by specifiing the [^\\] for scanf.
|
|
// [x] instructs scanf to read only 'x' and stop at any other input.
|
|
// [^x] instructs scanf to read anything until 'x' is reached.
|
|
//
|
|
if (_stscanf(tzPortName, _T("\\\\%[^\\] \\ %[^\0]"), tzFaxServerName, tzFaxPrinterName) != 2)
|
|
{
|
|
VERBOSE(SETUP_ERR, _T("sscanf() failed. Should be wrong tzPortName='%s'."), tzPortName);
|
|
dwRet = ERROR_INVALID_PARAMETER;
|
|
return dwRet;
|
|
}
|
|
|
|
//
|
|
// size(result name) = size(server name) + size(FAX_PRINTER_NAME) + size(space + 2 parentesis + NULL)
|
|
//
|
|
dwSize = _tcslen(tzFaxServerName) + _tcslen(tzFaxPrinterName) + 4;
|
|
|
|
lptstrResult = LPTSTR(MemAlloc(dwSize * sizeof TCHAR));
|
|
if (!lptstrResult)
|
|
{
|
|
VERBOSE (GENERAL_ERR, _T("Not enough memory"));
|
|
dwRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
return dwRet;
|
|
}
|
|
|
|
_sntprintf(lptstrResult, dwSize, _T("%s (%s)"), tzFaxPrinterName, tzFaxServerName);
|
|
VERBOSE(DBG_MSG, _T("Printer Name is : '%s'"), lptstrResult);
|
|
|
|
*ptzPrinterName = lptstrResult;
|
|
return dwRet;
|
|
}
|
|
|
|
/*
|
|
Function:
|
|
SetBOSProgramFolder
|
|
Purpose:
|
|
This function does the following :
|
|
a) creates path from the given CSIDL of the system path and given folder name.
|
|
b) verifyes that the path is valid.
|
|
c) optionally writes the valid path in the MSI property called 'BOSProgramFolder'.
|
|
|
|
Called from FindBOSProgramFolder custom action.
|
|
|
|
Params:
|
|
|
|
IN MSIHANDLE hInstall - the MSI handle
|
|
IN int nFolder - A CSIDL value that identifies the folder whose path is to be retrieved
|
|
LPCTSTR tzProgramName - localized name of the BOS Fax Client Program Menu Entry
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - everything was ok.
|
|
Win32 Error code in case if failure.
|
|
|
|
Author:
|
|
Iv Vakaluk, 01-July-2002
|
|
*/
|
|
DWORD SetBOSProgramFolder(MSIHANDLE hInstall, int nFolder, LPCTSTR tzProgramName)
|
|
{
|
|
DWORD dwRes = ERROR_SUCCESS;
|
|
HRESULT hr = ERROR_SUCCESS;
|
|
TCHAR tzFullProgramPath[MAX_PATH*2] = {0};
|
|
|
|
DBG_ENTER(_T("SetBOSProgramFolder"), dwRes);
|
|
|
|
//
|
|
// Get the path to the given CSIDL system folder
|
|
//
|
|
hr = SHGetFolderPath (NULL, nFolder, NULL, SHGFP_TYPE_CURRENT, (LPTSTR)tzFullProgramPath);
|
|
if (FAILED(hr))
|
|
{
|
|
CALL_FAIL (GENERAL_ERR, TEXT("SHGetFolderPath()"), hr);
|
|
return (dwRes = ERROR_PATH_NOT_FOUND);
|
|
}
|
|
VERBOSE(DBG_MSG, _T("The system folder to look in : %s"), tzFullProgramPath);
|
|
|
|
//
|
|
// add the program name to the path
|
|
//
|
|
_tcsncat(tzFullProgramPath, _T("\\"), (ARR_SIZE(tzFullProgramPath) - _tcslen(tzFullProgramPath) - 1));
|
|
_tcsncat(tzFullProgramPath, tzProgramName, (ARR_SIZE(tzFullProgramPath) - _tcslen(tzFullProgramPath) -1));
|
|
VERBOSE(DBG_MSG, _T("The full path to look for : %s"), tzFullProgramPath);
|
|
|
|
//
|
|
// check that this path is valid
|
|
//
|
|
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(tzFullProgramPath))
|
|
{
|
|
VERBOSE(DBG_MSG, _T("The full path is not found."));
|
|
return (dwRes = ERROR_PATH_NOT_FOUND);
|
|
}
|
|
|
|
VERBOSE(DBG_MSG, _T("The full path is OK ==> write it into MSI property."));
|
|
|
|
//
|
|
// write it into the MSI
|
|
//
|
|
if (hInstall)
|
|
{
|
|
UINT uiRes = ERROR_SUCCESS;
|
|
|
|
uiRes = MsiSetProperty(hInstall, _T("BOSProgramFolder"), tzFullProgramPath);
|
|
if (uiRes != ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(SETUP_ERR, _T("MSISetProperty(BOSProgramFolder) is failed."));
|
|
return (dwRes = ERROR_FUNCTION_FAILED);
|
|
}
|
|
}
|
|
|
|
return dwRes;
|
|
}
|
|
|
|
|
|
/*
|
|
Function:
|
|
FindBOSProgramFolder
|
|
Purpose:
|
|
This custom action is used to set the MSI property called 'BOSProgramFolder'
|
|
to the name of the folder of BOS Fax Client on NT4 machines.
|
|
This is because the shortcuts of BOS Fax Client is not removed during the upgrade to .NET Fax Client.
|
|
And we must remove them manually.
|
|
We are using RemoveFile table for this, and we must know the folder where these shortcuts reside.
|
|
|
|
The function does following :
|
|
a) reads from the MSI the name of the BOS Fax Client Program Menu Entry.
|
|
b) calls SetBOSProgramFolder to look for this program first in the
|
|
COMMON PROGRAMS and if not successfull, then in the CURRENT USER PROGRAMS profiles.
|
|
c) SetBOSProgramFolder checks for the path validity and writes it into the MSI.
|
|
Params:
|
|
|
|
IN MSIHANDLE hInstall - the MSI handle
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - everything was ok.
|
|
Win32 Error code in case if failure.
|
|
|
|
Author:
|
|
Iv Vakaluk, 30-June-2002
|
|
*/
|
|
|
|
DLL_API UINT __stdcall FindBOSProgramFolder(MSIHANDLE hInstall)
|
|
{
|
|
UINT rc = ERROR_INSTALL_FAILURE;
|
|
TCHAR tzProgramName[MAX_PATH] = {0};
|
|
|
|
DBG_ENTER(TEXT("FindBOSProgramFolder"), rc);
|
|
|
|
//
|
|
// Get from MSI the localized name of the program menu entry that we are looking for
|
|
//
|
|
if (!PrivateMsiGetProperty(hInstall, _T("BOSProgramName"), tzProgramName))
|
|
{
|
|
VERBOSE (SETUP_ERR, _T("PrivateMsiGetProperty(BOSProgramName) failed (ec: %ld)"), GetLastError());
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Look in the COMMON PROGRAMS
|
|
//
|
|
rc = SetBOSProgramFolder(hInstall, CSIDL_COMMON_PROGRAMS, tzProgramName);
|
|
if (rc == ERROR_PATH_NOT_FOUND)
|
|
{
|
|
//
|
|
// Look in the CURRENT USER PROGRAMS
|
|
//
|
|
rc = SetBOSProgramFolder(hInstall, CSIDL_PROGRAMS, tzProgramName);
|
|
}
|
|
|
|
if (rc != ERROR_SUCCESS)
|
|
{
|
|
VERBOSE(SETUP_ERR, _T("Failed to find a program path / to set MSI property."));
|
|
rc = ERROR_INSTALL_FAILURE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|