Skip to content

Commit

Permalink
Various reafactoring, plus a fix for #1
Browse files Browse the repository at this point in the history
  • Loading branch information
alexforster committed Jan 19, 2019
1 parent 616931e commit 52cce0f
Show file tree
Hide file tree
Showing 11 changed files with 1,037 additions and 847 deletions.
12 changes: 6 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
bin/
obj/
*.user*
*.nu*
.*
._*
bin/
obj/
*.user*
*.nu*
.*
._*
109 changes: 109 additions & 0 deletions AmiClient.Helpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* Copyright © 2019 Alex Forster. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Ami
{
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Reactive.Linq;
using System.Security.Cryptography;

public sealed partial class AmiClient
{
public async Task<Boolean> Login(String username, String secret, Boolean md5 = true)
{
if(username == null)
{
throw new ArgumentNullException(nameof(username));
}

if(secret == null)
{
throw new ArgumentNullException(nameof(secret));
}

AmiMessage request, response;

if(md5)
{
request = new AmiMessage
{
{ "Action", "Challenge" },
{ "AuthType", "MD5" },
};

response = await this.Publish(request);

if(!(response["Response"] ?? String.Empty).Equals("Success", StringComparison.OrdinalIgnoreCase))
{
return false;
}

var answer = MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(response["Challenge"] + secret));

var key = "";

for(var i = 0; i < answer.Length; i++)
{
key += answer[i].ToString("x2");
}

request = new AmiMessage
{
{ "Action", "Login" },
{ "AuthType", "MD5" },
{ "Username", username },
{ "Key", key },
};

response = await this.Publish(request);
}
else
{
request = new AmiMessage
{
{ "Action", "Login" },
{ "Username", username },
{ "Secret", secret },
};

response = await this.Publish(request);
}

return (response["Response"] ?? String.Empty).Equals("Success", StringComparison.OrdinalIgnoreCase);
}

public async Task<Boolean> Logoff()
{
AmiMessage request, response;

request = new AmiMessage
{
{ "Action", "Logoff" },
};

response = await this.Publish(request);

return (response["Response"] ?? String.Empty).Equals("Goodbye", StringComparison.OrdinalIgnoreCase);
}
}
}
213 changes: 107 additions & 106 deletions AmiClient.Observable.cs
Original file line number Diff line number Diff line change
@@ -1,106 +1,107 @@
/* Copyright © 2018 Alex Forster. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Ami
{
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.IO;
using System.Security.Cryptography;

public sealed partial class AmiClient : IDisposable, IObservable<AmiMessage>
{
private readonly ConcurrentDictionary<IObserver<AmiMessage>, Subscription> observers =
new ConcurrentDictionary<IObserver<AmiMessage>, Subscription>();

private void Dispatch(AmiMessage message)
{
foreach(var observer in this.observers.Keys)
{
observer.OnNext(message);
}
}

private void Dispatch(Exception exception)
{
foreach(var observer in this.observers.Keys)
{
observer.OnError(exception);

this.Unsubscribe(observer);
}
}

public IDisposable Subscribe(IObserver<AmiMessage> observer)
{
if(observer == null)
{
throw new ArgumentNullException(nameof(observer));
}

var subscription = new Subscription(this, observer);

this.observers.TryAdd(observer, subscription);

return subscription;
}

public void Unsubscribe(IObserver<AmiMessage> observer)
{
if(observer == null)
{
throw new ArgumentNullException(nameof(observer));
}

this.observers.TryRemove(observer, out _);
}

public void Dispose()
{
foreach(var observer in this.observers.Keys)
{
observer.OnCompleted();

this.Unsubscribe(observer);
}

this.processing = false;
}

public sealed class Subscription : IDisposable
{
private readonly AmiClient client;

private readonly IObserver<AmiMessage> observer;

internal Subscription(AmiClient client, IObserver<AmiMessage> observer)
{
this.client = client;
this.observer = observer;
}

public void Dispose()
{
this.client.Unsubscribe(this.observer);
}
}
}
}
/* Copyright © 2019 Alex Forster. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

namespace Ami
{
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System.Reactive.Linq;
using System.Security.Cryptography;

public sealed partial class AmiClient : IDisposable, IObservable<AmiMessage>
{
private readonly ConcurrentDictionary<IObserver<AmiMessage>, Subscription> observers =
new ConcurrentDictionary<IObserver<AmiMessage>, Subscription>();

private void Dispatch(AmiMessage message)
{
foreach(var observer in this.observers.Keys)
{
observer.OnNext(message);
}
}

private void Dispatch(Exception exception)
{
foreach(var observer in this.observers.Keys)
{
observer.OnError(exception);

this.Unsubscribe(observer);
}
}

public IDisposable Subscribe(IObserver<AmiMessage> observer)
{
if(observer == null)
{
throw new ArgumentNullException(nameof(observer));
}

var subscription = new Subscription(this, observer);

Debug.Assert(this.observers.TryAdd(observer, subscription));

return subscription;
}

public void Unsubscribe(IObserver<AmiMessage> observer)
{
if(observer == null)
{
throw new ArgumentNullException(nameof(observer));
}

this.observers.TryRemove(observer, out _);
}

public void Dispose()
{
foreach(var observer in this.observers.Keys)
{
observer.OnCompleted();

this.Unsubscribe(observer);
}

this.processing = false;
}

private sealed class Subscription : IDisposable
{
private readonly AmiClient client;

private readonly IObserver<AmiMessage> observer;

internal Subscription(AmiClient client, IObserver<AmiMessage> observer)
{
this.client = client;
this.observer = observer;
}

public void Dispose()
{
this.client.Unsubscribe(this.observer);
}
}
}
}
Loading

0 comments on commit 52cce0f

Please sign in to comment.