mirror of https://github.com/icedream/npsharp.git
Initial commit
commit
11be50dd4c
|
@ -0,0 +1,185 @@
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
build/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
|
||||||
|
# Roslyn cache directories
|
||||||
|
*.ide/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
#NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding addin-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
## TODO: Comment the next line if you want to checkin your
|
||||||
|
## web deploy settings but do note that will include unencrypted
|
||||||
|
## passwords
|
||||||
|
*.pubxml
|
||||||
|
|
||||||
|
# NuGet Packages Directory
|
||||||
|
packages/*
|
||||||
|
## TODO: If the tool you use requires repositories.config
|
||||||
|
## uncomment the next line
|
||||||
|
#!packages/repositories.config
|
||||||
|
|
||||||
|
# Enable "build/" folder in the NuGet Packages folder since
|
||||||
|
# NuGet packages use it for MSBuild targets.
|
||||||
|
# This line needs to be after the ignore of the build folder
|
||||||
|
# (and the packages folder if the line above has been uncommented)
|
||||||
|
!packages/build/
|
||||||
|
|
||||||
|
# Windows Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Windows Store app package directory
|
||||||
|
AppPackages/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
sql/
|
||||||
|
*.Cache
|
||||||
|
ClientBin/
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 2013
|
||||||
|
VisualStudioVersion = 12.0.21005.1
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "libnpsharp", "src\libnpsharp\libnpsharp.csproj", "{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,2 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NPID/@EntryIndexedValue">NPID</s:String></wpf:ResourceDictionary>
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NPSharp
|
||||||
|
{
|
||||||
|
class NpFileException : Exception
|
||||||
|
{
|
||||||
|
public NpFileException()
|
||||||
|
:base(@"Could not fetch file from NP server.")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NPSharp.Rpc;
|
||||||
|
using NPSharp.Rpc.Packets;
|
||||||
|
|
||||||
|
namespace NPSharp
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a high-level network platform client.
|
||||||
|
/// </summary>
|
||||||
|
public class NpClient
|
||||||
|
{
|
||||||
|
private readonly RpcClientStream _rpc;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private CancellationToken _cancellationToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the NP client with a specified host and port.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">The host to connect to.</param>
|
||||||
|
/// <param name="port">The port to use. Default: 3025.</param>
|
||||||
|
public NpClient(string host, ushort port = 3025)
|
||||||
|
{
|
||||||
|
_rpc = new RpcClientStream(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The assigned NP user ID. Will be set on successful authentication.
|
||||||
|
/// </summary>
|
||||||
|
public ulong LoginId { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The assigned session token for this client. Will be set on successful authentication.
|
||||||
|
/// </summary>
|
||||||
|
public string SessionToken { get; private set; }
|
||||||
|
|
||||||
|
// TODO: Handle connection failures via exception
|
||||||
|
/// <summary>
|
||||||
|
/// Connects the client to the NP server.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the connection succeeded, otherwise false.</returns>
|
||||||
|
public bool Connect()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_cancellationToken = _cancellationTokenSource.Token;
|
||||||
|
|
||||||
|
if (!_rpc.Open())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Processing thread start");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var message = _rpc.Read();
|
||||||
|
if (message == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO: log4net
|
||||||
|
Console.WriteLine("Received packet ID {1} (type {0})", message.GetType().Name, message.MessageId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ProtocolViolationException error)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Protocol violation: {0}. Disconnect imminent.", error.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.WriteLine("Processing thread exit");
|
||||||
|
}, _cancellationToken);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects the client from the NP server.
|
||||||
|
/// </summary>
|
||||||
|
public void Disconnect()
|
||||||
|
{
|
||||||
|
_cancellationTokenSource.Cancel(true); // TODO: Find a cleaner way to cancel _processingTask (focus: _rpc.Read)
|
||||||
|
_rpc.Close();
|
||||||
|
|
||||||
|
LoginId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Try to use an exception for failed action instead
|
||||||
|
/// <summary>
|
||||||
|
/// Authenticates this connection via a token. This token has to be requested via an external interface like remauth.php.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">The token to use for authentication</param>
|
||||||
|
/// <returns>True if the login succeeded, otherwise false.</returns>
|
||||||
|
public async Task<bool> AuthenticateWithToken(string token)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
|
_rpc.AttachCallback(packet =>
|
||||||
|
{
|
||||||
|
var result = (AuthenticateResultMessage) packet;
|
||||||
|
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
|
||||||
|
/// <summary>
|
||||||
|
/// Uploads a user file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The file name to save the contents to on the server</param>
|
||||||
|
/// <param name="contents">The raw byte contents</param>
|
||||||
|
/// <returns>True if the upload succeeded, otherwise false.</returns>
|
||||||
|
public async Task<bool> UploadUserFile(string filename, byte[] contents)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
|
_rpc.AttachCallback(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a user file and returns its contents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The file to download</param>
|
||||||
|
/// <returns>File contents as byte array</returns>
|
||||||
|
public async Task<byte[]> GetUserFile(string filename)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<byte[]>();
|
||||||
|
|
||||||
|
_rpc.AttachCallback(packet =>
|
||||||
|
{
|
||||||
|
var result = (StorageUserFileMessage) packet;
|
||||||
|
if (result.Result != 0)
|
||||||
|
{
|
||||||
|
tcs.SetException(new NpFileException());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tcs.SetResult(result.FileData);
|
||||||
|
});
|
||||||
|
_rpc.Send(new StorageGetUserFileMessage {FileName = filename, NPID = LoginId});
|
||||||
|
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a user file onto the harddisk.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The file to download</param>
|
||||||
|
/// <param name="targetpath">Path where to save the file</param>
|
||||||
|
public async void DownloadUserFileTo(string filename, string targetpath)
|
||||||
|
{
|
||||||
|
var contents = await GetUserFile(filename);
|
||||||
|
|
||||||
|
File.WriteAllBytes(targetpath, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a publisher file and returns its contents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The file to download</param>
|
||||||
|
/// <returns>File contents as byte array</returns>
|
||||||
|
public async Task<byte[]> GetPublisherFile(string filename)
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<byte[]>();
|
||||||
|
|
||||||
|
_rpc.AttachCallback(packet =>
|
||||||
|
{
|
||||||
|
var result = (StoragePublisherFileMessage) packet;
|
||||||
|
if (result.Result != 0)
|
||||||
|
{
|
||||||
|
tcs.SetException(new NpFileException());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tcs.SetResult(result.FileData);
|
||||||
|
});
|
||||||
|
_rpc.Send(new StorageGetPublisherFileMessage {FileName = filename});
|
||||||
|
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Downloads a publisher file onto the harddisk.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">The file to download</param>
|
||||||
|
/// <param name="targetpath">Path where to save the file</param>
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
|
// die mit einer Assembly verknüpft sind.
|
||||||
|
[assembly: AssemblyTitle(".NET NP library")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Icedream")]
|
||||||
|
[assembly: AssemblyProduct(".NET NP library")]
|
||||||
|
[assembly: AssemblyCopyright("© 2014 Icedream")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
||||||
|
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
|
||||||
|
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||||
|
[assembly: Guid("0efdd0b5-fd69-48fd-b714-f4f0e032f417")]
|
||||||
|
|
||||||
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
//
|
||||||
|
// Hauptversion
|
||||||
|
// Nebenversion
|
||||||
|
// Buildnummer
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||||
|
// übernehmen, indem Sie "*" eingeben:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("0.1.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("0.1.0.0")]
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1006)]
|
||||||
|
[ProtoContract]
|
||||||
|
class AuthenticateExternalStatusMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Status { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1010)]
|
||||||
|
[ProtoContract]
|
||||||
|
class AuthenticateResultMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public ulong NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public string SessionToken { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1003)]
|
||||||
|
[ProtoContract]
|
||||||
|
class AuthenticateWithTokenMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(2001)]
|
||||||
|
[ProtoContract]
|
||||||
|
class CloseAppMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string Reason { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1000)]
|
||||||
|
[ProtoContract]
|
||||||
|
class HelloMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
// I seriously have no idea where in the code this is used but whatever
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Number1 { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public int Number2 { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(4)]
|
||||||
|
public string String2 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(2002)]
|
||||||
|
[ProtoContract]
|
||||||
|
class MessagingSendDataMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public ulong NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
class PacketAttribute : Attribute
|
||||||
|
{
|
||||||
|
public PacketAttribute(uint type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
public class RpcClientMessage : RpcMessage
|
||||||
|
{
|
||||||
|
public byte[] Serialize(uint id)
|
||||||
|
{
|
||||||
|
byte[] content;
|
||||||
|
using (var bufferStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
ProtoBuf.Serializer.Serialize(bufferStream, this);
|
||||||
|
bufferStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
content = bufferStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new List<byte>();
|
||||||
|
buffer.AddRange(BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder(Signature)));
|
||||||
|
buffer.AddRange(BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder(content.Length)));
|
||||||
|
buffer.AddRange(BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder(GetTypeId())));
|
||||||
|
buffer.AddRange(BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder(id)));
|
||||||
|
buffer.AddRange(content);
|
||||||
|
|
||||||
|
return buffer.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
public class RpcMessage
|
||||||
|
{
|
||||||
|
internal const uint Signature = 0xDEADC0DE; // I wonder if aiw3 changed this since kernal noted it in his source code.
|
||||||
|
|
||||||
|
public uint GetTypeId()
|
||||||
|
{
|
||||||
|
var packet = (PacketAttribute) GetType().GetCustomAttributes(typeof (PacketAttribute), false).Single();
|
||||||
|
return packet.Type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
public class RpcServerMessage : RpcMessage
|
||||||
|
{
|
||||||
|
public uint MessageId { get; private set; }
|
||||||
|
|
||||||
|
public static RpcServerMessage Deserialize(NetworkStream ns)
|
||||||
|
{
|
||||||
|
var header = new byte[16];
|
||||||
|
var l = ns.Read(header, 0, header.Length);
|
||||||
|
if (l == 0)
|
||||||
|
return null;
|
||||||
|
if (l < 16)
|
||||||
|
throw new ProtocolViolationException("Received incomplete header");
|
||||||
|
|
||||||
|
var signature = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToUInt32(header, 0));
|
||||||
|
var length = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToUInt32(header, 4));
|
||||||
|
var type = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToUInt32(header, 8));
|
||||||
|
var buffer = new byte[length];
|
||||||
|
ns.Read(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
if (signature != Signature)
|
||||||
|
throw new ProtocolViolationException("Received packet with invalid signature");
|
||||||
|
|
||||||
|
RpcServerMessage packet;
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(buffer))
|
||||||
|
{
|
||||||
|
var types = Assembly.GetExecutingAssembly().GetTypes().Where(
|
||||||
|
t =>
|
||||||
|
t.IsSubclassOf(typeof (RpcServerMessage))
|
||||||
|
&&
|
||||||
|
((PacketAttribute) t.GetCustomAttributes(typeof (PacketAttribute), false).Single()).Type == type
|
||||||
|
).ToArray();
|
||||||
|
if (!types.Any())
|
||||||
|
{
|
||||||
|
throw new ProtocolViolationException("Received packet of unknown type");
|
||||||
|
}
|
||||||
|
if (types.Count() > 1)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Debug.Fail(string.Format("Bug in program code: Found more than 1 type for packet ID {0}", type));
|
||||||
|
#else
|
||||||
|
// TODO: log4net
|
||||||
|
return null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
packet = (RpcServerMessage)ProtoBuf.Serializer.NonGeneric.Deserialize(
|
||||||
|
types.Single(),
|
||||||
|
ms
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
packet.MessageId = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToUInt32(header, 12));
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
[Packet(1101)]
|
||||||
|
class StorageGetPublisherFileMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1102)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StorageGetUserFileMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public ulong NPID { get; set; } // SERIOUSLY WHY IS THIS EVEN HERE
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1111)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StoragePublisherFileMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public byte[] FileData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1104)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StorageSendRandomStringMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string RandomString { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1112)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StorageUserFileMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public ulong NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(4)]
|
||||||
|
public byte[] FileData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1103)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StorageWriteUserFileMessage : RpcClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public ulong NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public byte[] FileData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc.Packets
|
||||||
|
{
|
||||||
|
[Packet(1113)]
|
||||||
|
[ProtoContract]
|
||||||
|
class StorageWriteUserFileResultMessage : RpcServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public ulong NPID { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using NPSharp.Rpc.Packets;
|
||||||
|
|
||||||
|
namespace NPSharp.Rpc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a low-level client stream which can communicate with an NP server using RPC packets.
|
||||||
|
/// </summary>
|
||||||
|
public class RpcClientStream
|
||||||
|
{
|
||||||
|
private NetworkStream _ns;
|
||||||
|
private uint _id;
|
||||||
|
|
||||||
|
private readonly string _host;
|
||||||
|
private readonly ushort _port;
|
||||||
|
|
||||||
|
private readonly Dictionary<uint, Action<RpcServerMessage>> _callbacks = new Dictionary<uint, Action<RpcServerMessage>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an RPC connection stream with a specified host and port.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">The host to connect to.</param>
|
||||||
|
/// <param name="port">The port to use. Default: 3025.</param>
|
||||||
|
public RpcClientStream(string host, ushort port = 3025)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the RPC stream to the NP server.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the connection succeeded, otherwise false.</returns>
|
||||||
|
public bool Open()
|
||||||
|
{
|
||||||
|
// Connection already established?
|
||||||
|
if (_ns != null)
|
||||||
|
throw new InvalidOperationException("Connection already opened");
|
||||||
|
|
||||||
|
var tcp = new TcpClient();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tcp.Connect(_host, _port);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_ns = tcp.GetStream();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the connection with the NP server.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeout"></param>
|
||||||
|
public void Close(int timeout = 2000)
|
||||||
|
{
|
||||||
|
// Connection already closed?
|
||||||
|
if (_ns == null)
|
||||||
|
throw new InvalidOperationException("Connection already closed");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_ns.Close(timeout);
|
||||||
|
_ns.Dispose();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_ns = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches a callback to the next message being sent out. This allows handling response packets.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback">The method to call when we receive a response to the next message</param>
|
||||||
|
public void AttachCallback(Action<RpcServerMessage> callback)
|
||||||
|
{
|
||||||
|
if (_callbacks.ContainsKey(_id))
|
||||||
|
throw new Exception("There is already a callback for the current message. You can only add max. one callback.");
|
||||||
|
_callbacks.Add(_id, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Exposure of message ID needed or no?
|
||||||
|
/// <summary>
|
||||||
|
/// Sends out an RPC message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The RPC message to send out.</param>
|
||||||
|
/// <returns>The new ID of the message.</returns>
|
||||||
|
public uint Send(RpcClientMessage message)
|
||||||
|
{
|
||||||
|
if (_ns == null)
|
||||||
|
throw new InvalidOperationException("You need to open the stream first.");
|
||||||
|
|
||||||
|
var buffer = message.Serialize(_id);
|
||||||
|
|
||||||
|
_ns.Write(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
return _id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the next RPC message from the server and reads it.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The received server message.</returns>
|
||||||
|
public RpcServerMessage Read()
|
||||||
|
{
|
||||||
|
if (_ns == null)
|
||||||
|
throw new InvalidOperationException("You need to open the stream first.");
|
||||||
|
|
||||||
|
var message = RpcServerMessage.Deserialize(_ns);
|
||||||
|
|
||||||
|
if (message == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!_callbacks.ContainsKey(message.MessageId))
|
||||||
|
return message;
|
||||||
|
|
||||||
|
_callbacks[message.MessageId].Invoke(message);
|
||||||
|
_callbacks.Remove(message.MessageId);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{1A5AC63A-250E-4BC8-B81A-822AC31F5E37}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>NPSharp</RootNamespace>
|
||||||
|
<AssemblyName>libnpsharp</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<Prefer32Bit>false</Prefer32Bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="protobuf-net">
|
||||||
|
<HintPath>..\packages\protobuf-net.2.0.0.668\lib\net40\protobuf-net.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="NpClient.cs" />
|
||||||
|
<Compile Include="NPFileException.cs" />
|
||||||
|
<Compile Include="Rpc\RpcClientStream.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\AuthenticateExternalStatusMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\AuthenticateResultMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\AuthenticateWithTokenMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\CloseAppMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\HelloMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\MessagingSendDataMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\RpcMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\PacketAttribute.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\RpcClientMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\RpcServerMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageGetUserFileMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageGetPublisherFileMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StoragePublisherFileMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageSendRandomStringMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageUserFileMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageWriteUserFileMessage.cs" />
|
||||||
|
<Compile Include="Rpc\Packets\StorageWriteUserFileResultMessage.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup />
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="protobuf-net" version="2.0.0.668" targetFramework="net40" />
|
||||||
|
</packages>
|
Loading…
Reference in New Issue