WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 35  →  ?path2? @ 36
/trunk/WingMan/AutoCompletion/AutoCompletion.cs
@@ -124,7 +124,29 @@
new SqliteConnection($"URI=file:{"Autocomplete.db"}"))
{
await sqliteConnection.OpenAsync(CancellationToken);
 
// Create table if it does not exist.
using (var sqliteCommand =
new SqliteCommand($"CREATE TABLE IF NOT EXISTS {name} (data text UNIQUE NOT NULL)",
sqliteConnection))
{
using (var dbtransaction = sqliteConnection.BeginTransaction())
{
try
{
await sqliteCommand.ExecuteReaderAsync(CancellationToken);
 
dbtransaction.Commit();
}
catch
{
dbtransaction.Rollback();
throw;
}
}
}
 
using (var sqliteCommand =
new SqliteCommand($"SELECT data FROM {name}", sqliteConnection))
{
using (var dbtransaction = sqliteConnection.BeginTransaction())
@@ -165,4 +187,4 @@
}
}
}
}
}
/trunk/WingMan/Bindings/ExecuteKeyBinding.cs
@@ -1,23 +1,22 @@
using System.Xml.Serialization;
using ProtoBuf;
 
namespace WingMan.Bindings
{
[ProtoContract]
public class ExecuteKeyBinding
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer =
new XmlSerializer(typeof(ExecuteKeyBinding));
 
public ExecuteKeyBinding()
{
}
 
public ExecuteKeyBinding(string nick, string name)
public ExecuteKeyBinding(string nick, string name) : this()
{
Nick = nick;
Name = name;
}
 
public string Nick { get; set; }
public string Name { get; set; }
[ProtoMember(1)] public string Nick { get; set; }
 
[ProtoMember(2)] public string Name { get; set; }
}
}
}
/trunk/WingMan/Bindings/KeyBindingExchange.cs
@@ -1,25 +1,30 @@
using System.Collections.Generic;
using System.Xml.Serialization;
using ProtoBuf;
 
namespace WingMan.Bindings
{
[ProtoContract]
public class KeyBindingExchange
{
[XmlIgnore] public static readonly XmlSerializer XmlSerializer =
new XmlSerializer(typeof(KeyBindingExchange));
private readonly List<string> _keyBindings = new List<string>();
 
public KeyBindingExchange()
{
}
 
public KeyBindingExchange(string nick, List<KeyBinding> keyBindings)
public KeyBindingExchange(string nick, List<string> keyBindings) : this()
{
Nick = nick;
KeyBindings = keyBindings;
}
 
public string Nick { get; set; }
[ProtoMember(1)] public string Nick { get; set; }
 
public List<KeyBinding> KeyBindings { get; set; }
[ProtoMember(2)]
public List<string> KeyBindings
{
get => _keyBindings;
set => _keyBindings.AddRange(value);
}
}
}
}
/trunk/WingMan/Bindings/KeyBindingsSynchronizer.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ProtoBuf;
using WingMan.Communication;
 
namespace WingMan.Bindings
@@ -21,7 +22,7 @@
CancellationToken = cancellationToken;
TaskScheduler = taskScheduler;
 
SynchronizedMouseKeyBindings = new ConcurrentDictionary<string, List<KeyBinding>>();
SynchronizedMouseKeyBindings = new ConcurrentDictionary<string, List<string>>();
 
MqttCommunication.OnMessageReceived += MqttCommunicationOnMessageReceived;
 
@@ -30,7 +31,7 @@
 
private LocalKeyBindings LocalKeyBindings { get; }
 
private ConcurrentDictionary<string, List<KeyBinding>> SynchronizedMouseKeyBindings { get; }
private ConcurrentDictionary<string, List<string>> SynchronizedMouseKeyBindings { get; }
 
private MqttCommunication MqttCommunication { get; }
 
@@ -57,7 +58,7 @@
memoryStream.Position = 0L;
 
var mouseKeyBindingsExchange =
(KeyBindingExchange) KeyBindingExchange.XmlSerializer.Deserialize(memoryStream);
Serializer.Deserialize<KeyBindingExchange>(memoryStream);
 
// Do not add own bindings.
if (string.Equals(mouseKeyBindingsExchange.Nick, MqttCommunication.Nick))
@@ -91,8 +92,9 @@
 
