using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using log4net;
using NPSharp.RPC;
using NPSharp.RPC.Messages.Client;
using NPSharp.RPC.Messages.Server;
namespace NPSharp.NP
{
///
/// Represents a high-level network platform client.
///
public class NPClient
{
private readonly string _host;
private readonly ILog _log;
private readonly ushort _port;
private CancellationToken _cancellationToken;
private CancellationTokenSource _cancellationTokenSource;
private RPCClientStream _rpc;
///
/// Initializes the NP client with a specified host and port.
///
/// The host to connect to.
/// The port to use. Default: 3025.
public NPClient(string host, ushort port = 3025)
{
_host = host;
_port = port;
_log = LogManager.GetLogger("NPClient");
}
///
/// The assigned NP user ID. Will be set on successful authentication.
///
public ulong LoginId { get; private set; }
///
/// The assigned session token for this client. Will be set on successful authentication.
///
public string SessionToken { get; private set; }
// TODO: Handle connection failures via exception
///
/// Connects the client to the NP server.
///
/// True if the connection succeeded, otherwise false.
public bool Connect()
{
_log.Debug("Connect() start");
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
try
{
_rpc = RPCClientStream.Open(_host, _port);
}
catch (Exception err)
{
#if DEBUG
_log.ErrorFormat(@"Could not initialize RPC: {0}", err);
#else
_log.ErrorFormat(@"Could not initialize RPC: {0}", err.Message);
#endif
return false;
}
Task.Factory.StartNew(() =>
{
_log.Debug("Now receiving RPC messages");
try
{
while (true)
{
if (_rpc.Read() == null)
break;
}
}
catch (ProtocolViolationException error)
{
_log.ErrorFormat("Protocol violation: {0}. Disconnect imminent.", error.Message);
Disconnect();
}
_log.Debug("Now not receiving RPC messages anymore");
}, _cancellationToken);
_log.Debug("Connect() done");
return true;
}
///
/// Disconnects the client from the NP server.
///
public void Disconnect()
{
_log.Debug("Disconnect() start");
_cancellationTokenSource.Cancel(true);
// TODO: Find a cleaner way to cancel _processingTask (focus: _rpc.Read)
//_procTask.Wait(_cancellationToken);
_rpc.Close();
LoginId = 0;
_log.Debug("Disconnect() done");
}
// TODO: Try to use an exception for failed action instead
///
/// Authenticates this connection via a token. This token has to be requested via an external interface like
/// remauth.php.
///
/// The token to use for authentication
/// True if the login succeeded, otherwise false.
public async Task AuthenticateWithToken(string token)
{
var tcs = new TaskCompletionSource();
_rpc.AttachHandlerForNextMessage(packet =>
{
var result = packet as AuthenticateResultMessage;
if (result == null)
return;
if (result.Result != 0)
tcs.SetResult(false);
LoginId = result.NPID;
SessionToken = result.SessionToken;
tcs.SetResult(true);
});
_rpc.Send(new AuthenticateWithTokenMessage {Token = token});
return await tcs.Task;
}
// TODO: Try to use an exception for failed action instead
///
/// Uploads a user file.
///
/// The file name to save the contents to on the server
/// The raw byte contents
/// True if the upload succeeded, otherwise false.
public async Task UploadUserFile(string filename, byte[] contents)
{
var tcs = new TaskCompletionSource();
_rpc.AttachHandlerForNextMessage(packet =>
{
var result = (StorageWriteUserFileResultMessage) packet;
if (result.Result != 0)
tcs.SetResult(false);
tcs.SetResult(true);
});
_rpc.Send(new StorageWriteUserFileMessage {FileData = contents, FileName = filename, NPID = LoginId});
return await tcs.Task;
}
///
/// Downloads a user file and returns its contents.
///
/// The file to download
/// File contents as byte array
public async Task GetUserFile(string filename)
{
var tcs = new TaskCompletionSource();
_rpc.AttachHandlerForNextMessage(packet =>
{
var result = (StorageUserFileMessage) packet;
if (result.Result != 0)
{
tcs.SetException(new NpFileException(result.Result));
return;
}
tcs.SetResult(result.FileData);
});
_rpc.Send(new StorageGetUserFileMessage {FileName = filename, NPID = LoginId});
return await tcs.Task;
}
///
/// Downloads a user file onto the harddisk.
///
/// The file to download
/// Path where to save the file
public async void DownloadUserFileTo(string filename, string targetpath)
{
var contents = await GetUserFile(filename);
File.WriteAllBytes(targetpath, contents);
}
///
/// Downloads a publisher file and returns its contents.
///
/// The file to download
/// File contents as byte array
public async Task GetPublisherFile(string filename)
{
var tcs = new TaskCompletionSource();
_rpc.AttachHandlerForNextMessage(packet =>
{
var result = (StoragePublisherFileMessage) packet;
if (result.Result != 0)
{
tcs.SetException(new NpFileException(result.Result));
return;
}
tcs.SetResult(result.FileData);
});
_rpc.Send(new StorageGetPublisherFileMessage {FileName = filename});
return await tcs.Task;
}
///
/// Downloads a publisher file onto the harddisk.
///
/// The file to download
/// Path where to save the file
public async void DownloadPublisherFileTo(string filename, string targetpath)
{
var contents = await GetPublisherFile(filename);
File.WriteAllBytes(targetpath, contents);
}
public void SendRandomString(string data)
{
_rpc.Send(new StorageSendRandomStringMessage {RandomString = data});
}
}
}