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}); } } }