windows-nt/Source/XPSP1/NT/enduser/netmeeting/as/cpi32/asmaster.cpp

1045 lines
22 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#include "precomp.h"
#include <RegEntry.h>
#include <oprahcom.h>
#include <asmaster.h>
#define MLZ_FILE_ZONE ZONE_CORE
ASMaster *g_pMaster = NULL;
HRESULT WINAPI CreateASObject
(
IAppSharingNotify * pNotify,
UINT flags,
IAppSharing** ppAS
)
{
HRESULT hr = E_OUTOFMEMORY;
ASMaster * pMaster = NULL;
DebugEntry(CreateASObject);
ASSERT(ppAS);
if (g_pMaster != NULL)
{
ERROR_OUT(("CreateASObject: IAppSharing * alreadycreated; only one allowed at a time"));
hr = E_UNEXPECTED;
DC_QUIT;
}
ASSERT(!g_asMainThreadId);
ASSERT(!g_putOM);
ASSERT(!g_putAL);
ASSERT(!g_putAS);
pMaster = new ASMaster(flags, pNotify);
if (pMaster != NULL)
{
//
// Register as the groupware primary, with an event proc but no exit proc
//
if (!UT_InitTask(UTTASK_UI, &g_putUI))
{
ERROR_OUT(("Failed to register UI task"));
DC_QUIT;
}
UT_RegisterEvent(g_putUI, eventProc, g_putUI, UT_PRIORITY_NORMAL);
// Start groupware thread.
if (!DCS_StartThread(WorkThreadEntryPoint))
{
ERROR_OUT(("Couldn't start groupware thread"));
DC_QUIT;
}
// Make sure the work thread initialization is ok
if (! g_asMainThreadId)
{
ERROR_OUT(("Init failed in the work thread"));
DC_QUIT;
}
//
// Success!
//
}
hr = S_OK;
DC_EXIT_POINT:
if (!SUCCEEDED(hr))
{
if (pMaster)
{
ERROR_OUT(("CreateASObject: Init of ASMaster failed"));
pMaster->Release();
pMaster = NULL;
}
}
*ppAS = pMaster;
DebugExitHRESULT(CreateASObject, hr);
return hr;
}
ASMaster::ASMaster(UINT flags, IAppSharingNotify * pNotify) :
m_cRefs (1),
m_pNotify (pNotify)
{
DebugEntry(ASMaster::ASMaster);
if (m_pNotify)
{
m_pNotify->AddRef();
}
ASSERT(!g_pMaster);
g_pMaster = this;
//
// Set up global flags:
// * service
// * unattended
//
g_asOptions = flags;
DebugExitVOID(ASMaster::ASMaster);
}
ASMaster::~ASMaster()
{
DebugEntry(ASMaster::~ASMaster);
//
// Kill any share that's current or pending in the queue
// This will do nothing if no share is extant at the time the
// message is received.
//
if (g_asMainWindow)
{
PostMessage(g_asMainWindow, DCS_KILLSHARE_MSG, 0, 0);
}
//
// Kill off the worker thread
//
if (g_asMainThreadId)
{
PostThreadMessage(g_asMainThreadId, WM_QUIT, 0, 0);
}
//
// Clean up the UI
//
if (g_putUI)
{
UT_TermTask(&g_putUI);
}
// global variables cleanup
if (m_pNotify)
{
m_pNotify->Release();
m_pNotify = NULL;
}
if (g_pMaster == this)
{
g_pMaster = NULL;
}
DebugExitVOID(ASMaster::~ASMaster);
}
STDMETHODIMP ASMaster::QueryInterface(REFIID iid, void ** pv)
{
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) ASMaster::AddRef()
{
InterlockedIncrement(&m_cRefs);
return m_cRefs;
}
STDMETHODIMP_(ULONG) ASMaster::Release()
{
ASSERT(m_cRefs > 0);
if (::InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
return 0;
}
return m_cRefs;
}
//
// WorkThreadEntryPoint()
//
// This is the groupware code--obman, taskloader, and app sharing
//
DWORD WINAPI WorkThreadEntryPoint(LPVOID hEventWait)
{
BOOL result = FALSE;
BOOL fCMGCleanup = FALSE;
BOOL fOMCleanup = FALSE;
BOOL fALCleanup = FALSE;
BOOL fDCSCleanup = FALSE;
MSG msg;
HWND hwndTop;
DebugEntry(WorkThreadEntryPoint);
//
// Get the current thread ID. This is used in the stop code to know
// if the previous thread is still exiting. In the run-when-windows
// starts mode, our init code is called when Conf brings up UI and our
// term code is called when Conf brings it down. We have a race condition
// because this thread is created on each init. If we create a new
// one while the old one is exiting, we will stomp over each other and
// GP-fault.
//
g_asMainThreadId = GetCurrentThreadId();
//
// Get our policies
//
g_asPolicies = 0;
if (g_asOptions & AS_SERVICE)
{
//
// No old whiteboard, no how, for RDS
//
g_asPolicies |= SHP_POLICY_NOOLDWHITEBOARD;
}
else
{
RegEntry rePol(POLICIES_KEY, HKEY_CURRENT_USER);
//
// Is old whiteboard disabled?
//
if (rePol.GetNumber(REGVAL_POL_NO_OLDWHITEBOARD, DEFAULT_POL_NO_OLDWHITEBOARD))
{
WARNING_OUT(("Policy disables Old Whiteboard"));
g_asPolicies |= SHP_POLICY_NOOLDWHITEBOARD;
}
//
// Is application sharing disabled completely?
//
if (rePol.GetNumber(REGVAL_POL_NO_APP_SHARING, DEFAULT_POL_NO_APP_SHARING))
{
WARNING_OUT(("Policy disables App Sharing"));
g_asPolicies |= SHP_POLICY_NOAPPSHARING;
}
else
{
//
// Only grab AS policies if AS is even allowed
//
if (rePol.GetNumber(REGVAL_POL_NO_SHARING, DEFAULT_POL_NO_SHARING))
{
WARNING_OUT(("Policy prevents user from sharing"));
g_asPolicies |= SHP_POLICY_NOSHARING;
}
if (rePol.GetNumber(REGVAL_POL_NO_MSDOS_SHARING, DEFAULT_POL_NO_MSDOS_SHARING))
{
WARNING_OUT(("Policy prevents user from sharing command prompt"));
g_asPolicies |= SHP_POLICY_NODOSBOXSHARE;
}
if (rePol.GetNumber(REGVAL_POL_NO_EXPLORER_SHARING, DEFAULT_POL_NO_EXPLORER_SHARING))
{
WARNING_OUT(("Policy prevents user from sharing explorer"));
g_asPolicies |= SHP_POLICY_NOEXPLORERSHARE;
}
if (rePol.GetNumber(REGVAL_POL_NO_DESKTOP_SHARING, DEFAULT_POL_NO_DESKTOP_SHARING))
{
WARNING_OUT(("Policy prevents user from sharing desktop"));
g_asPolicies |= SHP_POLICY_NODESKTOPSHARE;
}
if (rePol.GetNumber(REGVAL_POL_NO_TRUECOLOR_SHARING, DEFAULT_POL_NO_TRUECOLOR_SHARING))
{
WARNING_OUT(("Policy prevents user from sharing in true color"));
g_asPolicies |= SHP_POLICY_NOTRUECOLOR;
}
if (rePol.GetNumber(REGVAL_POL_NO_ALLOW_CONTROL, DEFAULT_POL_NO_ALLOW_CONTROL))
{
WARNING_OUT(("Policy prevents user from letting others control"));
g_asPolicies |= SHP_POLICY_NOCONTROL;
}
}
}
// Register the call primary code, for T.120 GCC
if (!CMP_Init(&fCMGCleanup))
{
ERROR_OUT(("CMP_Init failed"));
DC_QUIT;
}
if (!(g_asPolicies & SHP_POLICY_NOOLDWHITEBOARD))
{
if (!OMP_Init(&fOMCleanup))
{
ERROR_OUT(("Couldn't start ObMan"));
DC_QUIT;
}
if (!ALP_Init(&fALCleanup))
{
ERROR_OUT(("Couldn't start AppLoader"));
DC_QUIT;
}
}
//
// Do DCS fast init; slow font enum will happen later off a posted
// message. We can still share & participate in sharing without a
// full font list...
//
if (!(g_asPolicies & SHP_POLICY_NOAPPSHARING))
{
fDCSCleanup = TRUE;
if (!DCS_Init())
{
ERROR_OUT(("AS did not initialize"));
DC_QUIT;
}
}
//
// We've successfully initialised - let the thread which created this
// one continue
//
SetEvent((HANDLE)hEventWait);
//
// Enter the main message processing loop:
//
while (GetMessage(&msg, NULL, 0, 0))
{
//
// For dialogs, it's OK to do normal message processing.
//
if (hwndTop = IsForDialog(msg.hwnd))
{
if (!IsDialogMessage(hwndTop, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
//
// Note that this message dispatch loop DOES NOT include a call to
// Translate Message. This is because we do not want it to call
// ToAscii and affect the state maintained internally by ToAscii.
// We will call ToAscii ourselves in the IM when the user is typing
// in a view and calling it more than once for a keystroke
// will cause it to return wrong results (eg for dead keys).
//
// The consequence of this is that any windows which are driven by
// this dispatch loop will NOT receive WM_CHAR or WM_SYSCHAR
// messages. This is not a problem for dialog windows belonging to
// a task using this message loop as the dialog will run its own
// dispatch loop.
//
// If it becomes necessary for windows driven by this dispatch loop
// to get their messages translated then we could add logic to
// determine whether the message is destined for a view
// before deciding whether to translate it.
//
//
// Because we don't have a translate message in our message loop we
// need to do the following to ensure the keyboard LEDs follow what
// the user does when their input is going to this message loop.
//
if (((msg.message == WM_KEYDOWN) ||
(msg.message == WM_SYSKEYDOWN) ||
(msg.message == WM_KEYUP) ||
(msg.message == WM_SYSKEYUP)) &&
IM_KEY_IS_TOGGLE(msg.wParam))
{
BYTE kbState[256];
//
// There is a chance the LEDs state has changed so..
//
GetKeyboardState(kbState);
SetKeyboardState(kbState);
}
DispatchMessage(&msg);
}
}
result = (int)msg.wParam;
//
// We emerge from the processing loop when someone posts us a WM_QUIT.
// We do ObMan specific termination then call UT_TermTask (which will
// call any exit procedures we have registered).
//
DC_EXIT_POINT:
if (fDCSCleanup)
DCS_Term();
if (fALCleanup)
ALP_Term();
if (fOMCleanup)
OMP_Term();
if (fCMGCleanup)
CMP_Term();
g_asMainThreadId = 0;
DebugExitDWORD(WorkThreadEntryPoint, result);
return(result);
}
//
// IsForDialog()
// Returns if the message is intended for a window in a dialog. AppSharing
// has the host UI dialog, incoming request dialogs, and possibly
// notification message box dialogs.
//
HWND IsForDialog(HWND hwnd)
{
BOOL rc = FALSE;
HWND hwndParent;
DebugEntry(IsForDialog);
if (!hwnd)
DC_QUIT;
while (GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
{
hwndParent = GetParent(hwnd);
if (hwndParent == GetDesktopWindow())
break;
hwnd = hwndParent;
}
if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_DLGMODALFRAME)
{
// This is a dialog
}
else
{
hwnd = NULL;
}
DC_EXIT_POINT:
DebugExitPTR(IsForDialog, hwnd);
return(hwnd);
}
//
// ASMaster member functions
//
//
//
//
// ASMaster::OnEvent
//
// Parameters: event event type
// param1 other parameter
// param2 other parameter
//
//
BOOL CALLBACK eventProc
(
LPVOID cpiHandle_,
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
BOOL rc;
if (g_pMaster)
{
rc = g_pMaster->OnEvent(event, param1, param2);
}
else
{
WARNING_OUT(("Received ASMaster event %d but no g_pMaster", event));
rc = FALSE;
}
return rc;
}
BOOL ASMaster::OnEvent
(
UINT event,
UINT_PTR param1,
UINT_PTR param2
)
{
BOOL rc = TRUE;
DebugEntry(ASMaster::OnEvent);
if (!m_pNotify)
{
// Nothing to do
rc = FALSE;
DC_QUIT;
}
switch (event)
{
case SH_EVT_APPSHARE_READY:
m_pNotify->OnReadyToShare(param1 != 0);
break;
case SH_EVT_SHARE_STARTED:
m_pNotify->OnShareStarted();
break;
case SH_EVT_SHARING_STARTED:
m_pNotify->OnSharingStarted();
break;
case SH_EVT_SHARE_ENDED:
m_pNotify->OnShareEnded();
break;
case SH_EVT_PERSON_JOINED:
m_pNotify->OnPersonJoined((IAS_GCC_ID)param1);
break;
case SH_EVT_PERSON_LEFT:
m_pNotify->OnPersonLeft((IAS_GCC_ID)param1);
break;
case SH_EVT_STARTINCONTROL:
m_pNotify->OnStartInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_STOPINCONTROL:
m_pNotify->OnStopInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_PAUSEDINCONTROL:
m_pNotify->OnPausedInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_UNPAUSEDINCONTROL:
m_pNotify->OnUnpausedInControl((IAS_GCC_ID)param1);
break;
case SH_EVT_CONTROLLABLE:
m_pNotify->OnControllable(param1 != 0);
break;
case SH_EVT_STARTCONTROLLED:
m_pNotify->OnStartControlled((IAS_GCC_ID)param1);
break;
case SH_EVT_STOPCONTROLLED:
m_pNotify->OnStopControlled((IAS_GCC_ID)param1);
break;
case SH_EVT_PAUSEDCONTROLLED:
m_pNotify->OnPausedControlled((IAS_GCC_ID)param1);
break;
case SH_EVT_UNPAUSEDCONTROLLED:
m_pNotify->OnUnpausedControlled((IAS_GCC_ID)param1);
break;
default:
// Unrecognized, unhandled event
rc = FALSE;
break;
}
DC_EXIT_POINT:
DebugExitBOOL(ASMaster::OnEvent, rc);
return(rc);
}
//
// ASMaster::IsSharingAvailable()
//
STDMETHODIMP_(BOOL) ASMaster::IsSharingAvailable()
{
return(g_asSession.hwndHostUI != NULL);
}
//
// ASMaster::CanShareNow()
//
STDMETHODIMP_(BOOL) ASMaster::CanShareNow()
{
BOOL rc = FALSE;
UT_Lock(UTLOCK_AS);
//
// We can share if
// * We can capture graphic output on this OS
// * We're in a call
//
if (g_asSession.hwndHostUI &&
g_asSession.callID &&
(g_asSession.attendeePermissions & NM_PERMIT_SHARE) &&
(g_s20State >= S20_NO_SHARE))
{
rc = TRUE;
}
UT_Unlock(UTLOCK_AS);
return(rc);
}
//
// ASMaster::InInShare()
//
STDMETHODIMP_(BOOL) ASMaster::IsInShare()
{
return(g_asSession.pShare != NULL);
}
//
// ASMaster::IsSharing()
//
STDMETHODIMP_(BOOL) ASMaster::IsSharing()
{
IAS_PERSON_STATUS personStatus;
::ZeroMemory(&personStatus, sizeof(personStatus));
personStatus.cbSize = sizeof(personStatus);
GetPersonStatus(0, &personStatus);
return(personStatus.AreSharing != 0);
}
//
// CanAllowControl()
// We can allow control if we're sharing and it's not prevented by policy
//
STDMETHODIMP_(BOOL) ASMaster::CanAllowControl(void)
{
if (g_asPolicies & SHP_POLICY_NOCONTROL)
return(FALSE);
return(IsSharing());
}
//
// IsControllable()
// We are controllable if our state isn't detached.
//
STDMETHODIMP_(BOOL) ASMaster::IsControllable(void)
{
IAS_PERSON_STATUS personStatus;
::ZeroMemory(&personStatus, sizeof(personStatus));
personStatus.cbSize = sizeof(personStatus);
GetPersonStatus(0, &personStatus);
return(personStatus.Controllable != 0);
}
//
// GetPersonStatus()
//
STDMETHODIMP ASMaster::GetPersonStatus(IAS_GCC_ID Person, IAS_PERSON_STATUS * pStatus)
{
return(::SHP_GetPersonStatus(Person, pStatus));
}
//
// ASMaster::IsWindowShareable()
//
STDMETHODIMP_(BOOL) ASMaster::IsWindowShareable(HWND hwnd)
{
return(CanShareNow() && HET_IsWindowShareable(hwnd));
}
//
// ASMaster::IsWindowShared()
//
STDMETHODIMP_(BOOL) ASMaster::IsWindowShared(HWND hwnd)
{
return(HET_IsWindowShared(hwnd));
}
//
//
// ASMaster::Share
//
// Parameters: HWND of the window to share. This can be any (valid) HWND.
//
//
STDMETHODIMP ASMaster::Share(HWND hwnd, IAS_SHARE_TYPE uAppType)
{
HRESULT hr;
DebugEntry(ASMaster::Share);
hr = E_FAIL;
if (!CanShareNow())
{
WARNING_OUT(("Share failing; can't share now"));
DC_QUIT;
}
//
// If this is the desktop, check for a policy against just it.
//
if (hwnd == ::GetDesktopWindow())
{
if (g_asPolicies & SHP_POLICY_NODESKTOPSHARE)
{
WARNING_OUT(("Sharing desktop failing; prevented by policy"));
DC_QUIT;
}
}
switch (uAppType)
{
case IAS_SHARE_DEFAULT:
case IAS_SHARE_BYPROCESS:
case IAS_SHARE_BYTHREAD:
case IAS_SHARE_BYWINDOW:
break;
default:
{
ERROR_OUT(("IAppSharing::Share - invalid share type %d", uAppType));
return E_INVALIDARG;
}
}
if (SHP_Share(hwnd, uAppType))
{
hr = S_OK;
}
DC_EXIT_POINT:
DebugExitHRESULT(ASMaster::Share, hr);
return hr;
}
//
//
// ASMaster::Unshare
//
// Parameters: HWND of the window to unshare
//
//
STDMETHODIMP ASMaster::Unshare(HWND hwnd)
{
return(::SHP_Unshare(hwnd));
}
//
//
// ASMaster::LaunchHostUI()
//
//
STDMETHODIMP ASMaster::LaunchHostUI(void)
{
return(SHP_LaunchHostUI());
}
//
//
// ASMaster::GetShareableApps
//
// Generates a list of HWND's into <validAppList>
// These objects are allocated dynamically, so must be deleted by the
// caller.
//
//
STDMETHODIMP ASMaster::GetShareableApps(IAS_HWND_ARRAY **ppHwnds)
{
if (!CanShareNow())
return(E_FAIL);
return(HET_GetAppsList(ppHwnds) ? S_OK : E_FAIL);
}
STDMETHODIMP ASMaster::FreeShareableApps(IAS_HWND_ARRAY * pMemory)
{
HET_FreeAppsList(pMemory);
return S_OK;
}
//
// TakeControl()
//
// From viewer to host, asking to take control of host.
//
STDMETHODIMP ASMaster::TakeControl(IAS_GCC_ID PersonOf)
{
return(SHP_TakeControl(PersonOf));
}
//
// CancelTakeControl()
//
// From viewer to host, to cancel pending TakeControl request.
//
STDMETHODIMP ASMaster::CancelTakeControl(IAS_GCC_ID PersonOf)
{
return(SHP_CancelTakeControl(PersonOf));
}
//
// ReleaseControl()
//
// From viewer to host, telling host that viewer is not in control of host
// anymore.
//
STDMETHODIMP ASMaster::ReleaseControl(IAS_GCC_ID PersonOf)
{
return(SHP_ReleaseControl(PersonOf));
}
//
// PassControl()
//
// From viewer to host, when viewer is in control of host, asking to pass
// control of host to a different viewer.
STDMETHODIMP ASMaster::PassControl(IAS_GCC_ID PersonOf, IAS_GCC_ID PersonTo)
{
return(SHP_PassControl(PersonOf, PersonTo));
}
//
// AllowControl()
//
// On host side, to allow/stop allowing control at all of shared apps/desktop.
// When one starts to host, allowing control always starts as off. So
// turning on allowing control, stopping sharing, then sharing something
// else will not leave host vulnerable.
//
// When turning it off, if a viewer was in control of the host, kill control
// from the host to the viewer will occur first.
//
// The "ESC" key is an accelerator to stop allowing control, when pressed
// by the user on the host who is currently controlled.
//
STDMETHODIMP ASMaster::AllowControl(BOOL fAllow)
{
return(::SHP_AllowControl(fAllow));
}
//
// GiveControl()
//
// From host to viewer, inviting the viewer to take control of the host.
// It's the inverse of TakeControl.
//
STDMETHODIMP ASMaster::GiveControl(IAS_GCC_ID PersonTo)
{
return(SHP_GiveControl(PersonTo));
}
//
// CancelGiveControl()
//
// From host to viewer, to cancel pending GiveControl request
//
STDMETHODIMP ASMaster::CancelGiveControl(IAS_GCC_ID PersonTo)
{
return(SHP_CancelGiveControl(PersonTo));
}
//
// RevokeControl()
//
// From host to viewer, when host wishes to stop viewer from controlling him.
// AllowControl is still on, for another to possibly take control of the host.
//
// Mouse clicks and key presses other than "ESC" by the user on the controlled
// host host areaccelerators to kill control.
//
STDMETHODIMP ASMaster::RevokeControl(IAS_GCC_ID PersonTo)
{
return(SHP_RevokeControl(PersonTo));
}
//
// PauseControl()
//
// On host, to temporarily allow local user to do stuff without breaking
// control bond. We put the viewer on hold.
//
STDMETHODIMP ASMaster::PauseControl(IAS_GCC_ID PersonInControl)
{
return(SHP_PauseControl(PersonInControl, TRUE));
}
//
// UnpauseControl()
//
// On host, to unpause control that has been paused. We take the viewer
// off hold.
//
STDMETHODIMP ASMaster::UnpauseControl(IAS_GCC_ID PersonInControl)
{
return(SHP_PauseControl(PersonInControl, FALSE));
}
//
// StartStopOldWB
//
extern "C"
{
BOOL WINAPI StartStopOldWB(LPCTSTR szFile)
{
LPTSTR szCopyOfFile;
ValidateUTClient(g_putUI);
if (g_asPolicies & SHP_POLICY_NOOLDWHITEBOARD)
{
WARNING_OUT(("Not launching old whiteboard; prevented by policy"));
return(FALSE);
}
//
// Because we're posting a message effectively, we have to make a
// copy of the string. If we ever have "SendEvent", we won't have
// that problem anymore.
//
if (szFile)
{
int cchLength;
BOOL fSkippedQuote;
// Skip past first quote
if (fSkippedQuote = (*szFile == '"'))
szFile++;
cchLength = lstrlen(szFile);
szCopyOfFile = (LPTSTR)::GlobalAlloc(GPTR, (cchLength+1)*sizeof(TCHAR));
if (!szCopyOfFile)
{
ERROR_OUT(("Can't make file name copy for whiteboard launch"));
return(FALSE);
}
lstrcpy(szCopyOfFile, szFile);
//
// NOTE:
// There may be DBCS implications with this. Hence we check to see
// if we skipped the first quote; we assume that if the file name
// starts with a quote it must end with one also. But we need to check
// it out.
//
// Strip last quote
if (fSkippedQuote && (cchLength > 0) && (szCopyOfFile[cchLength - 1] == '"'))
{
TRACE_OUT(("Skipping last quote in file name %s", szCopyOfFile));
szCopyOfFile[cchLength - 1] = '\0';
}
}
else
{
szCopyOfFile = NULL;
}
UT_PostEvent(g_putUI, g_putAL, NO_DELAY,
AL_INT_STARTSTOP_WB, 0, (UINT_PTR)szCopyOfFile);
return(TRUE);
}
}