#include "precomp.h" #include #include #include #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 // 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); } }