SDA-3565 Citrix media redirection presence update (#1339)

This commit is contained in:
Salah Benmoussati 2022-02-03 09:10:09 +01:00 committed by GitHub
parent bf86783621
commit e028ac0efa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 182 additions and 35 deletions

View File

@ -61,11 +61,13 @@ class Script
new Dir(@"%ProgramFiles%\" + productName,
new File(new Id("symphony_exe"), @"..\..\..\dist\win-unpacked\Symphony.exe",
// Create two shortcuts to the main Symphony.exe file, one on the desktop and one in the program menu
new FileShortcut(productName, @"%Desktop%") {
new FileShortcut(productName, @"%Desktop%")
{
IconFile = @"..\..\..\images\icon.ico",
Arguments = userDataPathArgument
},
new FileShortcut(productName, @"%ProgramMenu%") {
new FileShortcut(productName, @"%ProgramMenu%")
{
IconFile = @"..\..\..\images\icon.ico",
Arguments = userDataPathArgument
}
@ -304,7 +306,7 @@ class Script
if (e.IsInstalling || e.IsUpgrading)
{
// "ALLUSERS" will be set to "2" if installing through UI, so the "MSIINSTALLPERUSER" property can be used so the user can choose install scope
if (e.Session["ALLUSERS"] != "2" )
if (e.Session["ALLUSERS"] != "2")
{
// If "ALLUSERS" is "1" or "", this is a quiet command line installation, and we need to set the right paths here, since the UI haven't.
@ -328,10 +330,11 @@ class Script
// Try to close all running symphony instances before installing. Since we have started using the EndSession message to tell the app to exit,
// we don't really need to force terminate anymore. But the older versions of SDA does not listen for the EndSession event, so we still need
// this code to ensure older versions gets shut down properly.
System.Diagnostics.Process.GetProcessesByName("Symphony").ForEach(p => {
if( System.IO.Path.GetFileName(p.MainModule.FileName) =="Symphony.exe")
System.Diagnostics.Process.GetProcessesByName("Symphony").ForEach(p =>
{
if( !p.HasExited )
if (System.IO.Path.GetFileName(p.MainModule.FileName) == "Symphony.exe")
{
if (!p.HasExited)
{
p.Kill();
p.WaitForExit();
@ -345,13 +348,14 @@ class Script
// We always seem to get this specific exception triggered, but the application still close down correctly.
// The exception description is "Only part of a ReadProcessMemory or WriteProcessMemory request was completed".
// We ignore that specific exception, so as not to put false error outputs into the log.
if (ex.NativeErrorCode != 299) {
e.Session.Log("Error trying to close all Symphony instances: " + ex.ToString() );
if (ex.NativeErrorCode != 299)
{
e.Session.Log("Error trying to close all Symphony instances: " + ex.ToString());
}
}
catch (System.Exception ex)
{
e.Session.Log("Error trying to close all Symphony instances: " + ex.ToString() );
e.Session.Log("Error trying to close all Symphony instances: " + ex.ToString());
}
}
}
@ -377,7 +381,7 @@ public class CustomActions
}
catch (System.Exception e)
{
session.Log("Error executing InstallVariant: " + e.ToString() );
session.Log("Error executing InstallVariant: " + e.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
@ -420,7 +424,7 @@ public class CustomActions
}
catch (System.Exception e)
{
session.Log("Error executing UpdateConfig: " + e.ToString() );
session.Log("Error executing UpdateConfig: " + e.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;
@ -432,7 +436,7 @@ public class CustomActions
// `value` is the value to insert for the setting, and needs to be grabbed from the propery
// collection before calling the function. The function returns the full config file content with
// the requested replacement performed.
static string ReplaceProperty( string data, string name, string value )
static string ReplaceProperty(string data, string name, string value)
{
// Using regular expressions to replace the existing value in the config file with the
// one from the property. This is the same as the regex we used to have in the old
@ -447,7 +451,7 @@ public class CustomActions
// `value` is the value to insert for the setting, and needs to be grabbed from the propery
// collection before calling the function. The function returns the full config file content with
// the requested replacement performed.
static string ReplaceBooleanProperty( string data, string name, string value )
static string ReplaceBooleanProperty(string data, string name, string value)
{
// Using regular expressions to replace the existing value in the config file with the
// one from the property. This is the same as the regex we used to have in the old
@ -460,7 +464,7 @@ public class CustomActions
// and will throw an error for invalid escape codes. To make a path valid for parsing, we need
// to replace each backslash with doubli backslash. After SDA have parsed the JSON, it will make
// the double backslash become single backslash again.
static string FixPathFormat( string path )
static string FixPathFormat(string path)
{
return path.Replace(@"\", @"\\");
}
@ -474,7 +478,7 @@ public class CustomActions
{
// Remove registry keys added for auto-launch
using( var key = Registry.Users.OpenSubKey(@"\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Run", true) )
using (var key = Registry.Users.OpenSubKey(@"\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Run", true))
{
if (key != null)
{
@ -485,14 +489,14 @@ public class CustomActions
// Remove registry keys added by protocol handlers
using( var key = Registry.LocalMachine.OpenSubKey(@"Software\Classes", true) )
using (var key = Registry.LocalMachine.OpenSubKey(@"Software\Classes", true))
{
if (key != null)
{
key.DeleteSubKeyTree("symphony", false);
}
}
using( var key = Registry.ClassesRoot.OpenSubKey(@"\", true) )
using (var key = Registry.ClassesRoot.OpenSubKey(@"\", true))
{
if (key != null)
{
@ -502,7 +506,7 @@ public class CustomActions
}
catch (System.Exception e)
{
session.Log("Error executing CleanRegistry: " + e.ToString() );
session.Log("Error executing CleanRegistry: " + e.ToString());
return ActionResult.Success;
}
return ActionResult.Success;
@ -516,7 +520,7 @@ public class CustomActions
{
// Remove registry keys added for auto-launch
using( var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true) )
using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true))
{
if (key != null)
{
@ -528,7 +532,7 @@ public class CustomActions
// Remove registry keys added by protocol handlers
using( var key = Registry.CurrentUser.OpenSubKey(@"Software\Classes", true) )
using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Classes", true))
{
if (key != null)
{
@ -538,7 +542,7 @@ public class CustomActions
}
catch (System.Exception e)
{
session.Log("Error executing CleanRegistryCurrentUser: " + e.ToString() );
session.Log("Error executing CleanRegistryCurrentUser: " + e.ToString());
return ActionResult.Success;
}
return ActionResult.Success;
@ -550,11 +554,12 @@ public class CustomActions
{
try
{
if (session.Property("LAUNCH_ON_INSTALL")=="true")
if (session.Property("LAUNCH_ON_INSTALL") == "true")
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = System.IO.Path.Combine(session.Property("INSTALLDIR"), "Symphony.exe");
if( session.Property("USER_DATA_PATH") != "" ) {
if (session.Property("USER_DATA_PATH") != "")
{
process.StartInfo.Arguments = "--userDataPath=\"" + session.Property("USER_DATA_PATH") + "\"";
}
process.Start();
@ -562,7 +567,7 @@ public class CustomActions
}
catch (System.Exception e)
{
session.Log("Error executing StartAfterInstall: " + e.ToString() );
session.Log("Error executing StartAfterInstall: " + e.ToString());
return ActionResult.Failure;
}
return ActionResult.Success;

6
package-lock.json generated
View File

@ -18114,6 +18114,12 @@
"integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=",
"dev": true
},
"winreg": {
"version": "1.2.4",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/winreg/-/winreg-1.2.4.tgz",
"integrity": "sha1-ugZWKbepJRMOFXeRCM9UCZDpjRs=",
"optional": true
},
"word-wrap": {
"version": "1.2.3",
"resolved": "https://repo.symphony.com/artifactory/api/npm/npm-virtual-dev/word-wrap/-/word-wrap-1.2.3.tgz",

View File

@ -179,7 +179,8 @@
"auto-update": "file:auto_update",
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet2.git#9.2.2",
"screen-share-indicator-frame": "git+https://github.com/symphonyoss/ScreenShareIndicatorFrame.git#v1.4.10",
"swift-search": "2.0.3"
"swift-search": "2.0.3",
"winreg": "^1.2.4"
},
"ava": {
"failFast": true,

View File

@ -0,0 +1,69 @@
import {
getCitrixMediaRedirectionStatus,
RedirectionStatus,
} from '../src/app/citrix-handler';
let regKeyValue;
jest.mock('winreg', () => {
return jest.fn().mockImplementation(() => {
return {
get: (_file, callback) => callback(null, regKeyValue),
};
});
});
jest.mock('../src/common/env', () => {
return {
isWindowsOS: true,
isLinux: false,
isMac: false,
isDevEnv: true,
};
});
describe('citrix handler', () => {
beforeEach(() => {
jest.clearAllMocks().resetModules();
});
it('status inactive', async () => {
regKeyValue = null;
const status = await getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.INACTIVE);
});
it('should return supported when having the right registry with value 1', async () => {
regKeyValue = { value: '0x01', type: 'REG_DWORD' };
const status = await getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.SUPPORTED);
});
it('should return unsupported when having the right registry with the wrong registry type', async () => {
regKeyValue = { value: '0x01', type: 'REG_BINARY' };
const status = await getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.UNSUPPORTED);
});
it('should return unsupported when finding the right registry with value 0 ', async () => {
regKeyValue = { value: '0x00', type: 'REG_DWORD' };
const status = await getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.UNSUPPORTED);
});
it('should return inactive on non windows Oses', async () => {
jest.mock('../src/common/env', () => {
return {
isWindowsOS: false,
isLinux: true,
isMac: false,
};
});
const {
getCitrixMediaRedirectionStatus,
RedirectionStatus,
} = require('../src/app/citrix-handler');
const status = await getCitrixMediaRedirectionStatus();
expect(status).toBe(RedirectionStatus.INACTIVE);
});
});

66
src/app/citrix-handler.ts Normal file
View File

@ -0,0 +1,66 @@
import { isWindowsOS } from '../common/env';
import { logger } from '../common/logger';
export enum RedirectionStatus {
/**
* Citrix virtual environment is not active
*/
INACTIVE = 'inactive',
/**
* Citrix virtual environment is active and media redirection is supported
*/
SUPPORTED = 'supported',
/**
* Citrix virtual environment is active but media redirection is not supported
*/
UNSUPPORTED = 'unsupported',
}
export enum RegistryValueType {
REG_DWORD = 'REG_DWORD',
}
const CITRIX_REGISTRY_KEY = '\\Software\\Citrix\\HDXMediaStream';
const CITRIX_REGISTRY_KEY_NAME = 'MSTeamsRedirectionSupport';
export const getCitrixMediaRedirectionStatus = async (): Promise<RedirectionStatus> => {
if (!isWindowsOS) {
// Citrix virtual environments are not supported on non-Windows OSes
return RedirectionStatus.INACTIVE;
}
const Registry = require('winreg');
const regKey = new Registry({
hive: Registry.HKCU,
key: CITRIX_REGISTRY_KEY,
});
return new Promise((resolve, _reject) => {
regKey.get(CITRIX_REGISTRY_KEY_NAME, (err, redirectionSupportItem) => {
logger.info('citrix: ', redirectionSupportItem);
if (err) {
logger.info('citrix-handler: error occurred. Details: ', err);
resolve(RedirectionStatus.INACTIVE);
} else {
if (!redirectionSupportItem) {
resolve(RedirectionStatus.INACTIVE);
}
if (redirectionSupportItem.type === 'REG_DWORD') {
const redirectionSupportValue = parseInt(
redirectionSupportItem.value,
16,
);
if (redirectionSupportValue === 1) {
resolve(RedirectionStatus.SUPPORTED);
} else {
resolve(RedirectionStatus.UNSUPPORTED);
}
} else {
resolve(RedirectionStatus.UNSUPPORTED);
}
}
});
});
};