Skip to content

Commit

Permalink
version 2.7.0 (see changelog)
Browse files Browse the repository at this point in the history
  • Loading branch information
cedrozor committed Oct 31, 2019
1 parent 3191e48 commit 2823f43
Show file tree
Hide file tree
Showing 121 changed files with 3,469 additions and 1,101 deletions.
38 changes: 37 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,42 @@
2019-09-23 Version 2.5.5 (patched)
2019-10-30 Version 2.7.0 (stable)
* HOTFIX * fixed a critical issue with FreeRDP (exit code 131085) when using an RD license server configured in "per device" mode, past the 120 days of the RDS grace period
updated readme and documentation about the RDS role
the browser "heartbeat" (used to detect if the browser window/tab was closed) is now on a different timer than the periodical fullscreen update (config.js; default 10 secs)
disabled the alt key capture because it may interfere with the browser alt+key menu
keys following the alt+gr modifier are now sent as scancode instead of unicode
ability to configure the login url (by default the myrtille login page; web.config)
updated the installer for a better display of prerequisites
added the option into the installer to choose either to pass the http session ID into url or store it into the default "ASP.NET_SessionId" cookie (the former allows multiple connections/tabs or iframes)
the connection API is now out of beta and installed by default (full auto-connect/start program from url syntax is still available)
the scale feature now keeps the aspect ratio on browser resize
fixed a display refresh issue under chrome + https if the clipboard is empty
increased the image cache duration (1 sec -> 3 secs)
halved the input buffer duration (based on the roundtrip duration)
updated display tweaking
improved cleanup on session disconnect into wfreerdp to prevent memory leaks with GDI+
improved cleanup on session disconnect into Myrtille to prevent memory leaks on the application pool
removed any unnecessary memory allocation and released any disposable object on the application pool
the application pool is now automatically recycled when there is no active remote session (configurable into web.config)
fixed an issue related to the disconnect callback (which was sometimes not received by the gateway due a WCF deadlock)
scripts and styles now have computed hashes to prevent browser caching (in case content is changed); they could also be minified, as needed (see comments in BundleConfig.cs)
re-enabled multiple connections/tabs (web.config, read comments about security)

2019-09-02 Version 2.6.0 (stable)
resynced FreeRDP (2.0.0-dev5)
added a REST API to disconnect a given remote session or all of them (on a gateway)
fixed an issue with some network or domain configuration that could delay the start of a connection
support of Connection Broker database, in High Availability mode
support of RDS API
configurable drain of disconnected sessions
seamless clipboard synchronization, using the async clipboard API (requires Chrome and HTTPS) with fallback to standard web API (for other browsers and HTTP)
now hiding iframe content on browser resize
various mockup improvements
get focus back on main window after a popup is closed
screenshot is now returned as HttpResponseMessage, byte array content
clipboard virtual channel comments and check
the self-signed certificate (installed by myrtille on install) is now issued with the machine FQDN to prevent ERR_CERT_AUTHORITY_INVALID (Subject Alternative Name Missing) in recent Chrome releases
now using a web worker for the periodical fullscreen updates to keep them going even if the main thread is paused (due to inactive window/tab, after focus lost for example)
some refactoring

2019-06-01 Version 2.5.5 (stable)
added audio support (MP3 (default)/WAV, config.js)
Expand Down
24 changes: 24 additions & 0 deletions DISCLAIMERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,30 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

## Cassia

MIT License

Copyright (c) 2008 - 2017 Dan Ports

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

## NAudio

Microsoft Public License (Ms-PL)
Expand Down
5 changes: 5 additions & 0 deletions DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ See the Myrtille.Admin.Web **mockup** to see how to implement Myrtille into your

- in HTML4 mode, data is sent to Myrtille using xhrs; these are limited in size (depends on browser and IIS config), so sending a large block of text with the "keyboard" popup may fail in such a case

- the clipboard synchronization requires Chrome (or async clipboard API support) and HTTPS connection and is limited to text only and 1MB max

## Troubleshoot
First at all, ensure the Myrtille prerequisites are met (IIS 7 or greater (preferably IIS 8+ with websocket protocol enabled) and .NET 4.5+). Note that IIS must be installed separately, before running the installer (see "Installation").