using (var memoryStream = new MemoryStream())
{
KeyBindingExchange.XmlSerializer.Serialize(memoryStream,
new KeyBindingExchange(MqttCommunication.Nick, LocalKeyBindings.Bindings));
Serializer.Serialize(memoryStream,
new KeyBindingExchange(MqttCommunication.Nick,
LocalKeyBindings.Bindings.Select(binding => binding.Name).ToList()));
 
memoryStream.Position = 0L;
 
/trunk/WingMan/Bindings/KeyInterceptor.cs
@@ -7,6 +7,7 @@
using System.Threading.Tasks.Dataflow;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using ProtoBuf;
using WingMan.Communication;
using WingMan.Utilities;
 
@@ -16,7 +17,7 @@
{
public delegate void MouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args);
 
private volatile bool ProcessPipe;
private volatile bool _processPipe;
 
public KeyInterceptor(RemoteKeyBindings remoteKeyBindings, MqttCommunication mqttCommunication,
TaskScheduler taskScheduler, CancellationToken cancellationToken)
@@ -98,7 +99,7 @@
 
try
{
if (!ProcessPipe)
if (!_processPipe)
return;
 
foreach (var binding in RemoteKeyBindings.Bindings)
@@ -116,7 +117,7 @@
 
using (var memoryStream = new MemoryStream())
{
ExecuteKeyBinding.XmlSerializer.Serialize(memoryStream,
Serializer.Serialize(memoryStream,
new ExecuteKeyBinding(binding.Nick, binding.Name));
 
memoryStream.Position = 0L;
@@ -135,7 +136,7 @@
 
private async void MouseKeyGloalHookOnKeyDown(object sender, KeyEventArgs e)
{
ProcessPipe = true;
_processPipe = true;
 
if (!KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key))
return;
@@ -154,7 +155,7 @@
 
private void MouseKeyGloalHookOnKeyUp(object sender, KeyEventArgs e)
{
ProcessPipe = false;
_processPipe = false;
}
}
}
}
/trunk/WingMan/Bindings/KeySimulator.cs
@@ -4,6 +4,7 @@
using System.Threading.Tasks;
using WindowsInput;
using WindowsInput.Native;
using ProtoBuf;
using WingMan.Communication;
using WingMan.Utilities;
 
@@ -52,7 +53,7 @@
memoryStream.Position = 0L;
 
var executeMouseKeyBinding =
(ExecuteKeyBinding) ExecuteKeyBinding.XmlSerializer.Deserialize(memoryStream);
Serializer.Deserialize<ExecuteKeyBinding>(memoryStream);
 
// Do not process own mouse key bindings.
if (!string.Equals(executeMouseKeyBinding.Nick, MqttCommunication.Nick, StringComparison.Ordinal))
/trunk/WingMan/Communication/MqttCommunication.cs
@@ -63,7 +63,7 @@
 
private IPAddress IpAddress { get; set; }
 
private int Port { get; set; }
public int Port { get; set; }
 
private string Password { get; set; }
 
@@ -159,9 +159,9 @@
 
private async Task StopClient()
{
await Client.StopAsync();
 
UnbindClientHandlers();
 
await Client.StopAsync();
}
 
public void BindClientHandlers()
@@ -186,7 +186,7 @@
{
using (var inputStream = new MemoryStream(e.ApplicationMessage.Payload))
{
using (var decryptedStream = await AES.Decrypt(inputStream, Password))
using (var decryptedStream = await Aes.Decrypt(inputStream, Password))
{
using (var lz4Decompress = new LZ4Stream(decryptedStream, CompressionMode.Decompress))
{
@@ -254,9 +254,9 @@
 
private async Task StopServer()
{
await Server.StopAsync();
 
UnbindServerHandlers();
 
await Server.StopAsync();
}
 
private void BindServerHandlers()
@@ -287,7 +287,7 @@
{
using (var inputStream = new MemoryStream(e.ApplicationMessage.Payload))
{
using (var decryptedStream = await AES.Decrypt(inputStream, Password))
using (var decryptedStream = await Aes.Decrypt(inputStream, Password))
{
using (var lz4Decompress = new LZ4Stream(decryptedStream, CompressionMode.Decompress))
{
@@ -383,7 +383,7 @@
 
compressStream.Position = 0L;
 
using (var outputStream = await AES.Encrypt(compressStream, Password))
using (var outputStream = await Aes.Encrypt(compressStream, Password))
{
var data = outputStream.ToArray();
switch (Type)
/trunk/WingMan/Discovery/Discovery.cs
@@ -24,16 +24,16 @@
private static NatDiscoverer NatDiscoverer { get; set; }
private static TaskScheduler TaskScheduler { get; set; }
 
private static CancellationTokenSource UPnPCancellationTokenSource { get; set; }
private static CancellationTokenSource PMPCancellationTokenSource { get; set; }
private static CancellationTokenSource UpnPCancellationTokenSource { get; set; }
private static CancellationTokenSource PmpCancellationTokenSource { get; set; }
 
public void Dispose()
{
PMPCancellationTokenSource?.Dispose();
PMPCancellationTokenSource = null;
PmpCancellationTokenSource?.Dispose();
PmpCancellationTokenSource = null;
 
PMPCancellationTokenSource?.Dispose();
PMPCancellationTokenSource = null;
PmpCancellationTokenSource?.Dispose();
PmpCancellationTokenSource = null;
}
 
public event PortMapFailed OnPortMapFailed;
@@ -42,24 +42,56 @@
{
switch (type)
{
case DiscoveryType.UPnP:
return await CreateUPnPMapping(port);
case DiscoveryType.PMP:
return await CreatePMPPortMapping(port);
case DiscoveryType.Upnp:
return await CreateUpnPMapping(port);
case DiscoveryType.Pmp:
return await CreatePmpPortMapping(port);
default:
throw new ArgumentException("Unknown disocvery type");
throw new ArgumentException("Unknown discovery type");
}
}
 
private async Task<bool> CreateUPnPMapping(int port)
public async Task<bool> DeleteMapping(DiscoveryType type, int port)
{
switch (type)
{
case DiscoveryType.Upnp:
return await DeleteUpnPMapping(port);
case DiscoveryType.Pmp:
return await DeletePmpPortMapping(port);
default:
throw new ArgumentException("Unknown discovery type");
}
}
 
private async Task<bool> DeleteUpnPMapping(int port)
{
try
{
UPnPCancellationTokenSource = new CancellationTokenSource();
UpnPCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(UPnPCancellationTokenSource.Token,
CancellationTokenSource.CreateLinkedTokenSource(UpnPCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Upnp, linkedCancellationTokenSource);
await device.DeletePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
 
return true;
}
catch
{
return false;
}
}
 
private async Task<bool> CreateUpnPMapping(int port)
{
try
{
UpnPCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(UpnPCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Upnp, linkedCancellationTokenSource);
await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
 
return true;
@@ -67,7 +99,7 @@
catch (Exception ex)
{
await Task.Delay(0, CancellationTokenSource.Token).ContinueWith(_ =>
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.UPnP, ex)),
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.Upnp, ex)),
CancellationTokenSource.Token, TaskContinuationOptions.None, TaskScheduler);
 
return false;
@@ -74,15 +106,33 @@
}
}
 
private async Task<bool> CreatePMPPortMapping(int port)
private async Task<bool> DeletePmpPortMapping(int port)
{
try
{
PMPCancellationTokenSource = new CancellationTokenSource();
PmpCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
PMPCancellationTokenSource.Token,
PmpCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Pmp, linkedCancellationTokenSource);
await device.DeletePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
return true;
}
catch
{
return false;
}
}
 
private async Task<bool> CreatePmpPortMapping(int port)
{
try
{
PmpCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
PmpCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Pmp, linkedCancellationTokenSource);
await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
return true;
}
@@ -89,7 +139,7 @@
catch (Exception ex)
{
await Task.Delay(0, CancellationTokenSource.Token).ContinueWith(_ =>
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.PMP, ex)),
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.Pmp, ex)),
CancellationTokenSource.Token, TaskContinuationOptions.None, TaskScheduler);
 
return false;
/trunk/WingMan/Discovery/DiscoveryType.cs
@@ -2,7 +2,7 @@
{
public enum DiscoveryType
{
UPnP,
PMP
Upnp,
Pmp
}
}
/trunk/WingMan/Lobby/LobbyMessage.cs
@@ -1,11 +1,22 @@
using System.Xml.Serialization;
using ProtoBuf;
 
namespace WingMan.Lobby
{
[ProtoContract]
public class LobbyMessage
{
[XmlIgnore] public static XmlSerializer XmlSerializer = new XmlSerializer(typeof(LobbyMessage));
public string Message { get; set; }
public string Nick { get; set; }
public LobbyMessage()
{
}
 
public LobbyMessage(string nick, string message) : this()
{
Nick = nick;
Message = message;
}
 
[ProtoMember(1)] public string Message { get; set; }
 
[ProtoMember(2)] public string Nick { get; set; }
}
}
}
/trunk/WingMan/Lobby/LobbyMessageSynchronizer.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ProtoBuf;
using WingMan.Communication;
 
namespace WingMan.Lobby
@@ -44,7 +45,7 @@
 
memoryStream.Position = 0L;
 
var lobbyMessage = (LobbyMessage) LobbyMessage.XmlSerializer.Deserialize(memoryStream);
var lobbyMessage = Serializer.Deserialize<LobbyMessage>(memoryStream);
 
await Task.Delay(0)
.ContinueWith(
@@ -58,11 +59,7 @@
{
using (var memoryStream = new MemoryStream())
{
LobbyMessage.XmlSerializer.Serialize(memoryStream, new LobbyMessage
{
Nick = MqttCommunication.Nick,
Message = message
});
Serializer.Serialize(memoryStream, new LobbyMessage(MqttCommunication.Nick, message));
 
memoryStream.Position = 0L;
 
/trunk/WingMan/Utilities/AES.cs
@@ -1,13 +1,13 @@
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
 
namespace WingMan.Utilities
{
public static class AES
public static class Aes
{
private const int AesKeySize = 256;
private const int AesKeyIterations = 4096;
private const int AesBlockSize = 128;
private const CipherMode AesCipherMode = CipherMode.CBC;
@@ -28,21 +28,22 @@
{
var outputStream = new MemoryStream();
 
using (var rijdanelManaged = new RijndaelManaged())
using (var aesManaged = new AesManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
aesManaged.BlockSize = AesBlockSize;
aesManaged.Mode = AesCipherMode;
aesManaged.Padding = AesPaddingMode;
aesManaged.KeySize = AesKeySize;
 
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
aesManaged.Key = derivedKey.GetBytes(aesManaged.KeySize / 8);
aesManaged.IV = derivedKey.GetBytes(aesManaged.BlockSize / 8);
 
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
using (var encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
{
using (var memoryStream = new MemoryStream())
{
@@ -76,21 +77,22 @@
/// <returns>an encrypted byte array</returns>
public static async Task<byte[]> Encrypt(byte[] data, string key)
{
using (var rijdanelManaged = new RijndaelManaged())
using (var aesManaged = new AesManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
aesManaged.BlockSize = AesBlockSize;
aesManaged.Mode = AesCipherMode;
aesManaged.Padding = AesPaddingMode;
aesManaged.KeySize = AesKeySize;
 
// Compute the salt and the IV from the key.
var salt = new byte[AesKeySaltBytes];
Rng.GetBytes(salt);
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
aesManaged.Key = derivedKey.GetBytes(aesManaged.KeySize / 8);
aesManaged.IV = derivedKey.GetBytes(aesManaged.BlockSize / 8);
 
using (var encryptor = rijdanelManaged.CreateEncryptor(rijdanelManaged.Key, rijdanelManaged.IV))
using (var encryptor = aesManaged.CreateEncryptor(aesManaged.Key, aesManaged.IV))
{
using (var memoryStream = new MemoryStream())
{
@@ -133,19 +135,20 @@
//var salt = data.Take(AesKeySaltBytes).ToArray();
//var text = data.Skip(AesKeySaltBytes).ToArray();
 
using (var rijdanelManaged = new RijndaelManaged())
using (var aesManaged = new AesManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
aesManaged.BlockSize = AesBlockSize;
aesManaged.Mode = AesCipherMode;
aesManaged.Padding = AesPaddingMode;
aesManaged.KeySize = AesKeySize;
 
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
aesManaged.Key = derivedKey.GetBytes(aesManaged.KeySize / 8);
aesManaged.IV = derivedKey.GetBytes(aesManaged.BlockSize / 8);
 
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
using (var decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
{
using (var memoryStream = new MemoryStream(text))
{
@@ -176,19 +179,20 @@
var salt = data.Take(AesKeySaltBytes).ToArray();
var text = data.Skip(AesKeySaltBytes).ToArray();
 
using (var rijdanelManaged = new RijndaelManaged())
using (var aesManaged = new AesManaged())
{
// FIPS-197 / CBC
rijdanelManaged.BlockSize = AesBlockSize;
rijdanelManaged.Mode = AesCipherMode;
rijdanelManaged.Padding = AesPaddingMode;
aesManaged.BlockSize = AesBlockSize;
aesManaged.Mode = AesCipherMode;
aesManaged.Padding = AesPaddingMode;
aesManaged.KeySize = AesKeySize;
 
// Retrieve the key and the IV from the salt.
var derivedKey = new Rfc2898DeriveBytes(key, salt, AesKeyIterations);
rijdanelManaged.Key = derivedKey.GetBytes(rijdanelManaged.KeySize / 8);
rijdanelManaged.IV = derivedKey.GetBytes(rijdanelManaged.BlockSize / 8);
aesManaged.Key = derivedKey.GetBytes(aesManaged.KeySize / 8);
aesManaged.IV = derivedKey.GetBytes(aesManaged.BlockSize / 8);
 
using (var decryptor = rijdanelManaged.CreateDecryptor(rijdanelManaged.Key, rijdanelManaged.IV))
using (var decryptor = aesManaged.CreateDecryptor(aesManaged.Key, aesManaged.IV))
{
using (var memoryStream = new MemoryStream(text))
{
@@ -208,15 +212,12 @@
}
}
 
public static string ExpandKey(string password, int size = 32)
public static string ExpandKey(string password, int length = 32)
{
var sb = new StringBuilder(password);
do
{
sb.Append(password);
} while (sb.Length < size);
 
return sb.ToString(0, size);
if (length <= password.Length) return password.Substring(0, length);
while (password.Length * 2 <= length) password += password;
if (password.Length < length) password += password.Substring(0, length - password.Length);
return password;
}
}
}
/trunk/WingMan/WingMan.csproj
@@ -57,11 +57,17 @@
<Reference Include="Open.Nat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f22a6a4582336c76, processorArchitecture=MSIL">
<HintPath>..\packages\Open.NAT.2.1.0.0\lib\net45\Open.Nat.dll</HintPath>
</Reference>
<Reference Include="protobuf-net, Version=2.4.0.0, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
<HintPath>..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data.Portable, Version=4.0.0.0, Culture=neutral, PublicKeyToken=59e704a76bc4613a, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Data.Sqlite.Portable.1.0.3.5\lib\net4\System.Data.Portable.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.5.24.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Tpl.Dataflow.4.5.24\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
</Reference>
/trunk/WingMan/WingManForm.Designer.cs
@@ -55,7 +55,7 @@
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.OverlayPanel = new System.Windows.Forms.Panel();
this.OverlayPanelLabel = new System.Windows.Forms.Label();
this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components);
this.WingManNotifyIcon = new System.Windows.Forms.NotifyIcon(this.components);
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
@@ -153,7 +153,7 @@
this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill;
this.groupBox2.Location = new System.Drawing.Point(0, 0);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(530, 337);
this.groupBox2.Size = new System.Drawing.Size(530, 332);
this.groupBox2.TabIndex = 1;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Lobby";
@@ -245,6 +245,8 @@
//
// LocalNameTextBox
//
this.LocalNameTextBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
this.LocalNameTextBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
this.LocalNameTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.LocalNameTextBox.Location = new System.Drawing.Point(72, 256);
this.LocalNameTextBox.Name = "LocalNameTextBox";
@@ -430,7 +432,7 @@
this.tabPage2.Location = new System.Drawing.Point(4, 22);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(530, 337);
this.tabPage2.Size = new System.Drawing.Size(530, 332);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Bindings";
this.tabPage2.UseVisualStyleBackColor = true;
@@ -441,7 +443,7 @@
this.tabPage3.Cursor = System.Windows.Forms.Cursors.Arrow;
this.tabPage3.Location = new System.Drawing.Point(4, 22);
this.tabPage3.Name = "tabPage3";
this.tabPage3.Size = new System.Drawing.Size(530, 337);
this.tabPage3.Size = new System.Drawing.Size(530, 332);
this.tabPage3.TabIndex = 2;
this.tabPage3.Text = "Chat";
this.tabPage3.UseVisualStyleBackColor = true;
@@ -492,12 +494,12 @@
this.OverlayPanelLabel.TabIndex = 0;
this.OverlayPanelLabel.Text = "Press key-combination to bind...";
//
// notifyIcon1
// WingManNotifyIcon
//
this.notifyIcon1.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon1.Icon")));
this.notifyIcon1.Text = "notifyIcon1";
this.notifyIcon1.Visible = true;
this.notifyIcon1.DoubleClick += new System.EventHandler(this.NotifyIconDoubleClick);
this.WingManNotifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("WingManNotifyIcon.Icon")));
this.WingManNotifyIcon.Text = "WingMan";
this.WingManNotifyIcon.Visible = true;
this.WingManNotifyIcon.DoubleClick += new System.EventHandler(this.NotifyIconDoubleClick);
//
// WingManForm
//
@@ -578,7 +580,7 @@
private System.Windows.Forms.Label OverlayPanelLabel;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.TextBox Password;
private System.Windows.Forms.NotifyIcon notifyIcon1;
private System.Windows.Forms.NotifyIcon WingManNotifyIcon;
private System.Windows.Forms.CheckedListBox LocalBindingsCheckedListBox;
}
}
/trunk/WingMan/WingManForm.cs
@@ -24,7 +24,7 @@
{
public partial class WingManForm : Form
{
private const int tabControlDetachPixelOffset = 20;
private const int TabControlDetachPixelOffset = 20;
 
public WingManForm()
{
@@ -45,6 +45,7 @@
Task.Run(() => AutoCompletion.Load(Address.Name, Address.AutoCompleteCustomSource));
Task.Run(() => AutoCompletion.Load(Port.Name, Address.AutoCompleteCustomSource));
Task.Run(() => AutoCompletion.Load(Nick.Name, Nick.AutoCompleteCustomSource));
Task.Run(() => AutoCompletion.Load(LocalNameTextBox.Name, LocalNameTextBox.AutoCompleteCustomSource));
 
MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
MqttCommunication.OnClientAuthenticationFailed += OnMqttClientAuthenticationFailed;
@@ -65,6 +66,7 @@
DataSource = LocalKeyBindings.Bindings
};
LocalCheckedListBoxBindingSource.ListChanged += LocalCheckedListBoxBindingSourceOnListChanged;
 
LocalBindingsCheckedListBox.DisplayMember = "DisplayName";
LocalBindingsCheckedListBox.ValueMember = "Keys";
LocalBindingsCheckedListBox.DataSource = LocalCheckedListBoxBindingSource;
@@ -78,10 +80,13 @@
{
DataSource = KeyBindingsExchange.ExchangeBindings
};
RemoteBindingsComboBoxSource.ListChanged += RemoteBindingsComboBoxSourceOnListChanged;
 
RemoteBindingsComboBox.DisplayMember = "Nick";
RemoteBindingsComboBox.ValueMember = "KeyBindings";
RemoteBindingsComboBox.DataSource = RemoteBindingsComboBoxSource;
 
 
// Start lobby message synchronizer.
LobbyMessageSynchronizer = new LobbyMessageSynchronizer(MqttCommunication, FormTaskScheduler,
FormCancellationTokenSource.Token);
@@ -113,7 +118,7 @@
 
private List<string> MouseKeyCombo { get; set; }
 
private LocalKeyBindings LocalKeyBindings { get; }
public LocalKeyBindings LocalKeyBindings { get; set; }
 
private RemoteKeyBindings RemoteKeyBindings { get; }
 
@@ -133,19 +138,24 @@
 
public KeySimulator KeySimulator { get; set; }
 
private static Point tabControlClickStartPosition { get; set; }
private static Point TabControlClickStartPosition { get; set; }
private static TabControl DetachedTabControl { get; set; }
private static Form DetachedForm { get; set; }
 
private void RemoteBindingsComboBoxSourceOnListChanged(object sender, ListChangedEventArgs e)
{
UpdateRemoteBindingsListBox();
}
 
public void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
{
switch (args.Type)
{
case DiscoveryType.UPnP:
case DiscoveryType.Upnp:
ActivityTextBox.AppendText(
$"{Strings.Failed_to_create_UPnP_port_mapping}{Environment.NewLine}");
break;
case DiscoveryType.PMP:
case DiscoveryType.Pmp:
ActivityTextBox.AppendText(
$"{Strings.Failed_to_create_PMP_port_mapping}{Environment.NewLine}");
break;
@@ -281,47 +291,36 @@
{
KeyBindingsExchange.ExchangeBindings.Add(e.Bindings);
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteItems();
return;
}
 
// If the bindings for the nick have not changed then do not update.
if (exchangeBindings.KeyBindings.SequenceEqual(e.Bindings.KeyBindings))
if (e.Bindings.KeyBindings.Count == exchangeBindings.KeyBindings.Count &&
e.Bindings.KeyBindings.All(exchangeBindings.KeyBindings.Contains))
{
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteItems();
return;
}
 
// Update the bindings.
exchangeBindings.KeyBindings = e.Bindings.KeyBindings;
exchangeBindings.KeyBindings.RemoveAll(binding => !e.Bindings.KeyBindings.Contains(binding));
exchangeBindings.KeyBindings.AddRange(
e.Bindings.KeyBindings.Where(binding => !exchangeBindings.KeyBindings.Contains(binding)));
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteItems();
}
 
private void UpdateRemoteItems()
private void UpdateRemoteBindingsListBox()
{
var exchangeBindings = (List<KeyBinding>) RemoteBindingsComboBox.SelectedValue;
if (exchangeBindings == null)
var keyBindingExchange = (KeyBindingExchange) RemoteBindingsComboBox.SelectedItem;
if (keyBindingExchange == null)
return;
 
var replaceMouseBindings = new ObservableCollection<RemoteKeyBinding>();
foreach (var remoteBinding in RemoteKeyBindings.Bindings)
{
if (!exchangeBindings.Any(binding =>
string.Equals(binding.Name, remoteBinding.Name, StringComparison.Ordinal)))
continue;
RemoteBindingsListBox.Items.Clear();
var bindings = KeyBindingsExchange.ExchangeBindings
.Where(binding => binding.Nick == keyBindingExchange.Nick)
.SelectMany(binding => binding.KeyBindings.Select(keyBinding => keyBinding))
.Select(binding => (object) binding).ToArray();
 
replaceMouseBindings.Add(remoteBinding);
}
 
RemoteKeyBindings.Bindings.Clear();
foreach (var binding in replaceMouseBindings) RemoteKeyBindings.Bindings.Add(binding);
 
RemoteBindingsListBox.Items.Clear();
RemoteBindingsListBox.DisplayMember = "Name";
RemoteBindingsListBox.ValueMember = "Name";
var bindings = exchangeBindings.Select(binding => (object) binding.Name).ToArray();
if (bindings.Length == 0)
return;
 
@@ -330,9 +329,9 @@
 
private void OnLobbyMessageReceived(object sender, LobbyMessageReceivedEventArgs e)
{
notifyIcon1.BalloonTipTitle = Strings.Lobby_message;
notifyIcon1.BalloonTipText = $"{e.Nick} : {e.Message}{Environment.NewLine}";
notifyIcon1.ShowBalloonTip(1000);
WingManNotifyIcon.BalloonTipTitle = Strings.Lobby_message;
WingManNotifyIcon.BalloonTipText = $"{e.Nick} : {e.Message}{Environment.NewLine}";
WingManNotifyIcon.ShowBalloonTip(1000);
 
LobbyTextBox.AppendText($"{e.Nick} : {e.Message}{Environment.NewLine}");
}
@@ -352,7 +351,16 @@
// Stop the MQTT server if it is running.
if (MqttCommunication.Running)
{
// Remote UPnP and Pmp mappings.
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port);
await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port);
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
await MqttCommunication.Stop();
 
HostButton.BackColor = Color.Empty;
 
// Enable controls.
@@ -370,10 +378,14 @@
StoreConnectionAutocomplete();
 
// Try to reserve port: try UPnP followed by PMP.
if (!await Discovery.CreateMapping(DiscoveryType.UPnP, port) &&
!await Discovery.CreateMapping(DiscoveryType.PMP, port))
ActivityTextBox.AppendText(
$"{Strings.Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly}{Environment.NewLine}");
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
if (!await Discovery.CreateMapping(DiscoveryType.Upnp, port) &&
!await Discovery.CreateMapping(DiscoveryType.Pmp, port))
ActivityTextBox.AppendText(
$"{Strings.Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly}{Environment.NewLine}");
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
// Start the MQTT server.
if (!await MqttCommunication
@@ -468,7 +480,7 @@
return false;
}
 
password = AES.ExpandKey(Password.Text);
password = Aes.ExpandKey(Password.Text);
 
Address.BackColor = Color.Empty;
Port.BackColor = Color.Empty;
@@ -531,7 +543,7 @@
LobbySayTextBox.Text = string.Empty;
}
 
private void LocalAddBindingButtonClick(object sender, EventArgs e)
private async void LocalAddBindingButtonClick(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(LocalNameTextBox.Text))
{
@@ -551,6 +563,10 @@
LocalNameTextBox.BackColor = Color.Empty;
LocalBindingsCheckedListBox.BackColor = Color.Empty;
 
LocalNameTextBox.AutoCompleteCustomSource.Add(LocalNameTextBox.Text);
 
await AutoCompletion.Save(LocalNameTextBox.Name, LocalNameTextBox.AutoCompleteCustomSource);
 
ShowOverlayPanel();
 
MouseKeyCombo = new List<string>();
@@ -607,11 +623,11 @@
 
private async void LocalBindingsRemoveButtonClick(object sender, EventArgs e)
{
var helmBinding = (KeyBinding) LocalBindingsCheckedListBox.SelectedItem;
if (helmBinding == null)
var localBinding = (KeyBinding) LocalBindingsCheckedListBox.SelectedItem;
if (localBinding == null)
return;
 
LocalKeyBindings.Bindings.Remove(helmBinding);
LocalKeyBindings.Bindings.Remove(localBinding);
LocalCheckedListBoxBindingSource.ResetBindings(false);
 
await SaveLocalMouseKeyBindings();
@@ -630,7 +646,7 @@
 
private void RemoteBindingsComboBoxSelectionChangeCompleted(object sender, EventArgs e)
{
UpdateRemoteItems();
UpdateRemoteBindingsListBox();
}
 
private async void WingManFormOnLoad(object sender, EventArgs e)
@@ -762,7 +778,7 @@
{
if (e.Button != MouseButtons.Left) return;
 
tabControlClickStartPosition = e.Location;
TabControlClickStartPosition = e.Location;
}
 
private void WingManTabControlMouseMove(object sender, MouseEventArgs e)
@@ -769,19 +785,19 @@
{
if (e.Button == MouseButtons.Left)
{
var mouseOffsetX = tabControlClickStartPosition.X - e.X;
var mouseOffsetY = tabControlClickStartPosition.Y - e.Y;
var mouseOffsetX = TabControlClickStartPosition.X - e.X;
var mouseOffsetY = TabControlClickStartPosition.Y - e.Y;
 
if (mouseOffsetX <= tabControlDetachPixelOffset && mouseOffsetY <= tabControlDetachPixelOffset)
if (mouseOffsetX <= TabControlDetachPixelOffset && mouseOffsetY <= TabControlDetachPixelOffset)
return;
 
tabControl1.DoDragDrop(tabControl1.SelectedTab, DragDropEffects.Move);
tabControlClickStartPosition = new Point();
TabControlClickStartPosition = new Point();
 
return;
}
 
tabControlClickStartPosition = new Point();
TabControlClickStartPosition = new Point();
}
 
private void WingManTabControlGiveFeedback(object sender, GiveFeedbackEventArgs e)
@@ -814,7 +830,10 @@
Location = MousePosition,
MaximizeBox = false,
SizeGripStyle = SizeGripStyle.Hide,
FormBorderStyle = FormBorderStyle.FixedSingle
FormBorderStyle = FormBorderStyle.FixedSingle,
Name = Name,
Text = Text,
Icon = Icon
};
 
DetachedTabControl = new TabControl
/trunk/WingMan/WingManForm.resx
@@ -960,10 +960,10 @@
SUVORK5CYII=
</value>
</data>
<metadata name="notifyIcon1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<metadata name="WingManNotifyIcon.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="notifyIcon1.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<data name="WingManNotifyIcon.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
AAABAAkAEBAAAAEAIABoBAAAlgAAABgYAAABACAAiAkAAP4EAAAgIAAAAQAgAKgQAACGDgAAMDAAAAEA
IACoJQAALh8AAEBAAAABACAAKEIAANZEAABgYAAAAQAgAKiUAAD+hgAAgIAAAAEAIAAoCAEAphsBAMDA
/trunk/WingMan/packages.config
@@ -9,5 +9,6 @@
<package id="MQTTnet" version="2.8.4" targetFramework="net452" />
<package id="MQTTnet.Extensions.ManagedClient" version="2.8.4" targetFramework="net452" />
<package id="Open.NAT" version="2.1.0.0" targetFramework="net452" />
<package id="protobuf-net" version="2.4.0" targetFramework="net452" />
<package id="SQLite.Native" version="3.12.3" targetFramework="net452" />
</packages>