|
|
#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); } }
|