Expand Down Expand Up @@ -349,6 +351,9 @@ Also please read notes and limitations above.
- Check the gateway windows event logs, particulary regarding .NET.
- Retry with debug enabled and check logs (into the "log" folder). You can change their verbosity level in config (but be warned it will affect peformance and flood the disk if set too verbose).

- Reconnection on browser resize (toolbar option) doesn't work
- this feature requires the RDS disconnection timeout to be set and greater than 5 secs (to give FreeRDP time to reconnect)

- Some characters/keys are not working as expected
- Keyboard is mapped to english/US layout by default. Try to add that layout to your server (and select it when connected).

Expand Down
2 changes: 1 addition & 1 deletion Myrtille.Admin.Services/MyrtilleApiHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static class MyrtilleApiHost

public static void Start()
{
var url = "http://*:" + Settings.Default.WebApiPort + "/Myrtille/";
var url = "http://*:" + Settings.Default.WebApiPort + "/MyrtilleAdmin/";
Console.WriteLine($"{DateTime.UtcNow} - Starting Myrtille Admin API at url: " + url);

try
Expand Down
4 changes: 2 additions & 2 deletions Myrtille.Admin.Services/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.5.0")]
[assembly: AssemblyFileVersion("2.5.5.0")]
[assembly: AssemblyVersion("2.7.0.0")]
[assembly: AssemblyFileVersion("2.7.0.0")]
16 changes: 9 additions & 7 deletions Myrtille.Admin.Services/Services/ConnectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Guid GetConnectionId(ConnectionInfo connectionInfo)
connections.Add(connection.Id, connection);
}

Trace.TraceInformation("new connection: {0}, domain={1}, user={2}, host={3}, vm={4}", connection.Id, connection.Info.User.Domain, connection.Info.User.UserName, connection.Info.Host.IPAddress, connection.Info.VM != null ? connection.Info.VM.Guid.ToString() : string.Empty);
Trace.TraceInformation("GetConnectionId: {0}, domain={1}, user={2}, host={3}, vm={4}", connection.Id, connection.Info.User.Domain, connection.Info.User.UserName, connection.Info.Host.IPAddress, connection.Info.VM != null ? connection.Info.VM.Guid.ToString() : string.Empty);

return connection.Id;
}
Expand All @@ -61,12 +61,12 @@ public ConnectionInfo GetConnectionInfo(Guid connectionId)
{
if (connection.InfoAccessed)
{
Trace.TraceWarning("connection: {0}, info was already accessed, access denied", connection.Id);
Trace.TraceWarning("GetConnectionInfo: {0}, info was already accessed, access denied", connection.Id);
return null;
}

connection.InfoAccessed = true;
Trace.TraceInformation("connection: {0}, domain={1}, user={2}, host={3}, vm={4}", connection.Id, connection.Info.User.Domain, connection.Info.User.UserName, connection.Info.Host.IPAddress, connection.Info.VM != null ? connection.Info.VM.Guid.ToString() : string.Empty);
Trace.TraceInformation("GetConnectionInfo: {0}, domain={1}, user={2}, host={3}, vm={4}", connection.Id, connection.Info.User.Domain, connection.Info.User.UserName, connection.Info.Host.IPAddress, connection.Info.VM != null ? connection.Info.VM.Guid.ToString() : string.Empty);
return connection.Info;
}
else
Expand All @@ -78,13 +78,15 @@ public ConnectionInfo GetConnectionInfo(Guid connectionId)

public bool IsUserAllowedToConnectHost(string domain, string userName, string hostIPAddress, Guid VMGuid)
{
// this method is just an empty shell
// have your own implementation to allow or deny user access to a given host
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(hostIPAddress))
{
Trace.TraceInformation("access granted: domain={0}, user={1}, host={2}, vm={3}", domain, userName, hostIPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty);
Trace.TraceInformation("IsUserAllowedToConnectHost, access granted: domain={0}, user={1}, host={2}, vm={3}", domain, userName, hostIPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty);
return true;
}

