mirror of https://github.com/icedream/npsharp.git
Tons of server code work. This library has potential.
parent
483842b7d9
commit
d3a44df55b
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<solution>
|
<solution>
|
||||||
<add key="disableSourceControlIntegration" value="true" />
|
<add key="disableSourceControlIntegration" value="true" />
|
||||||
|
|
|
@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{587B7B
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "npfile", "src\npfile\npfile.csproj", "{19EBF339-E076-4962-A671-5B44A978687D}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "npfile", "src\npfile\npfile.csproj", "{19EBF339-E076-4962-A671-5B44A978687D}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "npserv", "npserv\npserv.csproj", "{1FF77692-D07C-4131-95AE-21AD2A74CA11}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -34,6 +36,10 @@ Global
|
||||||
{19EBF339-E076-4962-A671-5B44A978687D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{19EBF339-E076-4962-A671-5B44A978687D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{19EBF339-E076-4962-A671-5B44A978687D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{19EBF339-E076-4962-A671-5B44A978687D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{19EBF339-E076-4962-A671-5B44A978687D}.Release|Any CPU.Build.0 = Release|Any CPU
|
{19EBF339-E076-4962-A671-5B44A978687D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{1FF77692-D07C-4131-95AE-21AD2A74CA11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{1FF77692-D07C-4131-95AE-21AD2A74CA11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{1FF77692-D07C-4131-95AE-21AD2A74CA11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{1FF77692-D07C-4131-95AE-21AD2A74CA11}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NP/@EntryIndexedValue">NP</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NP/@EntryIndexedValue">NP</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NPID/@EntryIndexedValue">NPID</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NPID/@EntryIndexedValue">NPID</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RPC/@EntryIndexedValue">RPC</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UTF/@EntryIndexedValue">UTF</s:String></wpf:ResourceDictionary>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace NPSharp.CommandLine.Server
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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("npserv")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("Hewlett-Packard")]
|
||||||
|
[assembly: AssemblyProduct("npserv")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © Hewlett-Packard 2014")]
|
||||||
|
[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("098bf075-4a4b-437c-9283-011ffb6ff277")]
|
||||||
|
|
||||||
|
// 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("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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>{1FF77692-D07C-4131-95AE-21AD2A74CA11}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>NPSharp.CommandLine.Server</RootNamespace>
|
||||||
|
<AssemblyName>npserv</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<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="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</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>
|
|
@ -1,16 +1,33 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace NPSharp.Authentication
|
namespace NPSharp.Authentication
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a client which can communicate with an authentication endpoint in order to retrieve session
|
||||||
|
/// information, including tokens for authentication with NP servers.
|
||||||
|
/// </summary>
|
||||||
public class AuthenticationHelper
|
public class AuthenticationHelper
|
||||||
{
|
{
|
||||||
private string _path;
|
private readonly string _host;
|
||||||
private ushort _port;
|
private readonly string _path;
|
||||||
private string _host;
|
private readonly ushort _port;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="NPSharp.Authentication.AuthenticationHelper" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host">Hostname of the authentication endpoint.</param>
|
||||||
|
/// <param name="port">Port of the authentication endpoint.</param>
|
||||||
|
/// <param name="path">Path of the authentication endpoint.</param>
|
||||||
|
public AuthenticationHelper(string host, ushort port = 12003, string path = "/authenticate")
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
_port = port;
|
||||||
|
_path = path;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the username.
|
/// Gets the username.
|
||||||
|
@ -36,80 +53,73 @@ namespace NPSharp.Authentication
|
||||||
/// <value>The user identifier.</value>
|
/// <value>The user identifier.</value>
|
||||||
public uint UserId { get; private set; }
|
public uint UserId { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="NPSharp.Authentication.TokenRetriever"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="host">Hostname of the authentication endpoint.</param>
|
|
||||||
/// <param name="port">Port of the authentication endpoint.</param>
|
|
||||||
/// <param name="path">Path of the authentication endpoint.</param>
|
|
||||||
public AuthenticationHelper (string host, ushort port = 12003, string path = "/authenticate")
|
|
||||||
{
|
|
||||||
_host = host;
|
|
||||||
_port = port;
|
|
||||||
_path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticate the specified username and password.
|
/// Authenticate the specified username and password.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">The username to use for authentication.</param>
|
/// <param name="username">The username to use for authentication.</param>
|
||||||
/// <param name="password">The password to use for authentication.</param>
|
/// <param name="password">The password to use for authentication.</param>
|
||||||
public void Authenticate (string username, string password)
|
public void Authenticate(string username, string password)
|
||||||
{
|
{
|
||||||
var post = string.Format ("{0}&&{1}", username, password);
|
string post = string.Format("{0}&&{1}", username, password);
|
||||||
|
|
||||||
var uri = new UriBuilder {
|
Uri uri = new UriBuilder
|
||||||
|
{
|
||||||
Scheme = "http",
|
Scheme = "http",
|
||||||
Port = _port,
|
Port = _port,
|
||||||
Host = _host,
|
Host = _host,
|
||||||
Path = _path
|
Path = _path
|
||||||
}.Uri;
|
}.Uri;
|
||||||
|
|
||||||
var req = (HttpWebRequest)WebRequest.Create(uri);
|
var req = (HttpWebRequest) WebRequest.Create(uri);
|
||||||
req.Method = "POST";
|
req.Method = "POST";
|
||||||
req.ContentType = "application/x-www-form-urlencoded";
|
req.ContentType = "application/x-www-form-urlencoded";
|
||||||
req.AllowAutoRedirect = true;
|
req.AllowAutoRedirect = true;
|
||||||
using (var reqStream = req.GetRequestStream()) {
|
using (Stream reqStream = req.GetRequestStream())
|
||||||
var buffer = Encoding.UTF8.GetBytes (post);
|
{
|
||||||
reqStream.Write (buffer, 0, post.Length);
|
byte[] buffer = Encoding.UTF8.GetBytes(post);
|
||||||
reqStream.Flush ();
|
reqStream.Write(buffer, 0, post.Length);
|
||||||
|
reqStream.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response will be in this syntax:
|
// Response will be in this syntax:
|
||||||
// (ok|fail)#text#userid#username#email#sessiontoken
|
// (ok|fail)#text#userid#username#email#sessiontoken
|
||||||
var rx = new Regex("^(?<status>ok|fail)#(?<text>.+)#(?<userid>[0-9]+)#(?<username>.+)#(?<usermail>.+)#(?<sessiontoken>[^#]+)[#]*$");
|
var rx =
|
||||||
var resp = (HttpWebResponse)req.GetResponse ();
|
new Regex(
|
||||||
using (var respStream = resp.GetResponseStream()) {
|
"^(?<status>ok|fail)#(?<text>.+)#(?<userid>[0-9]+)#(?<username>.+)#(?<usermail>.+)#(?<sessiontoken>[^#]+)[#]*$");
|
||||||
|
var resp = (HttpWebResponse) req.GetResponse();
|
||||||
|
using (Stream respStream = resp.GetResponseStream())
|
||||||
|
{
|
||||||
if (respStream == null)
|
if (respStream == null)
|
||||||
throw new Exception(@"No answer from server");
|
throw new Exception(@"No answer from server");
|
||||||
using (var respReader = new StreamReader(respStream)) {
|
using (var respReader = new StreamReader(respStream))
|
||||||
while (!respReader.EndOfStream) {
|
{
|
||||||
var line = respReader.ReadLine ();
|
while (!respReader.EndOfStream)
|
||||||
|
{
|
||||||
|
string line = respReader.ReadLine();
|
||||||
|
|
||||||
// No answer?
|
// No answer?
|
||||||
if (string.IsNullOrEmpty(line))
|
if (string.IsNullOrEmpty(line))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// DW response line found?
|
// DW response line found?
|
||||||
if (!rx.IsMatch (line))
|
if (!rx.IsMatch(line))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// This is a DW response line, analyze
|
// This is a DW response line, analyze
|
||||||
var rxm = rx.Match (line);
|
Match rxm = rx.Match(line);
|
||||||
|
|
||||||
// Login succeeded?
|
// Login succeeded?
|
||||||
if (rxm.Groups ["status"].Value != "ok")
|
if (rxm.Groups["status"].Value != "ok")
|
||||||
throw new Exception (rxm.Groups ["text"].Value);
|
throw new Exception(rxm.Groups["text"].Value);
|
||||||
|
|
||||||
// Store all data
|
// Store all data
|
||||||
Username = rxm.Groups ["username"].Value;
|
Username = rxm.Groups["username"].Value;
|
||||||
UserEMail = rxm.Groups ["usermail"].Value;
|
UserEMail = rxm.Groups["usermail"].Value;
|
||||||
SessionToken = rxm.Groups ["sessiontoken"].Value;
|
SessionToken = rxm.Groups["sessiontoken"].Value;
|
||||||
UserId = uint.Parse (rxm.Groups ["userid"].Value);
|
UserId = uint.Parse(rxm.Groups["userid"].Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NPSharp
|
||||||
|
{
|
||||||
|
public class ClientEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
internal ClientEventArgs(NPServer.NPServerClient client)
|
||||||
|
{
|
||||||
|
Client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NPServer.NPServerClient Client { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -15,11 +14,12 @@ namespace NPSharp
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NPClient
|
public class NPClient
|
||||||
{
|
{
|
||||||
private readonly RPCClientStream _rpc;
|
private readonly string _host;
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private CancellationToken _cancellationToken;
|
|
||||||
private Task _procTask;
|
|
||||||
private readonly ILog _log;
|
private readonly ILog _log;
|
||||||
|
private readonly ushort _port;
|
||||||
|
private CancellationToken _cancellationToken;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
private RPCClientStream _rpc;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the NP client with a specified host and port.
|
/// Initializes the NP client with a specified host and port.
|
||||||
|
@ -28,8 +28,9 @@ namespace NPSharp
|
||||||
/// <param name="port">The port to use. Default: 3025.</param>
|
/// <param name="port">The port to use. Default: 3025.</param>
|
||||||
public NPClient(string host, ushort port = 3025)
|
public NPClient(string host, ushort port = 3025)
|
||||||
{
|
{
|
||||||
_rpc = new RPCClientStream(host, port);
|
_host = host;
|
||||||
_log = LogManager.GetLogger ("NPClient");
|
_port = port;
|
||||||
|
_log = LogManager.GetLogger("NPClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -54,17 +55,30 @@ namespace NPSharp
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
_cancellationToken = _cancellationTokenSource.Token;
|
_cancellationToken = _cancellationTokenSource.Token;
|
||||||
|
|
||||||
if (!_rpc.Open())
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
_procTask = Task.Factory.StartNew(() =>
|
Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
_log.Debug("Now receiving RPC messages");
|
_log.Debug("Now receiving RPC messages");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
_rpc.Read();
|
if (_rpc.Read() == null)
|
||||||
|
break;
|
||||||
|
_log.Debug("Disconnected.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ProtocolViolationException error)
|
catch (ProtocolViolationException error)
|
||||||
|
@ -87,7 +101,8 @@ namespace NPSharp
|
||||||
{
|
{
|
||||||
_log.Debug("Disconnect() start");
|
_log.Debug("Disconnect() start");
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel(true); // TODO: Find a cleaner way to cancel _processingTask (focus: _rpc.Read)
|
_cancellationTokenSource.Cancel(true);
|
||||||
|
// TODO: Find a cleaner way to cancel _processingTask (focus: _rpc.Read)
|
||||||
//_procTask.Wait(_cancellationToken);
|
//_procTask.Wait(_cancellationToken);
|
||||||
_rpc.Close();
|
_rpc.Close();
|
||||||
|
|
||||||
|
@ -98,7 +113,8 @@ namespace NPSharp
|
||||||
|
|
||||||
// TODO: Try to use an exception for failed action instead
|
// TODO: Try to use an exception for failed action instead
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticates this connection via a token. This token has to be requested via an external interface like remauth.php.
|
/// Authenticates this connection via a token. This token has to be requested via an external interface like
|
||||||
|
/// remauth.php.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="token">The token to use for authentication</param>
|
/// <param name="token">The token to use for authentication</param>
|
||||||
/// <returns>True if the login succeeded, otherwise false.</returns>
|
/// <returns>True if the login succeeded, otherwise false.</returns>
|
||||||
|
@ -106,7 +122,7 @@ namespace NPSharp
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
_rpc.AttachCallback(packet =>
|
_rpc.AttachHandlerForNextMessage(packet =>
|
||||||
{
|
{
|
||||||
var result = packet as AuthenticateResultMessage;
|
var result = packet as AuthenticateResultMessage;
|
||||||
if (result == null)
|
if (result == null)
|
||||||
|
@ -117,7 +133,7 @@ namespace NPSharp
|
||||||
LoginId = result.NPID;
|
LoginId = result.NPID;
|
||||||
SessionToken = result.SessionToken;
|
SessionToken = result.SessionToken;
|
||||||
tcs.SetResult(true);
|
tcs.SetResult(true);
|
||||||
}, 10);
|
});
|
||||||
_rpc.Send(new AuthenticateWithTokenMessage {Token = token});
|
_rpc.Send(new AuthenticateWithTokenMessage {Token = token});
|
||||||
|
|
||||||
return await tcs.Task;
|
return await tcs.Task;
|
||||||
|
@ -134,13 +150,13 @@ namespace NPSharp
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
_rpc.AttachCallback(packet =>
|
_rpc.AttachHandlerForNextMessage(packet =>
|
||||||
{
|
{
|
||||||
var result = (StorageWriteUserFileResultMessage) packet;
|
var result = (StorageWriteUserFileResultMessage) packet;
|
||||||
if (result.Result != 0)
|
if (result.Result != 0)
|
||||||
tcs.SetResult(false);
|
tcs.SetResult(false);
|
||||||
tcs.SetResult(true);
|
tcs.SetResult(true);
|
||||||
}, 10);
|
});
|
||||||
_rpc.Send(new StorageWriteUserFileMessage {FileData = contents, FileName = filename, NPID = LoginId});
|
_rpc.Send(new StorageWriteUserFileMessage {FileData = contents, FileName = filename, NPID = LoginId});
|
||||||
|
|
||||||
return await tcs.Task;
|
return await tcs.Task;
|
||||||
|
@ -155,7 +171,7 @@ namespace NPSharp
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<byte[]>();
|
var tcs = new TaskCompletionSource<byte[]>();
|
||||||
|
|
||||||
_rpc.AttachCallback(packet =>
|
_rpc.AttachHandlerForNextMessage(packet =>
|
||||||
{
|
{
|
||||||
var result = (StorageUserFileMessage) packet;
|
var result = (StorageUserFileMessage) packet;
|
||||||
if (result.Result != 0)
|
if (result.Result != 0)
|
||||||
|
@ -164,7 +180,7 @@ namespace NPSharp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tcs.SetResult(result.FileData);
|
tcs.SetResult(result.FileData);
|
||||||
}, 10);
|
});
|
||||||
_rpc.Send(new StorageGetUserFileMessage {FileName = filename, NPID = LoginId});
|
_rpc.Send(new StorageGetUserFileMessage {FileName = filename, NPID = LoginId});
|
||||||
|
|
||||||
return await tcs.Task;
|
return await tcs.Task;
|
||||||
|
@ -178,7 +194,7 @@ namespace NPSharp
|
||||||
/// <param name="targetpath">Path where to save the file</param>
|
/// <param name="targetpath">Path where to save the file</param>
|
||||||
public async void DownloadUserFileTo(string filename, string targetpath)
|
public async void DownloadUserFileTo(string filename, string targetpath)
|
||||||
{
|
{
|
||||||
var contents = await GetUserFile(filename);
|
byte[] contents = await GetUserFile(filename);
|
||||||
|
|
||||||
File.WriteAllBytes(targetpath, contents);
|
File.WriteAllBytes(targetpath, contents);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +209,7 @@ namespace NPSharp
|
||||||
{
|
{
|
||||||
var tcs = new TaskCompletionSource<byte[]>();
|
var tcs = new TaskCompletionSource<byte[]>();
|
||||||
|
|
||||||
_rpc.AttachCallback(packet =>
|
_rpc.AttachHandlerForNextMessage(packet =>
|
||||||
{
|
{
|
||||||
var result = (StoragePublisherFileMessage) packet;
|
var result = (StoragePublisherFileMessage) packet;
|
||||||
if (result.Result != 0)
|
if (result.Result != 0)
|
||||||
|
@ -202,7 +218,7 @@ namespace NPSharp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tcs.SetResult(result.FileData);
|
tcs.SetResult(result.FileData);
|
||||||
}, 10);
|
});
|
||||||
_rpc.Send(new StorageGetPublisherFileMessage {FileName = filename});
|
_rpc.Send(new StorageGetPublisherFileMessage {FileName = filename});
|
||||||
|
|
||||||
return await tcs.Task;
|
return await tcs.Task;
|
||||||
|
@ -215,14 +231,14 @@ namespace NPSharp
|
||||||
/// <param name="targetpath">Path where to save the file</param>
|
/// <param name="targetpath">Path where to save the file</param>
|
||||||
public async void DownloadPublisherFileTo(string filename, string targetpath)
|
public async void DownloadPublisherFileTo(string filename, string targetpath)
|
||||||
{
|
{
|
||||||
var contents = await GetPublisherFile(filename);
|
byte[] contents = await GetPublisherFile(filename);
|
||||||
|
|
||||||
File.WriteAllBytes(targetpath, contents);
|
File.WriteAllBytes(targetpath, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendRandomString(string data)
|
public void SendRandomString(string data)
|
||||||
{
|
{
|
||||||
_rpc.Send(new StorageSendRandomStringMessage() { RandomString=data });
|
_rpc.Send(new StorageSendRandomStringMessage {RandomString = data});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace NPSharp
|
namespace NPSharp
|
||||||
{
|
{
|
||||||
class NpFileException : Exception
|
internal class NpFileException : Exception
|
||||||
{
|
{
|
||||||
internal NpFileException(int error)
|
internal NpFileException(int error)
|
||||||
: base(error == 1 ? @"File not found on NP server" : @"Internal error on NP server")
|
: base(error == 1 ? @"File not found on NP server" : @"Internal error on NP server")
|
||||||
|
|
|
@ -0,0 +1,443 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Configuration;
|
||||||
|
using log4net;
|
||||||
|
using NPSharp.RPC;
|
||||||
|
using NPSharp.RPC.Messages;
|
||||||
|
using NPSharp.Steam;
|
||||||
|
|
||||||
|
namespace NPSharp
|
||||||
|
{
|
||||||
|
public class NPServer
|
||||||
|
{
|
||||||
|
public delegate void ClientEventHandler(object sender, ClientEventArgs args);
|
||||||
|
|
||||||
|
public IFileServingHandler FileHandler { get; set; }
|
||||||
|
|
||||||
|
public IUserAvatarHandler UserAvatarHandler { get; set; }
|
||||||
|
|
||||||
|
public interface IUserAvatarHandler
|
||||||
|
{
|
||||||
|
byte[] GetUserAvatar(CSteamID id);
|
||||||
|
|
||||||
|
byte[] GetDefaultAvatar();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<NPServerClient> _clients;
|
||||||
|
private readonly ILog _log;
|
||||||
|
|
||||||
|
public NPServer()
|
||||||
|
{
|
||||||
|
_log = LogManager.GetLogger("NPServer");
|
||||||
|
_clients = new List<NPServerClient>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _handleClient(NPServerClient client)
|
||||||
|
{
|
||||||
|
#region RPC authentication message handlers
|
||||||
|
client.RPC.AttachHandlerForMessageType<AuthenticateWithKeyMessage>(msg =>
|
||||||
|
{
|
||||||
|
var result = new AuthenticationResult();;
|
||||||
|
if (AuthenticationHandler != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = AuthenticationHandler.AuthenticateServer(client, msg.LicenseKey);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
_log.Error("Error occurred in authentication handler", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send authentication result directly to client
|
||||||
|
client.RPC.Send(new AuthenticateResultMessage
|
||||||
|
{
|
||||||
|
NPID = result.UserID,
|
||||||
|
Result = result.Result ? 0 : 1,
|
||||||
|
SessionToken = string.Empty
|
||||||
|
});
|
||||||
|
|
||||||
|
// Authentication failed => close connection
|
||||||
|
if (!result.Result)
|
||||||
|
{
|
||||||
|
client.RPC.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign login ID
|
||||||
|
client.UserID = result.UserID;
|
||||||
|
client.IsServer = true;
|
||||||
|
|
||||||
|
OnClientAuthenticated(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.RPC.AttachHandlerForMessageType<AuthenticateWithDetailsMessage>(msg =>
|
||||||
|
{
|
||||||
|
var result = new AuthenticationResult();
|
||||||
|
if (AuthenticationHandler != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = AuthenticationHandler.AuthenticateUser(client, msg.Username, msg.Password);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
_log.Error("Error occurred in authentication handler", error);
|
||||||
|
result = new AuthenticationResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send authentication result directly to client
|
||||||
|
client.RPC.Send(new AuthenticateResultMessage
|
||||||
|
{
|
||||||
|
NPID = result.UserID,
|
||||||
|
Result = result.Result ? 0 : 1,
|
||||||
|
SessionToken = string.Empty
|
||||||
|
});
|
||||||
|
|
||||||
|
// Authentication failed => close connection
|
||||||
|
if (!result.Result)
|
||||||
|
{
|
||||||
|
client.RPC.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign login ID
|
||||||
|
client.UserID = result.UserID;
|
||||||
|
|
||||||
|
// Send "online" notification to all friends of this player
|
||||||
|
foreach (var fconn in client.FriendConnections)
|
||||||
|
{
|
||||||
|
fconn.RPC.Send(new FriendsPresenceMessage
|
||||||
|
{
|
||||||
|
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.NPID,
|
||||||
|
Friend = client.UserID,
|
||||||
|
Presence = client.PresenceData,
|
||||||
|
PresenceState = client.DedicatedServer == null ? 1 : 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OnClientAuthenticated(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.RPC.AttachHandlerForMessageType<AuthenticateWithTokenMessage>(msg =>
|
||||||
|
{
|
||||||
|
var result = new AuthenticationResult();
|
||||||
|
if (AuthenticationHandler != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = AuthenticationHandler.AuthenticateUser(client, msg.Token);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
_log.Error("Error occurred in authentication handler", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send authentication result directly to client
|
||||||
|
client.RPC.Send(new AuthenticateResultMessage
|
||||||
|
{
|
||||||
|
NPID = result.UserID,
|
||||||
|
Result = result.Result ? 0 : 1,
|
||||||
|
SessionToken = msg.Token
|
||||||
|
});
|
||||||
|
|
||||||
|
// Authentication failed => close connection
|
||||||
|
if (!result.Result)
|
||||||
|
{
|
||||||
|
client.RPC.Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign login ID
|
||||||
|
client.UserID = result.UserID;
|
||||||
|
|
||||||
|
// Send "online" notification to all friends of this player
|
||||||
|
foreach (var fconn in client.FriendConnections)
|
||||||
|
{
|
||||||
|
fconn.RPC.Send(new FriendsPresenceMessage
|
||||||
|
{
|
||||||
|
CurrentServer = client.DedicatedServer == null ? 0 : client.DedicatedServer.UserID,
|
||||||
|
Friend = client.UserID,
|
||||||
|
Presence = client.PresenceData,
|
||||||
|
PresenceState = client.DedicatedServer == null ? 1 : 2
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
OnClientAuthenticated(client);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.RPC.AttachHandlerForMessageType<AuthenticateValidateTicketMessage>(msg =>
|
||||||
|
{
|
||||||
|
var validTicket = false;
|
||||||
|
|
||||||
|
if (!client.IsDirty)
|
||||||
|
{
|
||||||
|
Ticket ticketData = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ticketData = new Ticket(msg.Ticket);
|
||||||
|
|
||||||
|
_log.DebugFormat("Ticket[Version={0},ServerID={1},Time={2}]", ticketData.Version,
|
||||||
|
ticketData.ServerID, ticketData.Time);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ArgumentException error)
|
||||||
|
{
|
||||||
|
_log.Warn("Got some weird-length ticket data", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketData != null)
|
||||||
|
{
|
||||||
|
if (ticketData.Version == 1) // Version 1 enforcement
|
||||||
|
{
|
||||||
|
if (ticketData.ClientID == client.UserID) // NPID enforcement
|
||||||
|
{
|
||||||
|
var s = _clients.Where(c => c.IsServer && !c.IsDirty && c.UserID == ticketData.ServerID).ToArray();
|
||||||
|
|
||||||
|
if (s.Any())
|
||||||
|
{
|
||||||
|
// TODO: Time validation. Problem is some clocks go wrong by minutes!
|
||||||
|
client.DedicatedServer = s.First();
|
||||||
|
validTicket = true;
|
||||||
|
_log.Debug("Ticket validated");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_log.Warn("Ticket invalid, could not find any sane servers with requested server ID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_log.Warn("Ticket invalid, found NPID spoofing attempt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_log.Warn("Ticket invalid, found invalid version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_log.Warn("Ticket invalid, client is marked as dirty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid data buffer
|
||||||
|
client.RPC.Send(new AuthenticateValidateTicketResultMessage
|
||||||
|
{
|
||||||
|
GroupID = client.GroupID,
|
||||||
|
NPID = client.UserID,
|
||||||
|
Result = validTicket ? 0 : 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RPC friend message handlers
|
||||||
|
client.RPC.AttachHandlerForMessageType<FriendsSetPresenceMessage>(msg =>
|
||||||
|
{
|
||||||
|
foreach (var pdata in msg.Presence)
|
||||||
|
{
|
||||||
|
client.SetPresence(pdata.Key, pdata.Value);
|
||||||
|
_log.DebugFormat("Client says presence \"{0}\" is \"{1}\"", pdata.Key, pdata.Value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.RPC.AttachHandlerForMessageType<FriendsGetUserAvatarMessage>(msg =>
|
||||||
|
{
|
||||||
|
// Why so goddamn complicated, NTA. Fuck.
|
||||||
|
// TODO: Not compatible with non-public accounts
|
||||||
|
var npid = new CSteamID((uint) msg.Guid, EUniverse.Public,
|
||||||
|
EAccountType.Individual).ConvertToUint64();
|
||||||
|
|
||||||
|
var avatar = UserAvatarHandler.GetUserAvatar(npid) ?? UserAvatarHandler.GetDefaultAvatar();
|
||||||
|
|
||||||
|
client.RPC.Send(new FriendsGetUserAvatarResultMessage
|
||||||
|
{
|
||||||
|
FileData = avatar,
|
||||||
|
Guid = msg.Guid,
|
||||||
|
Result = avatar != null ? 0 : 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.RPC.AttachHandlerForMessageType<FriendsGetProfileDataMessage>(msg =>
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// TODO: RPC message handling for storage
|
||||||
|
// TODO: RPC message handling for MessagingSendData
|
||||||
|
// TODO: RPC message handling for server sessions
|
||||||
|
|
||||||
|
_clients.Add(client);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_log.Debug("Client connected");
|
||||||
|
OnClientConnected(client);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var msg = client.RPC.Read();
|
||||||
|
if (msg == null)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_log.Debug("Client disconnected");
|
||||||
|
OnClientDisconnected(client);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
_log.Error("Error in client handling loop", error);
|
||||||
|
client.RPC.Send(new CloseAppMessage{Reason="Server-side error occurred, try again later."});
|
||||||
|
client.RPC.Close();
|
||||||
|
}
|
||||||
|
_clients.Remove(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NPServerClient[] Clients
|
||||||
|
{
|
||||||
|
get { return _clients.ToArray(); } // Avoid race condition by IEnum changes
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IFileServingHandler
|
||||||
|
{
|
||||||
|
Stream ReadUserFile(NPServerClient client, string file);
|
||||||
|
|
||||||
|
Stream ReadPublisherFile(NPServerClient client, string file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAuthenticationHandler AuthenticationHandler { get; set; }
|
||||||
|
|
||||||
|
public interface IAuthenticationHandler
|
||||||
|
{
|
||||||
|
AuthenticationResult AuthenticateUser(NPServerClient client, string username, string password);
|
||||||
|
|
||||||
|
AuthenticationResult AuthenticateUser(NPServerClient client, string token);
|
||||||
|
|
||||||
|
AuthenticationResult AuthenticateServer(NPServerClient client, string licenseKey);
|
||||||
|
|
||||||
|
TicketValidationResult ValidateTicket(NPServerClient client, NPServerClient server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AuthenticationResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an authentication result instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="npid">Set this to null if authentication should fail, otherwise use an instance of a steam ID which is unique to the user.</param>
|
||||||
|
public AuthenticationResult(CSteamID npid = null)
|
||||||
|
{
|
||||||
|
UserID = npid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Result { get { return UserID != null; } }
|
||||||
|
|
||||||
|
public CSteamID UserID { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TicketValidationResult
|
||||||
|
{
|
||||||
|
Valid = 0,
|
||||||
|
Invalid = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFriendsHandler FriendsHandler { get; set; }
|
||||||
|
|
||||||
|
public interface IFriendsHandler
|
||||||
|
{
|
||||||
|
IEnumerable<FriendDetails> GetFriends(NPServerClient client);
|
||||||
|
|
||||||
|
/*
|
||||||
|
void SetFriendStatus(NPServerClient client, PresenceState presenceState,
|
||||||
|
Dictionary<string, string> presenceData, ulong serverID)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NPServerClient
|
||||||
|
{
|
||||||
|
internal NPServerClient(NPServer np, RPCServerStream rpcclient)
|
||||||
|
{
|
||||||
|
NP = np;
|
||||||
|
RPC = rpcclient;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly RPCServerStream RPC;
|
||||||
|
|
||||||
|
internal readonly NPServer NP;
|
||||||
|
|
||||||
|
public CSteamID UserID { get; internal set; }
|
||||||
|
|
||||||
|
public IEnumerable<FriendDetails> Friends
|
||||||
|
{
|
||||||
|
get { return NP.FriendsHandler.GetFriends(this).ToArray(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<NPServerClient> FriendConnections
|
||||||
|
{
|
||||||
|
get { return NP.Clients.Where(c => Friends.Any(f => f.NPID == c.NPID)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal NPServerClient DedicatedServer;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, string> _presence = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
public FriendsPresence[] PresenceData { get
|
||||||
|
{
|
||||||
|
return _presence.Select(i => new FriendsPresence {Key = i.Key, Value = i.Value}).ToArray();
|
||||||
|
} }
|
||||||
|
|
||||||
|
public bool IsServer { get; set; }
|
||||||
|
public bool IsDirty { get; set; }
|
||||||
|
public int GroupID { get; set; }
|
||||||
|
|
||||||
|
internal void SetPresence(string key, string value)
|
||||||
|
{
|
||||||
|
if (!_presence.ContainsKey(key))
|
||||||
|
_presence.Add(key, value);
|
||||||
|
else
|
||||||
|
_presence[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PresenceState
|
||||||
|
{
|
||||||
|
Offline = 0,
|
||||||
|
Online = 1,
|
||||||
|
Playing = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
public event ClientEventHandler ClientConnected;
|
||||||
|
|
||||||
|
protected virtual void OnClientConnected(NPServerClient client)
|
||||||
|
{
|
||||||
|
var handler = ClientConnected;
|
||||||
|
var args = new ClientEventArgs(client);
|
||||||
|
if (handler != null) handler(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event ClientEventHandler ClientDisconnected;
|
||||||
|
|
||||||
|
protected virtual void OnClientDisconnected(NPServerClient client)
|
||||||
|
{
|
||||||
|
var handler = ClientDisconnected;
|
||||||
|
var args = new ClientEventArgs(client);
|
||||||
|
if (handler != null) handler(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public event ClientEventHandler ClientAuthenticated;
|
||||||
|
|
||||||
|
protected virtual void OnClientAuthenticated(NPServerClient client)
|
||||||
|
{
|
||||||
|
var handler = ClientAuthenticated;
|
||||||
|
var args = new ClientEventArgs(client);
|
||||||
|
if (handler != null) handler(this, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ using System.Runtime.InteropServices;
|
||||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
// die mit einer Assembly verknüpft sind.
|
// die mit einer Assembly verknüpft sind.
|
||||||
|
|
||||||
[assembly: AssemblyTitle("NPSharp Library")]
|
[assembly: AssemblyTitle("NPSharp Library")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
@ -16,9 +17,11 @@ using System.Runtime.InteropServices;
|
||||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
// 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
|
// 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.
|
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||||
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
// 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")]
|
[assembly: Guid("0efdd0b5-fd69-48fd-b714-f4f0e032f417")]
|
||||||
|
|
||||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
@ -31,5 +34,6 @@ using System.Runtime.InteropServices;
|
||||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||||
// übernehmen, indem Sie "*" eingeben:
|
// übernehmen, indem Sie "*" eingeben:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("0.1.*")]
|
[assembly: AssemblyVersion("0.1.*")]
|
||||||
[assembly: AssemblyFileVersion("0.1")]
|
[assembly: AssemblyFileVersion("0.1")]
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1004)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateRegisterServerMessage : RPCClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1, IsRequired=false)]
|
||||||
|
public string ConfigPath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1022)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateRegisterServerResultMessage : RPCServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string LicenseKey { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public int ServerID { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1011)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateUserGroupMessage : RPCServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int GroupID { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1003)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateValidateTicketMessage : RPCClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1, DataFormat = DataFormat.FixedSize)]
|
||||||
|
public UInt32 ClientIP { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2, DataFormat = DataFormat.FixedSize)]
|
||||||
|
public UInt64 NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public byte[] Ticket { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1012)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateValidateTicketResultMessage : RPCServerMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public int Result { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2, DataFormat=DataFormat.FixedSize)]
|
||||||
|
public UInt64 NPID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public int GroupID { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1002)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateWithDetailsMessage : RPCClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC.Messages
|
||||||
|
{
|
||||||
|
[Packet(1001)]
|
||||||
|
[ProtoContract]
|
||||||
|
public sealed class AuthenticateWithKeyMessage : RPCClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public string LicenseKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,9 @@ namespace NPSharp.RPC.Messages
|
||||||
[ProtoContract]
|
[ProtoContract]
|
||||||
public sealed class FriendDetails
|
public sealed class FriendDetails
|
||||||
{
|
{
|
||||||
internal FriendDetails() { }
|
internal FriendDetails()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public UInt64 NPID { get; set; }
|
public UInt64 NPID { get; set; }
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace NPSharp.RPC.Messages
|
||||||
public sealed class FriendsGetProfileDataMessage : RPCClientMessage
|
public sealed class FriendsGetProfileDataMessage : RPCClientMessage
|
||||||
{
|
{
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public UInt64[] IDs { get; set; }
|
public UInt64[] FriendIDs { get; set; }
|
||||||
|
|
||||||
[ProtoMember(2)]
|
[ProtoMember(2)]
|
||||||
public string ProfileType { get; set; }
|
public string ProfileType { get; set; }
|
||||||
|
|
|
@ -6,7 +6,9 @@ namespace NPSharp.RPC.Messages
|
||||||
[Packet(1215)]
|
[Packet(1215)]
|
||||||
public sealed class FriendsGetUserAvatarResultMessage : RPCServerMessage
|
public sealed class FriendsGetUserAvatarResultMessage : RPCServerMessage
|
||||||
{
|
{
|
||||||
internal FriendsGetUserAvatarResultMessage() { }
|
internal FriendsGetUserAvatarResultMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public int Result { get; internal set; }
|
public int Result { get; internal set; }
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace NPSharp.RPC.Messages
|
||||||
{
|
{
|
||||||
[ProtoContract]
|
[ProtoContract]
|
||||||
[Packet(1212)]
|
[Packet(1212)]
|
||||||
public sealed class FriendsPresenceMessage
|
public sealed class FriendsPresenceMessage : RPCServerMessage
|
||||||
{
|
{
|
||||||
internal FriendsPresenceMessage()
|
internal FriendsPresenceMessage()
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,7 +6,9 @@ namespace NPSharp.RPC.Messages
|
||||||
[Packet(1211)]
|
[Packet(1211)]
|
||||||
public sealed class FriendsRosterMessage : RPCServerMessage
|
public sealed class FriendsRosterMessage : RPCServerMessage
|
||||||
{
|
{
|
||||||
internal FriendsRosterMessage() { }
|
internal FriendsRosterMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public FriendDetails[] Friends { get; set; }
|
public FriendDetails[] Friends { get; set; }
|
||||||
|
|
|
@ -6,7 +6,9 @@ namespace NPSharp.RPC.Messages
|
||||||
[ProtoContract]
|
[ProtoContract]
|
||||||
public sealed class ProfileDataResult
|
public sealed class ProfileDataResult
|
||||||
{
|
{
|
||||||
internal ProfileDataResult() { }
|
internal ProfileDataResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[ProtoMember(1)]
|
[ProtoMember(1)]
|
||||||
public UInt64 NPID { get; set; }
|
public UInt64 NPID { get; set; }
|
||||||
|
|
|
@ -1,83 +1,6 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using log4net;
|
|
||||||
|
|
||||||
namespace NPSharp.RPC.Messages
|
namespace NPSharp.RPC.Messages
|
||||||
{
|
{
|
||||||
public abstract class RPCClientMessage : RPCMessage
|
public abstract class RPCClientMessage : RPCMessage
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly ILog Log;
|
|
||||||
|
|
||||||
static RPCClientMessage()
|
|
||||||
{
|
|
||||||
Log = LogManager.GetLogger("RPCClientMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] Serialize(uint id)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
Log.DebugFormat("RPCClientMessage[ID={0},Type={1},TypeName={2}] {{", id, GetTypeId(), GetType().Name);
|
|
||||||
foreach (var prop in GetType().GetProperties())
|
|
||||||
{
|
|
||||||
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(this));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
byte[] content;
|
|
||||||
using (var bufferStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
ProtoBuf.Serializer.Serialize(bufferStream, this);
|
|
||||||
bufferStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
content = bufferStream.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.DebugFormat("}} => Serialized to {0} bytes", content.Length);
|
|
||||||
|
|
||||||
byte[] buffArray;
|
|
||||||
using (var ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
using (var bw = new BinaryWriter(ms))
|
|
||||||
{
|
|
||||||
bw.Write(Signature);
|
|
||||||
bw.Write((uint)content.Length);
|
|
||||||
bw.Write(GetTypeId());
|
|
||||||
bw.Write(id);
|
|
||||||
bw.Write(content);
|
|
||||||
bw.Flush();
|
|
||||||
|
|
||||||
ms.Seek(0, SeekOrigin.Begin);
|
|
||||||
buffArray = ms.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Console.Write("\t");
|
|
||||||
|
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
|
||||||
Console.Write(BitConverter.ToString(buffArray, 0, sizeof(uint)).Replace("-", ""));
|
|
||||||
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkRed;
|
|
||||||
Console.Write(BitConverter.ToString(buffArray, 1 * sizeof(uint), sizeof(uint)).Replace("-", ""));
|
|
||||||
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
|
||||||
Console.Write(BitConverter.ToString(buffArray, 2 * sizeof(uint), sizeof(uint)).Replace("-", ""));
|
|
||||||
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
|
||||||
Console.Write(BitConverter.ToString(buffArray, 3 * sizeof(uint), sizeof(uint)).Replace("-", ""));
|
|
||||||
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.ForegroundColor = ConsoleColor.Magenta;
|
|
||||||
Console.Write(BitConverter.ToString(buffArray, 4 * sizeof(uint)).Replace("-", ""));
|
|
||||||
|
|
||||||
Console.ResetColor();
|
|
||||||
Console.WriteLine();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return buffArray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,162 @@
|
||||||
using System.Linq;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Reflection;
|
||||||
|
using log4net;
|
||||||
|
using ProtoBuf;
|
||||||
|
|
||||||
namespace NPSharp.RPC.Messages
|
namespace NPSharp.RPC.Messages
|
||||||
{
|
{
|
||||||
public abstract class RPCMessage
|
public abstract class RPCMessage
|
||||||
{
|
{
|
||||||
internal const uint Signature = 0xDEADC0DE; // I wonder if aiw3 changed this since kernal noted it in his source code.
|
internal const uint Signature = 0xDEADC0DE;
|
||||||
|
// I wonder if aiw3 changed this since kernal noted it in his source code.
|
||||||
|
|
||||||
|
private static readonly ILog Log;
|
||||||
|
|
||||||
|
static RPCMessage()
|
||||||
|
{
|
||||||
|
Log = LogManager.GetLogger("RPC");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint MessageId { get; set; }
|
||||||
|
|
||||||
public uint GetTypeId()
|
public uint GetTypeId()
|
||||||
{
|
{
|
||||||
var packet = (PacketAttribute) GetType().GetCustomAttributes(typeof (PacketAttribute), false).Single();
|
var packet = (PacketAttribute) GetType().GetCustomAttributes(typeof (PacketAttribute), false).Single();
|
||||||
return packet.Type;
|
return packet.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(Socket sock) where T : RPCMessage
|
||||||
|
{
|
||||||
|
var header = new byte[4*sizeof (uint)];
|
||||||
|
|
||||||
|
while (sock.Connected && !sock.Poll(1000, SelectMode.SelectRead)) { }
|
||||||
|
|
||||||
|
if (!sock.Connected)
|
||||||
|
{
|
||||||
|
// Socket disconnected
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var l = sock.Receive(header);
|
||||||
|
if (l == 0)
|
||||||
|
{
|
||||||
|
Log.Debug("Received 0 bytes");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (l < 16)
|
||||||
|
{
|
||||||
|
Log.ErrorFormat("Received incomplete header ({0} bytes of 16 wanted bytes)", l);
|
||||||
|
throw new ProtocolViolationException("Received incomplete header");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint signature, length, type, pid;
|
||||||
|
using (var ms = new MemoryStream(header))
|
||||||
|
{
|
||||||
|
using (var br = new BinaryReader(ms))
|
||||||
|
{
|
||||||
|
signature = br.ReadUInt32();
|
||||||
|
length = br.ReadUInt32();
|
||||||
|
type = br.ReadUInt32();
|
||||||
|
pid = br.ReadUInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = new byte[length];
|
||||||
|
sock.Receive(buffer);
|
||||||
|
|
||||||
|
if (signature != Signature)
|
||||||
|
{
|
||||||
|
Log.Error("Received invalid signature");
|
||||||
|
throw new ProtocolViolationException("Received packet with invalid signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
T packet;
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(buffer))
|
||||||
|
{
|
||||||
|
var types = Assembly.GetExecutingAssembly().GetTypes().Where(
|
||||||
|
t =>
|
||||||
|
t.IsSubclassOf(typeof (T))
|
||||||
|
&&
|
||||||
|
((PacketAttribute) t.GetCustomAttributes(typeof (PacketAttribute), false).Single()).Type == type
|
||||||
|
).ToArray();
|
||||||
|
if (!types.Any())
|
||||||
|
{
|
||||||
|
throw new ProtocolViolationException(string.Format("Received packet of unknown type ({0})", 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
|
||||||
|
return null;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
packet = (T) Serializer.NonGeneric.Deserialize(
|
||||||
|
types.Single(),
|
||||||
|
ms
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
packet.MessageId = pid;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Log.DebugFormat("{3}[ID={0},Type={1},TypeName={2}] {{", pid, packet.GetTypeId(), packet.GetType().Name,
|
||||||
|
typeof (T).Name);
|
||||||
|
foreach (
|
||||||
|
var prop in
|
||||||
|
packet.GetType().GetProperties().Where(p => !(p.DeclaringType == typeof (RPCServerMessage))))
|
||||||
|
{
|
||||||
|
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(packet));
|
||||||
|
}
|
||||||
|
Log.DebugFormat("}} // deserialized from {0} bytes", header.Length + buffer.Length);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
byte[] content;
|
||||||
|
using (var bufferStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
Serializer.Serialize(bufferStream, this);
|
||||||
|
bufferStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
content = bufferStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buffArray;
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var bw = new BinaryWriter(ms))
|
||||||
|
{
|
||||||
|
bw.Write(Signature);
|
||||||
|
bw.Write((uint) content.Length);
|
||||||
|
bw.Write(GetTypeId());
|
||||||
|
bw.Write(MessageId);
|
||||||
|
bw.Write(content);
|
||||||
|
bw.Flush();
|
||||||
|
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
buffArray = ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Log.DebugFormat("{3}[ID={0},Type={1},TypeName={2}] {{", MessageId, GetTypeId(), GetType().Name,
|
||||||
|
GetType().Name);
|
||||||
|
foreach (var prop in GetType().GetProperties())
|
||||||
|
{
|
||||||
|
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(this));
|
||||||
|
}
|
||||||
|
Log.DebugFormat("}} // serialized to {0} bytes", buffArray.Length);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return buffArray;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,105 +1,6 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Reflection;
|
|
||||||
using log4net;
|
|
||||||
|
|
||||||
namespace NPSharp.RPC.Messages
|
namespace NPSharp.RPC.Messages
|
||||||
{
|
{
|
||||||
public abstract class RPCServerMessage : RPCMessage
|
public abstract class RPCServerMessage : RPCMessage
|
||||||
{
|
{
|
||||||
|
|
||||||
private static readonly ILog Log;
|
|
||||||
|
|
||||||
static RPCServerMessage()
|
|
||||||
{
|
|
||||||
Log = LogManager.GetLogger("RPCServerMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal constructor to make classes unconstructible from outside
|
|
||||||
internal RPCServerMessage() { }
|
|
||||||
|
|
||||||
public uint MessageId { get; private set; }
|
|
||||||
|
|
||||||
public static RPCServerMessage Deserialize(NetworkStream ns)
|
|
||||||
{
|
|
||||||
var header = new byte[4 * sizeof(uint)];
|
|
||||||
Log.Debug("Reading...");
|
|
||||||
var l = ns.Read(header, 0, header.Length);
|
|
||||||
if (l == 0)
|
|
||||||
{
|
|
||||||
Log.Debug("Received 0 bytes");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (l < 16)
|
|
||||||
{
|
|
||||||
Log.ErrorFormat("Received incomplete header ({0} bytes of 16 wanted bytes)", l);
|
|
||||||
throw new ProtocolViolationException("Received incomplete header");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint signature, length, type, pid;
|
|
||||||
using (var ms = new MemoryStream(header))
|
|
||||||
{
|
|
||||||
using (var br = new BinaryReader(ms))
|
|
||||||
{
|
|
||||||
signature = br.ReadUInt32();
|
|
||||||
length = br.ReadUInt32();
|
|
||||||
type = br.ReadUInt32();
|
|
||||||
pid = br.ReadUInt32();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = new byte[length];
|
|
||||||
ns.Read(buffer, 0, buffer.Length);
|
|
||||||
|
|
||||||
if (signature != Signature)
|
|
||||||
{
|
|
||||||
Log.Error("Received invalid 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(string.Format("Received packet of unknown type ({0})", 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
|
|
||||||
return null;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
packet = (RPCServerMessage)ProtoBuf.Serializer.NonGeneric.Deserialize(
|
|
||||||
types.Single(),
|
|
||||||
ms
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.MessageId = pid;
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Log.DebugFormat("RPCServerMessage[ID={0},Type={1},TypeName={2}] {{", pid, packet.GetTypeId(), packet.GetType().Name);
|
|
||||||
foreach (var prop in packet.GetType().GetProperties().Where(p => !(p.DeclaringType == typeof (RPCServerMessage))))
|
|
||||||
{
|
|
||||||
Log.DebugFormat("\t{0} = {1}", prop.Name, prop.GetValue(packet));
|
|
||||||
}
|
|
||||||
Log.DebugFormat("}} => Read from {0} bytes", header.Length + buffer.Length);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,154 +1,25 @@
|
||||||
using System;
|
using System.Net.Sockets;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using log4net;
|
|
||||||
using NPSharp.RPC.Messages;
|
using NPSharp.RPC.Messages;
|
||||||
|
|
||||||
namespace NPSharp.RPC
|
namespace NPSharp.RPC
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a low-level client stream which can communicate with an NP server using RPC packets.
|
/// Represents a low-level client stream which can communicate with an NP server using RPC messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RPCClientStream
|
public class RPCClientStream : RPCStream<RPCClientMessage, RPCServerMessage>
|
||||||
{
|
{
|
||||||
private NetworkStream _ns;
|
public RPCClientStream(Socket sock) : base(sock)
|
||||||
private uint _id;
|
|
||||||
private readonly ILog _log;
|
|
||||||
|
|
||||||
private readonly string _host;
|
|
||||||
private readonly ushort _port;
|
|
||||||
|
|
||||||
private readonly Dictionary<uint, Tuple<DateTime, Action<RPCServerMessage>>> _callbacks = new Dictionary<uint, Tuple<DateTime, 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;
|
|
||||||
_log = LogManager.GetLogger("RPC");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public static RPCClientStream Open(string host, ushort port = 3025)
|
||||||
/// Opens the RPC stream to the NP server.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if the connection succeeded, otherwise false.</returns>
|
|
||||||
public bool Open()
|
|
||||||
{
|
{
|
||||||
_log.Debug("Open() start");
|
var sock = new Socket(
|
||||||
|
AddressFamily.InterNetwork,
|
||||||
// Connection already established?
|
SocketType.Stream,
|
||||||
if (_ns != null)
|
ProtocolType.Tcp);
|
||||||
throw new InvalidOperationException("Connection already opened");
|
sock.Connect(host, port);
|
||||||
|
return new RPCClientStream(sock);
|
||||||
var tcp = new TcpClient();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tcp.Connect(_host, _port);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ns = tcp.GetStream();
|
|
||||||
|
|
||||||
_log.Debug("Open() end");
|
|
||||||
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
|
|
||||||
{
|
|
||||||
_callbacks.Clear();
|
|
||||||
_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>
|
|
||||||
/// <param name="timeout">Time in seconds from now in which this callback will expire for the next packet</param>
|
|
||||||
public void AttachCallback(Action<RPCServerMessage> callback, double timeout)
|
|
||||||
{
|
|
||||||
_cleanupCallbacks();
|
|
||||||
_log.DebugFormat("AttachCallback for packet id {0}", _id);
|
|
||||||
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, new Tuple<DateTime, Action<RPCServerMessage>>(DateTime.Now + TimeSpan.FromSeconds(timeout), 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);
|
|
||||||
_ns.Flush();
|
|
||||||
|
|
||||||
_log.DebugFormat("Sent packet ID {1} (type {0})", message.GetType().Name, _id);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
_log.Debug("Recv NULL message");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_callbacks.ContainsKey(message.MessageId))
|
|
||||||
return message;
|
|
||||||
|
|
||||||
_cleanupCallbacks();
|
|
||||||
if (_callbacks.ContainsKey(message.MessageId))
|
|
||||||
_callbacks[message.MessageId].Item2.Invoke(message);
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void _cleanupCallbacks()
|
|
||||||
{
|
|
||||||
var cbr = (from item in _callbacks where item.Value.Item1 < DateTime.Now select item.Key).ToArray();
|
|
||||||
foreach (var cb in cbr)
|
|
||||||
_callbacks.Remove(cb);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using NPSharp.RPC.Messages;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a low-level stream which communicates with an NP client using RPC messages.
|
||||||
|
/// </summary>
|
||||||
|
public class RPCServerStream : RPCStream<RPCServerMessage, RPCClientMessage>
|
||||||
|
{
|
||||||
|
public RPCServerStream(Socket sock) : base(sock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using log4net;
|
||||||
|
using NPSharp.RPC.Messages;
|
||||||
|
|
||||||
|
namespace NPSharp.RPC
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a low-level client stream which can communicate using RPC packets.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RPCStream<TSend, TRecv>
|
||||||
|
where TSend : RPCMessage
|
||||||
|
where TRecv : RPCMessage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Registered callbacks for all received messages.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly List<Action<TRecv>> GeneralCallbacks =
|
||||||
|
new List<Action<TRecv>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registered callbacks for specific received message IDs.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly List<KeyValuePair<uint, Action<TRecv>>> IDCallbacks =
|
||||||
|
new List<KeyValuePair<uint, Action<TRecv>>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registered callbacks for specific received message type IDs.
|
||||||
|
/// </summary>
|
||||||
|
protected readonly List<KeyValuePair<uint, Action<TRecv>>> TypeCallbacks =
|
||||||
|
new List<KeyValuePair<uint, Action<TRecv>>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logger instance.
|
||||||
|
/// </summary>
|
||||||
|
private readonly ILog _log;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ID of the next message.
|
||||||
|
/// </summary>
|
||||||
|
protected uint MessageID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base stream.
|
||||||
|
/// </summary>
|
||||||
|
private Socket _sock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an RPC connection stream from an already established network connection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sock">Client's network stream</param>
|
||||||
|
protected RPCStream(Socket sock)
|
||||||
|
{
|
||||||
|
_log = LogManager.GetLogger("RPC");
|
||||||
|
_sock = sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the next message's ID.
|
||||||
|
/// </summary>
|
||||||
|
protected void IterateMessageID()
|
||||||
|
{
|
||||||
|
MessageID++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes the connection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeout"></param>
|
||||||
|
public void Close(int timeout = 2000)
|
||||||
|
{
|
||||||
|
// Connection already closed?
|
||||||
|
if (_sock == null)
|
||||||
|
throw new InvalidOperationException("Connection already closed");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GeneralCallbacks.Clear();
|
||||||
|
IDCallbacks.Clear();
|
||||||
|
TypeCallbacks.Clear();
|
||||||
|
|
||||||
|
_sock.Close(timeout);
|
||||||
|
_sock.Dispose();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sock = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches a callback to the connection which handles a specific incoming RPC message type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The message type to handle, must be a subtype of RPCMessage</typeparam>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void AttachHandlerForMessageType<T>(Action<T> callback) where T : TRecv
|
||||||
|
{
|
||||||
|
TypeCallbacks.Add(
|
||||||
|
new KeyValuePair<uint, Action<TRecv>>(
|
||||||
|
((PacketAttribute) typeof (T).GetCustomAttributes(typeof (PacketAttribute), false).Single()).Type,
|
||||||
|
(Action<TRecv>)callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches a callback to the connection which handles a response to the next message we send.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void AttachHandlerForNextMessage(Action<TRecv> callback)
|
||||||
|
{
|
||||||
|
IDCallbacks.Add(new KeyValuePair<uint, Action<TRecv>>(MessageID, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches a callback to the connection for all incoming RPC messages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void AttachHandler(Action<TRecv> callback)
|
||||||
|
{
|
||||||
|
GeneralCallbacks.Add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends out an RPC message to the remote endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The RPC message to send out.</param>
|
||||||
|
/// <returns>The new ID of the message.</returns>
|
||||||
|
public void Send(TSend message)
|
||||||
|
{
|
||||||
|
if (_sock == null)
|
||||||
|
throw new InvalidOperationException("You need to open the stream first.");
|
||||||
|
|
||||||
|
message.MessageId = MessageID;
|
||||||
|
|
||||||
|
var buffer = message.Serialize();
|
||||||
|
|
||||||
|
_sock.Send(buffer);
|
||||||
|
|
||||||
|
IterateMessageID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the next RPC message from the remote end and reads it.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The received server message.</returns>
|
||||||
|
public TRecv Read()
|
||||||
|
{
|
||||||
|
if (_sock == null)
|
||||||
|
throw new InvalidOperationException("You need to open the stream first.");
|
||||||
|
|
||||||
|
var message = RPCMessage.Deserialize<TRecv>(_sock);
|
||||||
|
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callbacks
|
||||||
|
foreach (var cbi in IDCallbacks.Where(p => p.Key == message.MessageId))
|
||||||
|
cbi.Value.Invoke(message);
|
||||||
|
foreach (var cbi in TypeCallbacks.Where(p => p.Key == message.GetTypeId()))
|
||||||
|
cbi.Value.Invoke(message);
|
||||||
|
foreach (var callback in GeneralCallbacks)
|
||||||
|
callback.Invoke(message);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,229 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace NPSharp.Steam
|
||||||
|
{
|
||||||
|
public class CSteamID
|
||||||
|
{
|
||||||
|
private readonly InteropHelp.BitVector64 steamid;
|
||||||
|
|
||||||
|
public CSteamID()
|
||||||
|
: this(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CSteamID(UInt32 unAccountID, EUniverse eUniverse, EAccountType eAccountType)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
Set(unAccountID, eUniverse, eAccountType);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CSteamID(UInt32 unAccountID, UInt32 unInstance, EUniverse eUniverse, EAccountType eAccountType)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
InstancedSet(unAccountID, unInstance, eUniverse, eAccountType);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CSteamID(UInt64 id)
|
||||||
|
{
|
||||||
|
steamid = new InteropHelp.BitVector64(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal CSteamID(SteamID_t sid)
|
||||||
|
: this(
|
||||||
|
sid.low32Bits, sid.high32Bits & 0xFFFFF, (EUniverse) (sid.high32Bits >> 24),
|
||||||
|
(EAccountType) ((sid.high32Bits >> 20) & 0xF))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt32 AccountID
|
||||||
|
{
|
||||||
|
get { return (UInt32) steamid[0, 0xFFFFFFFF]; }
|
||||||
|
set { steamid[0, 0xFFFFFFFF] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt32 AccountInstance
|
||||||
|
{
|
||||||
|
get { return (UInt32) steamid[32, 0xFFFFF]; }
|
||||||
|
set { steamid[32, 0xFFFFF] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public EAccountType AccountType
|
||||||
|
{
|
||||||
|
get { return (EAccountType) steamid[52, 0xF]; }
|
||||||
|
set { steamid[52, 0xF] = (UInt64) value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public EUniverse AccountUniverse
|
||||||
|
{
|
||||||
|
get { return (EUniverse) steamid[56, 0xFF]; }
|
||||||
|
set { steamid[56, 0xFF] = (UInt64) value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator UInt64(CSteamID sid)
|
||||||
|
{
|
||||||
|
return sid.steamid.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator CSteamID(UInt64 id)
|
||||||
|
{
|
||||||
|
return new CSteamID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(UInt32 unAccountID, EUniverse eUniverse, EAccountType eAccountType)
|
||||||
|
{
|
||||||
|
AccountID = unAccountID;
|
||||||
|
AccountUniverse = eUniverse;
|
||||||
|
AccountType = eAccountType;
|
||||||
|
|
||||||
|
if (eAccountType == EAccountType.Clan)
|
||||||
|
{
|
||||||
|
AccountInstance = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AccountInstance = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InstancedSet(UInt32 unAccountID, UInt32 unInstance, EUniverse eUniverse, EAccountType eAccountType)
|
||||||
|
{
|
||||||
|
AccountID = unAccountID;
|
||||||
|
AccountUniverse = eUniverse;
|
||||||
|
AccountType = eAccountType;
|
||||||
|
AccountInstance = unInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFromUint64(UInt64 ulSteamID)
|
||||||
|
{
|
||||||
|
steamid.Data = ulSteamID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt64 ConvertToUint64()
|
||||||
|
{
|
||||||
|
return steamid.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BBlankAnonAccount()
|
||||||
|
{
|
||||||
|
return AccountID == 0 && BAnonAccount() && AccountInstance == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BGameServerAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.GameServer ||
|
||||||
|
AccountType == EAccountType.AnonGameServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BContentServerAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.ContentServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BClanAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.Clan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BChatAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.Chat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLobby()
|
||||||
|
{
|
||||||
|
return (AccountType == EAccountType.Chat) && ((AccountInstance & (0x000FFFFF + 1) >> 2) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BAnonAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.AnonUser ||
|
||||||
|
AccountType == EAccountType.AnonGameServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BAnonUserAccount()
|
||||||
|
{
|
||||||
|
return AccountType == EAccountType.AnonUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
if (AccountType <= EAccountType.Invalid || AccountType >= EAccountType.Max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (AccountUniverse <= EUniverse.Invalid || AccountUniverse >= EUniverse.Max)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (AccountType == EAccountType.Individual)
|
||||||
|
{
|
||||||
|
if (AccountID == 0 || AccountInstance != 1)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AccountType == EAccountType.Clan)
|
||||||
|
{
|
||||||
|
if (AccountID == 0 || AccountInstance != 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Render()
|
||||||
|
{
|
||||||
|
switch (AccountType)
|
||||||
|
{
|
||||||
|
case EAccountType.Invalid:
|
||||||
|
case EAccountType.Individual:
|
||||||
|
return AccountUniverse <= EUniverse.Public ? String.Format("STEAM_0:{0}:{1}", AccountID & 1, AccountID >> 1) : String.Format("STEAM_{2}:{0}:{1}", AccountID & 1, AccountID >> 1, (int) AccountUniverse);
|
||||||
|
default:
|
||||||
|
return Convert.ToString(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(Object obj)
|
||||||
|
{
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var sid = obj as CSteamID;
|
||||||
|
if (sid == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return steamid.Data == sid.steamid.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(CSteamID sid)
|
||||||
|
{
|
||||||
|
if (sid == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return steamid.Data == sid.steamid.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator ==(CSteamID a, CSteamID b)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(a, b))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ((a == null) || (b == null))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return a.steamid.Data == b.steamid.Data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(CSteamID a, CSteamID b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return steamid.Data.GetHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
namespace NPSharp.Steam
|
||||||
|
{
|
||||||
|
public enum EAccountType
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
Individual = 1,
|
||||||
|
Multiseat = 2,
|
||||||
|
GameServer = 3,
|
||||||
|
AnonGameServer = 4,
|
||||||
|
Pending = 5,
|
||||||
|
ContentServer = 6,
|
||||||
|
Clan = 7,
|
||||||
|
Chat = 8,
|
||||||
|
ConsoleUser = 9,
|
||||||
|
AnonUser = 10,
|
||||||
|
Max = 11,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace NPSharp.Steam
|
||||||
|
{
|
||||||
|
public enum EUniverse
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
Public = 1,
|
||||||
|
Beta = 2,
|
||||||
|
Internal = 3,
|
||||||
|
Dev = 4,
|
||||||
|
Max = 5,
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NPSharp.Steam
|
||||||
|
{
|
||||||
|
internal class InteropHelp
|
||||||
|
{
|
||||||
|
private static readonly GCHandle NullHandle = GCHandle.Alloc(new byte[0], GCHandleType.Pinned);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decodes IntPtr as if it were a UTF-8 string
|
||||||
|
/// </summary>
|
||||||
|
public static string DecodeUTF8String(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int len = 0;
|
||||||
|
while (Marshal.ReadByte(ptr, len) != 0) len++;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var buffer = new byte[len];
|
||||||
|
Marshal.Copy(ptr, buffer, 0, buffer.Length);
|
||||||
|
return Encoding.UTF8.GetString(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes string as an IntPtr
|
||||||
|
/// </summary>
|
||||||
|
public static IntPtr EncodeUTF8String(string str, out GCHandle handle)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
handle = NullHandle;
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = Encoding.UTF8.GetByteCount(str);
|
||||||
|
var buffer = new byte[length + 1];
|
||||||
|
|
||||||
|
Encoding.UTF8.GetBytes(str, 0, str.Length, buffer, 0);
|
||||||
|
|
||||||
|
handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||||
|
return handle.AddrOfPinnedObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void FreeString(ref GCHandle handle)
|
||||||
|
{
|
||||||
|
if (handle == NullHandle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
handle.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BitVector64
|
||||||
|
{
|
||||||
|
private UInt64 data;
|
||||||
|
|
||||||
|
public BitVector64()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitVector64(UInt64 value)
|
||||||
|
{
|
||||||
|
data = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt64 Data
|
||||||
|
{
|
||||||
|
get { return data; }
|
||||||
|
set { data = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public UInt64 this[uint bitoffset, UInt64 valuemask]
|
||||||
|
{
|
||||||
|
get { return (data >> (ushort) bitoffset) & valuemask; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
data = (data & ~(valuemask << (ushort) bitoffset)) | ((value & valuemask) << (ushort) bitoffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
The code in this folder is copied over and reformatted (slightly edited) from Steam4NET.
|
||||||
|
|
||||||
|
The source code for Steam4NET is available at https://github.com/SteamRE/Steam4NET.
|
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace NPSharp.Steam
|
||||||
|
{
|
||||||
|
// Summary:
|
||||||
|
// Used to store a SteamID in callbacks (With proper alignment / padding).
|
||||||
|
// You probably don't want to use this type directly, convert it to CSteamID.
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 8)]
|
||||||
|
internal struct SteamID_t
|
||||||
|
{
|
||||||
|
public UInt32 low32Bits; // m_unAccountID (32)
|
||||||
|
public UInt32 high32Bits; // m_unAccountInstance (20), m_EAccountType (4), m_EUniverse (8)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using log4net.Appender;
|
||||||
|
|
||||||
|
namespace NPSharp
|
||||||
|
{
|
||||||
|
internal class Ticket
|
||||||
|
{
|
||||||
|
public Ticket(byte[] data)
|
||||||
|
{
|
||||||
|
if (data.Length < sizeof(uint) + (sizeof(ulong) * 2) + sizeof(uint))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Data buffer too short");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var ms = new MemoryStream(data))
|
||||||
|
using (var br = new BinaryReader(ms))
|
||||||
|
{
|
||||||
|
Version = br.ReadUInt32();
|
||||||
|
ClientID = br.ReadUInt64();
|
||||||
|
ServerID = br.ReadUInt64();
|
||||||
|
Time = br.ReadUInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Version { get; private set; }
|
||||||
|
|
||||||
|
public ulong ClientID { get; private set; }
|
||||||
|
|
||||||
|
public ulong ServerID { get; private set; }
|
||||||
|
|
||||||
|
public uint Time { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,8 +54,17 @@
|
||||||
<Reference Include="System.Xml.Serialization" />
|
<Reference Include="System.Xml.Serialization" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="ClientEventArgs.cs" />
|
||||||
<Compile Include="NPClient.cs" />
|
<Compile Include="NPClient.cs" />
|
||||||
<Compile Include="NPFileException.cs" />
|
<Compile Include="NPFileException.cs" />
|
||||||
|
<Compile Include="NPServer.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateRegisterServerMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateRegisterServerResultMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateUserGroupMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateValidateTicketMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateValidateTicketResultMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateWithDetailsMessage.cs" />
|
||||||
|
<Compile Include="RPC\Messages\AuthenticateWithKeyMessage.cs" />
|
||||||
<Compile Include="RPC\Messages\FriendDetails.cs" />
|
<Compile Include="RPC\Messages\FriendDetails.cs" />
|
||||||
<Compile Include="RPC\Messages\FriendsSetSteamIDMessage.cs" />
|
<Compile Include="RPC\Messages\FriendsSetSteamIDMessage.cs" />
|
||||||
<Compile Include="RPC\Messages\FriendsGetProfileDataMessage.cs" />
|
<Compile Include="RPC\Messages\FriendsGetProfileDataMessage.cs" />
|
||||||
|
@ -87,8 +96,18 @@
|
||||||
<Compile Include="RPC\Messages\StorageWriteUserFileResultMessage.cs" />
|
<Compile Include="RPC\Messages\StorageWriteUserFileResultMessage.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Authentication\AuthenticationHelper.cs" />
|
<Compile Include="Authentication\AuthenticationHelper.cs" />
|
||||||
|
<Compile Include="RPC\RPCServerStream.cs" />
|
||||||
|
<Compile Include="RPC\RPCStream.cs" />
|
||||||
|
<Compile Include="Steam\CSteamID.cs" />
|
||||||
|
<Compile Include="Steam\EAccountType.cs" />
|
||||||
|
<Compile Include="Steam\EUniverse.cs" />
|
||||||
|
<Compile Include="Steam\InteropHelp.cs" />
|
||||||
|
<Compile Include="Steam\SteamID_t.cs" />
|
||||||
|
<Compile Include="Ticket.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Steam\README.txt" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- 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.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<packages>
|
<packages>
|
||||||
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
||||||
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
|
<package id="protobuf-net" version="2.0.0.668" targetFramework="net45" />
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
using log4net;
|
using log4net;
|
||||||
using log4net.Appender;
|
using log4net.Appender;
|
||||||
using log4net.Config;
|
using log4net.Config;
|
||||||
|
@ -16,12 +17,13 @@ using uhttpsharp.Handlers;
|
||||||
using uhttpsharp.Headers;
|
using uhttpsharp.Headers;
|
||||||
using uhttpsharp.Listeners;
|
using uhttpsharp.Listeners;
|
||||||
using uhttpsharp.RequestProviders;
|
using uhttpsharp.RequestProviders;
|
||||||
|
using HttpResponse = uhttpsharp.HttpResponse;
|
||||||
|
|
||||||
namespace NPSharp.CommandLine.File
|
namespace NPSharp.CommandLine.File
|
||||||
{
|
{
|
||||||
class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// log4net setup
|
// log4net setup
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
|
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
|
||||||
|
@ -35,7 +37,8 @@ namespace NPSharp.CommandLine.File
|
||||||
#endif
|
#endif
|
||||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||||
};
|
};
|
||||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
BasicConfigurator.Configure(new IAppender[]
|
||||||
|
{appender, new DebugAppender {Layout = appender.Layout, Threshold = Level.All}});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -48,16 +51,38 @@ namespace NPSharp.CommandLine.File
|
||||||
#endif
|
#endif
|
||||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||||
};
|
};
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Debug, ForeColor = ColoredConsoleAppender.Colors.Cyan | ColoredConsoleAppender.Colors.HighIntensity });
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Info, ForeColor = ColoredConsoleAppender.Colors.Green | ColoredConsoleAppender.Colors.HighIntensity });
|
{
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Warn, ForeColor = ColoredConsoleAppender.Colors.Purple | ColoredConsoleAppender.Colors.HighIntensity });
|
Level = Level.Debug,
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Error, ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity });
|
ForeColor = ColoredConsoleAppender.Colors.Cyan | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Fatal, ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity, BackColor = ColoredConsoleAppender.Colors.Red });
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Info,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Green | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Warn,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Purple | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Error,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Fatal,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity,
|
||||||
|
BackColor = ColoredConsoleAppender.Colors.Red
|
||||||
|
});
|
||||||
appender.ActivateOptions();
|
appender.ActivateOptions();
|
||||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
BasicConfigurator.Configure(new IAppender[]
|
||||||
|
{appender, new DebugAppender {Layout = appender.Layout, Threshold = Level.All}});
|
||||||
}
|
}
|
||||||
|
|
||||||
var log = LogManager.GetLogger("Main");
|
ILog log = LogManager.GetLogger("Main");
|
||||||
|
|
||||||
// Arguments
|
// Arguments
|
||||||
if (args.Length < 4)
|
if (args.Length < 4)
|
||||||
|
@ -66,11 +91,11 @@ namespace NPSharp.CommandLine.File
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostname = args[0];
|
string hostname = args[0];
|
||||||
var port = ushort.Parse(args[1]);
|
ushort port = ushort.Parse(args[1]);
|
||||||
var username = args[2];
|
string username = args[2];
|
||||||
var password = args[3];
|
string password = args[3];
|
||||||
var hport = args.Length > 4 ? ushort.Parse(args[4]) : 5680;
|
int hport = args.Length > 4 ? ushort.Parse(args[4]) : 5680;
|
||||||
|
|
||||||
// NP connection setup
|
// NP connection setup
|
||||||
log.DebugFormat("Connecting to {0}:{1}...", hostname, port);
|
log.DebugFormat("Connecting to {0}:{1}...", hostname, port);
|
||||||
|
@ -112,7 +137,8 @@ namespace NPSharp.CommandLine.File
|
||||||
);
|
);
|
||||||
httpServer.Use(new AnonymousHttpRequestHandler((context, next) =>
|
httpServer.Use(new AnonymousHttpRequestHandler((context, next) =>
|
||||||
{
|
{
|
||||||
context.Response = new HttpResponse(HttpResponseCode.NotFound, "File not found", context.Request.Headers.KeepAliveConnection());
|
context.Response = new HttpResponse(HttpResponseCode.NotFound, "File not found",
|
||||||
|
context.Request.Headers.KeepAliveConnection());
|
||||||
return Task.Factory.GetCompleted();
|
return Task.Factory.GetCompleted();
|
||||||
}));
|
}));
|
||||||
httpServer.Start();
|
httpServer.Start();
|
||||||
|
@ -126,8 +152,8 @@ namespace NPSharp.CommandLine.File
|
||||||
|
|
||||||
internal class NP2HTTPUserFileHandler : IHttpRequestHandler
|
internal class NP2HTTPUserFileHandler : IHttpRequestHandler
|
||||||
{
|
{
|
||||||
private readonly NPClient _np;
|
|
||||||
private readonly ILog _log;
|
private readonly ILog _log;
|
||||||
|
private readonly NPClient _np;
|
||||||
|
|
||||||
public NP2HTTPUserFileHandler(NPClient np)
|
public NP2HTTPUserFileHandler(NPClient np)
|
||||||
{
|
{
|
||||||
|
@ -137,7 +163,9 @@ namespace NPSharp.CommandLine.File
|
||||||
|
|
||||||
public Task Handle(IHttpContext context, Func<Task> next)
|
public Task Handle(IHttpContext context, Func<Task> next)
|
||||||
{
|
{
|
||||||
var uri = context.Request.QueryString.Any() ? null : string.Join("/", context.Request.Uri.OriginalString.Split('/').Skip(2));
|
string uri = context.Request.QueryString.Any()
|
||||||
|
? null
|
||||||
|
: string.Join("/", context.Request.Uri.OriginalString.Split('/').Skip(2));
|
||||||
if (uri == null)
|
if (uri == null)
|
||||||
if (!context.Request.QueryString.TryGetByName("uri", out uri) || uri == null)
|
if (!context.Request.QueryString.TryGetByName("uri", out uri) || uri == null)
|
||||||
{
|
{
|
||||||
|
@ -149,7 +177,7 @@ namespace NPSharp.CommandLine.File
|
||||||
}
|
}
|
||||||
|
|
||||||
_log.InfoFormat("Requesting user file {0}", uri);
|
_log.InfoFormat("Requesting user file {0}", uri);
|
||||||
var task = _np.GetUserFile(uri);
|
Task<byte[]> task = _np.GetUserFile(uri);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
task.Wait();
|
task.Wait();
|
||||||
|
@ -158,13 +186,15 @@ namespace NPSharp.CommandLine.File
|
||||||
{
|
{
|
||||||
context.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "File not accessible",
|
context.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "File not accessible",
|
||||||
context.Request.Headers.KeepAliveConnection(),
|
context.Request.Headers.KeepAliveConnection(),
|
||||||
string.Format("<pre><tt><code>{0}</code></tt></pre>", task.Exception == null ? "Unknown error" : task.Exception.ToString())
|
string.Format("<pre><tt><code>{0}</code></tt></pre>",
|
||||||
|
task.Exception == null ? "Unknown error" : task.Exception.ToString())
|
||||||
);
|
);
|
||||||
return Task.Factory.GetCompleted();
|
return Task.Factory.GetCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return file contents
|
// Return file contents
|
||||||
context.Response = new HttpResponse(HttpResponseCode.Ok, System.Web.MimeMapping.GetMimeMapping(uri), new MemoryStream(task.Result), context.Request.Headers.KeepAliveConnection());
|
context.Response = new HttpResponse(HttpResponseCode.Ok, MimeMapping.GetMimeMapping(uri),
|
||||||
|
new MemoryStream(task.Result), context.Request.Headers.KeepAliveConnection());
|
||||||
|
|
||||||
return Task.Factory.GetCompleted();
|
return Task.Factory.GetCompleted();
|
||||||
}
|
}
|
||||||
|
@ -172,8 +202,8 @@ namespace NPSharp.CommandLine.File
|
||||||
|
|
||||||
internal class NP2HTTPPublisherFileHandler : IHttpRequestHandler
|
internal class NP2HTTPPublisherFileHandler : IHttpRequestHandler
|
||||||
{
|
{
|
||||||
private readonly NPClient _np;
|
|
||||||
private readonly ILog _log;
|
private readonly ILog _log;
|
||||||
|
private readonly NPClient _np;
|
||||||
|
|
||||||
public NP2HTTPPublisherFileHandler(NPClient np)
|
public NP2HTTPPublisherFileHandler(NPClient np)
|
||||||
{
|
{
|
||||||
|
@ -183,7 +213,9 @@ namespace NPSharp.CommandLine.File
|
||||||
|
|
||||||
public Task Handle(IHttpContext context, Func<Task> next)
|
public Task Handle(IHttpContext context, Func<Task> next)
|
||||||
{
|
{
|
||||||
var uri = context.Request.QueryString.Any() ? null : string.Join("/", context.Request.Uri.OriginalString.Split('/').Skip(2));
|
string uri = context.Request.QueryString.Any()
|
||||||
|
? null
|
||||||
|
: string.Join("/", context.Request.Uri.OriginalString.Split('/').Skip(2));
|
||||||
if (uri == null)
|
if (uri == null)
|
||||||
if (!context.Request.QueryString.TryGetByName("uri", out uri) || uri == null)
|
if (!context.Request.QueryString.TryGetByName("uri", out uri) || uri == null)
|
||||||
{
|
{
|
||||||
|
@ -195,7 +227,7 @@ namespace NPSharp.CommandLine.File
|
||||||
}
|
}
|
||||||
|
|
||||||
_log.InfoFormat("Requesting publisher file {0}", uri);
|
_log.InfoFormat("Requesting publisher file {0}", uri);
|
||||||
var task = _np.GetPublisherFile(uri);
|
Task<byte[]> task = _np.GetPublisherFile(uri);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
task.Wait();
|
task.Wait();
|
||||||
|
@ -204,13 +236,15 @@ namespace NPSharp.CommandLine.File
|
||||||
{
|
{
|
||||||
context.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "File not accessible",
|
context.Response = HttpResponse.CreateWithMessage(HttpResponseCode.NotFound, "File not accessible",
|
||||||
context.Request.Headers.KeepAliveConnection(),
|
context.Request.Headers.KeepAliveConnection(),
|
||||||
string.Format("<pre><tt><code>{0}</code></tt></pre>", task.Exception == null ? "Unknown error" : task.Exception.ToString())
|
string.Format("<pre><tt><code>{0}</code></tt></pre>",
|
||||||
|
task.Exception == null ? "Unknown error" : task.Exception.ToString())
|
||||||
);
|
);
|
||||||
return Task.Factory.GetCompleted();
|
return Task.Factory.GetCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return file contents
|
// Return file contents
|
||||||
context.Response = new HttpResponse(HttpResponseCode.Ok, System.Web.MimeMapping.GetMimeMapping(uri), new MemoryStream(task.Result), context.Request.Headers.KeepAliveConnection());
|
context.Response = new HttpResponse(HttpResponseCode.Ok, MimeMapping.GetMimeMapping(uri),
|
||||||
|
new MemoryStream(task.Result), context.Request.Headers.KeepAliveConnection());
|
||||||
|
|
||||||
return Task.Factory.GetCompleted();
|
return Task.Factory.GetCompleted();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
// die mit einer Assembly verknüpft sind.
|
// die mit einer Assembly verknüpft sind.
|
||||||
|
|
||||||
[assembly: AssemblyTitle("npfile")]
|
[assembly: AssemblyTitle("npfile")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
@ -17,9 +17,11 @@ using System.Runtime.InteropServices;
|
||||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
// 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
|
// 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.
|
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||||
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||||
|
|
||||||
[assembly: Guid("549e5fde-a94d-4154-9577-5743f8be3ed3")]
|
[assembly: Guid("549e5fde-a94d-4154-9577-5743f8be3ed3")]
|
||||||
|
|
||||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
@ -32,5 +34,6 @@ using System.Runtime.InteropServices;
|
||||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||||
// übernehmen, indem Sie "*" eingeben:
|
// übernehmen, indem Sie "*" eingeben:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<packages>
|
<packages>
|
||||||
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
|
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||||
|
|
|
@ -9,9 +9,9 @@ using NPSharp.Authentication;
|
||||||
|
|
||||||
namespace NPSharp.CommandLine.MOTD
|
namespace NPSharp.CommandLine.MOTD
|
||||||
{
|
{
|
||||||
class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
{
|
{
|
||||||
// log4net setup
|
// log4net setup
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
|
if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
|
||||||
|
@ -25,7 +25,8 @@ namespace NPSharp.CommandLine.MOTD
|
||||||
#endif
|
#endif
|
||||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||||
};
|
};
|
||||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
BasicConfigurator.Configure(new IAppender[]
|
||||||
|
{appender, new DebugAppender {Layout = appender.Layout, Threshold = Level.All}});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -38,16 +39,38 @@ namespace NPSharp.CommandLine.MOTD
|
||||||
#endif
|
#endif
|
||||||
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
Layout = new PatternLayout("<%d{HH:mm:ss}> [%logger:%thread] %level: %message%newline"),
|
||||||
};
|
};
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Debug, ForeColor = ColoredConsoleAppender.Colors.Cyan | ColoredConsoleAppender.Colors.HighIntensity });
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Info, ForeColor = ColoredConsoleAppender.Colors.Green | ColoredConsoleAppender.Colors.HighIntensity });
|
{
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Warn, ForeColor = ColoredConsoleAppender.Colors.Purple | ColoredConsoleAppender.Colors.HighIntensity });
|
Level = Level.Debug,
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Error, ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity });
|
ForeColor = ColoredConsoleAppender.Colors.Cyan | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
appender.AddMapping(new ColoredConsoleAppender.LevelColors { Level = Level.Fatal, ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity, BackColor = ColoredConsoleAppender.Colors.Red });
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Info,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Green | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Warn,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Purple | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Error,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.Red | ColoredConsoleAppender.Colors.HighIntensity
|
||||||
|
});
|
||||||
|
appender.AddMapping(new ColoredConsoleAppender.LevelColors
|
||||||
|
{
|
||||||
|
Level = Level.Fatal,
|
||||||
|
ForeColor = ColoredConsoleAppender.Colors.White | ColoredConsoleAppender.Colors.HighIntensity,
|
||||||
|
BackColor = ColoredConsoleAppender.Colors.Red
|
||||||
|
});
|
||||||
appender.ActivateOptions();
|
appender.ActivateOptions();
|
||||||
BasicConfigurator.Configure(new IAppender[] { appender, new DebugAppender { Layout = appender.Layout, Threshold = Level.All } });
|
BasicConfigurator.Configure(new IAppender[]
|
||||||
|
{appender, new DebugAppender {Layout = appender.Layout, Threshold = Level.All}});
|
||||||
}
|
}
|
||||||
|
|
||||||
var log = LogManager.GetLogger("Main");
|
ILog log = LogManager.GetLogger("Main");
|
||||||
|
|
||||||
// Arguments
|
// Arguments
|
||||||
if (args.Length < 4)
|
if (args.Length < 4)
|
||||||
|
@ -56,10 +79,10 @@ namespace NPSharp.CommandLine.MOTD
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostname = args[0];
|
string hostname = args[0];
|
||||||
var port = ushort.Parse(args[1]);
|
ushort port = ushort.Parse(args[1]);
|
||||||
var username = args[2];
|
string username = args[2];
|
||||||
var password = args[3];
|
string password = args[3];
|
||||||
|
|
||||||
// NP connection setup
|
// NP connection setup
|
||||||
log.DebugFormat("Connecting to {0}:{1}...", hostname, port);
|
log.DebugFormat("Connecting to {0}:{1}...", hostname, port);
|
||||||
|
@ -104,15 +127,13 @@ namespace NPSharp.CommandLine.MOTD
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
log.InfoFormat("Server says: {0}", Encoding.UTF8.GetString(np.GetPublisherFile("motd-english.txt").Result));
|
log.InfoFormat("Server says: {0}",
|
||||||
|
Encoding.UTF8.GetString(np.GetPublisherFile("motd-english.txt").Result));
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
log.ErrorFormat("Could not read MOTD from NP server.");
|
log.ErrorFormat("Could not read MOTD from NP server.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
||||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
||||||
// die mit einer Assembly verknüpft sind.
|
// die mit einer Assembly verknüpft sind.
|
||||||
|
|
||||||
[assembly: AssemblyTitle("NPSharp MOTD test client")]
|
[assembly: AssemblyTitle("NPSharp MOTD test client")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
@ -17,9 +17,11 @@ using System.Runtime.InteropServices;
|
||||||
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
|
// 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
|
// 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.
|
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
|
||||||
|
|
||||||
[assembly: ComVisible(false)]
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
||||||
|
|
||||||
[assembly: Guid("b91f9ba4-757a-4c72-b12a-c3e1f1b05715")]
|
[assembly: Guid("b91f9ba4-757a-4c72-b12a-c3e1f1b05715")]
|
||||||
|
|
||||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
||||||
|
@ -32,5 +34,6 @@ using System.Runtime.InteropServices;
|
||||||
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
|
||||||
// übernehmen, indem Sie "*" eingeben:
|
// übernehmen, indem Sie "*" eingeben:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("0.1.*")]
|
[assembly: AssemblyVersion("0.1.*")]
|
||||||
[assembly: AssemblyFileVersion("0.1")]
|
[assembly: AssemblyFileVersion("0.1")]
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<packages>
|
<packages>
|
||||||
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
<package id="log4net" version="2.0.3" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
Loading…
Reference in New Issue