Trace.TraceInformation("access denied: domain={0}, user={1}, host={2}, vm={3}", domain, userName, hostIPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty);
Trace.TraceInformation("IsUserAllowedToConnectHost, access denied: domain={0}, user={1}, host={2}, vm={3}", domain, userName, hostIPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty);
return false;
}

Expand All @@ -93,7 +95,7 @@ public bool SetConnectionState(Guid connectionId, string IPAddress, Guid VMGuid,
var connection = connections[connectionId] as Connection;
if (connection != null)
{
Trace.TraceInformation("connection: {0}, ip={1}, vm={2}, state={3}", connectionId, IPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty, state);
Trace.TraceInformation("SetConnectionState: {0}, ip={1}, vm={2}, state={3}", connectionId, IPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty, state);
connection.State = state;
return true;
}
Expand All @@ -107,7 +109,7 @@ public bool SetConnectionExitCode(Guid connectionId, string IPAddress, Guid VMGu
var connection = connections[connectionId] as Connection;
if (connection != null)
{
Trace.TraceInformation("connection: {0}, ip={1}, vm={2}, exit code={3}", connectionId, IPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty, exitCode);
Trace.TraceInformation("SetConnectionExitCode: {0}, ip={1}, vm={2}, exit code={3}", connectionId, IPAddress, VMGuid != Guid.Empty ? VMGuid.ToString() : string.Empty, exitCode);
connection.ExitCode = exitCode;
return true;
}
Expand Down
10 changes: 1 addition & 9 deletions Myrtille.Admin.Services/ServicesInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private void InitializeComponent()
this.serviceInstaller = new ServiceInstaller();
this.serviceInstaller.ServiceName = "Myrtille.Admin.Services";
this.serviceInstaller.Description = "Myrtille Admin API";
this.serviceInstaller.StartType = ServiceStartMode.Manual;
this.serviceInstaller.StartType = ServiceStartMode.Automatic;

this.Installers.AddRange(new Installer[] {
this.serviceProcessInstaller,
Expand Down Expand Up @@ -156,10 +156,6 @@ public override void Uninstall(

private void StartService()
{
// the connection api wasn't requested
if (string.IsNullOrEmpty(Context.Parameters["CONNECTIONAPI"]))
return;

Context.LogMessage("Starting Myrtille.Admin.Services");

// try to start the service
Expand Down Expand Up @@ -193,10 +189,6 @@ private void StartService()

private void StopService()
{
// the connection api wasn't requested
if (string.IsNullOrEmpty(Context.Parameters["CONNECTIONAPI"]))
return;

Context.LogMessage("Stopping Myrtille.Admin.Services");

// if the service is running while uninstall is going on, the user is asked wether to stop it or not
Expand Down
41 changes: 29 additions & 12 deletions Myrtille.Admin.Web/Default.aspx
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,49 @@
<div align="left">
<input type="hidden" runat="server" id="_guestId" />
<input type="hidden" runat="server" id="_allowControl" />
<input type="button" runat="server" id="AddGuestButton" value="Add a guest" data-fid="myrtille_1" onclick="_allowControl.value = prompt('Grant control to guest?', 'true');" onserverclick="AddGuestButtonClick"/><br />
<input type="button" runat="server" id="GetGuestsButton" value="Show guests list" data-fid="myrtille_1" onserverclick="GetGuestsButtonClick"/><br />
<input type="button" runat="server" id="GetGuestButton" value="Show guest info" onclick="_guestId.value = prompt('Enter guest id', 'guid');" onserverclick="GetGuestButtonClick"/><br />
<input type="button" runat="server" id="UpdateGuestButton" value="Update a guest" onclick="_guestId.value = prompt('Enter guest id', 'guid'); if (_guestId.value) { _allowControl.value = prompt('Grant control to guest?', 'true'); }" onserverclick="UpdateGuestButtonClick"/><br />
<input type="button" runat="server" id="RemoveGuestButton" value="Remove a guest" onclick="_guestId.value = prompt('Enter guest id', 'guid');" onserverclick="RemoveGuestButtonClick"/><br />
<input type="button" runat="server" id="AddGuestButton" value="Add a guest" data-fid="myrtille_1" onclick="_allowControl.value = prompt('Grant control to guest?', 'true');" onserverclick="AddGuestButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="GetGuestsButton" value="Show guests list" data-fid="myrtille_1" onserverclick="GetGuestsButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="GetGuestButton" value="Show guest info" data-fid="myrtille_1" onclick="_guestId.value = prompt('Enter guest id', 'guid');" onserverclick="GetGuestButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="UpdateGuestButton" value="Update a guest" data-fid="myrtille_1" onclick="_guestId.value = prompt('Enter guest id', 'guid'); if (_guestId.value) { _allowControl.value = prompt('Grant control to guest?', 'true'); }" onserverclick="UpdateGuestButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="RemoveGuestButton" value="Remove a guest" data-fid="myrtille_1" onclick="_guestId.value = prompt('Enter guest id', 'guid');" onserverclick="RemoveGuestButtonClick" disabled="disabled"/><br />
</div>

<br />

<div align="left">
<!-- relative iframe size: session is reconnected on browser resize (adjusting to screen) -->
<iframe runat="server" id="myrtille_1" name="myrtille_1"></iframe><br />
<div class="iframeOverlayOuter">
<canvas id="myrtille_1_overlay" class="iframeOverlayInner" width="0" height="0"></canvas>
<!-- relative iframe size: session is reconnected on browser resize (adjusting to screen) -->
<iframe runat="server" id="myrtille_1" name="myrtille_1"></iframe><br />
</div>
<span>^ relative iframe size (resized on browser resize)</span><br />
<input type="button" runat="server" id="SetScreenshotConfigButton" value="screenshot config (click first)" data-fid="myrtille_1" onserverclick="SetScreenshotConfigButtonClick"/><br />&nbsp;Screenshots will be saved into C:\path\to\screenshots on this machine<br />
<input type="button" runat="server" id="StartTakingScreenshotsButton" value="start taking screenshots" data-fid="myrtille_1" onserverclick="StartTakingScreenshotsButtonClick"/><br />
<input type="button" runat="server" id="StopTakingScreenshotsButton" value="stop taking screenshots" data-fid="myrtille_1" onserverclick="StopTakingScreenshotsButtonClick"/><br />
<input type="button" runat="server" id="TakeScreenshotButton" value="take screenshot" data-fid="myrtille_1" onserverclick="TakeScreenshotButtonClick"/>
<input type="button" runat="server" id="SetScreenshotConfigButton" value="screenshot config (click first)" data-fid="myrtille_1" onserverclick="SetScreenshotConfigButtonClick" disabled="disabled"/><br />&nbsp;Screenshots will be saved into C:\path\to\screenshots on this machine<br />
<input type="button" runat="server" id="StartTakingScreenshotsButton" value="start taking screenshots" data-fid="myrtille_1" onserverclick="StartTakingScreenshotsButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="StopTakingScreenshotsButton" value="stop taking screenshots" data-fid="myrtille_1" onserverclick="StopTakingScreenshotsButtonClick" disabled="disabled"/><br />
<input type="button" runat="server" id="TakeScreenshotButton" value="take screenshot" data-fid="myrtille_1" onserverclick="TakeScreenshotButtonClick" disabled="disabled"/>
</div>

<div align="left">
<input type="button" runat="server" id="myrtille_1_disconnect" value="Disconnect" data-fid="myrtille_1" onserverclick="DisconnectButtonClick" disabled="disabled"/>
</div>

<hr/>

<div align="left">
<!-- fixed iframe size -->
<!-- fixed iframe size (no need for an overlay) -->
<iframe runat="server" id="myrtille_2" name="myrtille_2"></iframe><br />
<span>^ fixed iframe size</span>
</div>

<div align="left">
<input type="button" runat="server" id="myrtille_2_disconnect" value="Disconnect" data-fid="myrtille_2" onserverclick="DisconnectButtonClick" disabled="disabled"/>
</div>

<hr/>

<div align="left">
<input type="button" runat="server" id="Logout" value="Logout" onserverclick="LogoutButtonClick" disabled="disabled"/>
</div>

</form>

Expand Down
Loading

0 comments on commit 2823f43

Please sign in to comment.