Pubnub SDK translation from Unity to Javascript Function? - html

In Javascript its basically:
pubnub.publish({
channel: 'Chatbox',
message: {
username: 'username',
text: message
}
});
pubnub.subscribe({
channel: 'Chatbox',
message: handleMessage
});
function handleMessage(message) {
var messageEl = $("<li class='message'>"
+ "<span class='username'>" + message.username + ": </span>"
+ message.text
+ "</li>");
messageList.append(messageEl);
messageList.listview('refresh');
In Unity instead of using message.username. Unity gives a dictionary. Unity seems to be the more complicated one so I would like to translate the unity from string url to publish sdk, if possible.
Here is the UNity Wrapper
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PubNubWrapper : MonoBehaviour
{
public static PubNubWrapper instance;
public string PublishKey = "";
public string SubscribeKey = "";
private Dictionary<string, System.Action<string>> channelMessageHandlers = new Dictionary<string, System.Action<string>>();
private string timeToken = "o";
void Awake()
{
instance = this;
// Debug.Log("Received message: ");
}
//send the ported message
/// <summary>
/// The Publish function creates a new JSON string by creating a WWW object. The WWW object is the object
/// making the requests to the PubNub Server. Part of the Publish, Subscribe and Unsubscribe requests for web services
/// </summary>
/// <param name="message"></param>
/// <param name="channel"></param>
public void Publish(string message, string channel)
{
//esc the message so we can put it into webservices
string escapedMessage = WWW.EscapeURL(message).Replace("+", "%20"); // UNity's URL escaping function replaces spaces '+'
//form the URL
//http://pubsub/pubnub.com
// /publish
// /[publishKey]
// /[subscribe key]
// /[o
// /[channel name]
// /o
// /[JSON message data]
string url =
"http://pubsub.pubnub.com" +"/publish" +"/" + PublishKey +"/" + SubscribeKey +"/o" +"/" + channel +"/o" +"/\"" + escapedMessage + "\"";
//make the request with a newly created WWW object
WWW www = new WWW(url);
}
/// <summary>
/// Subscribe creates an open port to listen to published messages sent with a JSON string
/// </summary>
/// <param name="channel"> N/a </param>
/// <param name="messageHandler"> N/a </param>
public void Subscribe(string channel, System.Action<string> messageHandler)
{
channelMessageHandlers.Add(channel, messageHandler);
StartCoroutine(doSubscribe(channel));
}
/// <summary>
/// The doSubscribe is the action for the channel to listen to for published messages
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
IEnumerator doSubscribe(string channel)
{
//The mssage handler here will again be JSON for parsing
while (channelMessageHandlers.ContainsKey(channel))
{
// form the URL
// /http://pubsub.pubnub.com
// /subscribe
// /[subscribe key here]
// / [channel key here]
// /o
// /[time token here]
string url =
"http://pubsub.pubnub.com" +
"/subscribe" +
"/" + SubscribeKey +
"/" + channel +
"/o" +
"/" + timeToken;
// make the request
WWW www = new WWW(url);
//in Unity we can yield
// a WWW object whic makes Unity "pause"
// a subroutine until the request has either encountered and error or done
yield return www;
//www.error is a string
// it will either be null/empty if there is no error, or it
// will contain the error message if there was one
if (!string.IsNullOrEmpty(www.error))
{
//log the error to the console
Debug.LogWarning("Subscribe failed: " + www.error);
//unsibscribe from the channel automatically
// yield break causes Unity to stop exiting this
// coroutine. It is equivalent to a return function
yield break;
}
//parse the JSON response from the client from subscription
string newToken;
string[] newMessages = PubNubUtils.ParseSubscribeResponse(
www.text, out newToken);
timeToken = newToken;
//make sure there is a still a subscription
if (channelMessageHandlers.ContainsKey(channel))
{
// handle each message separately
for (int i = 0; i < newMessages.Length; i++)
{
channelMessageHandlers[channel](newMessages[i]);
}
}
}
}
/// <summary>
/// Listen to the channel for subscriptions
/// </summary>
/// <param name="channel"></param>
public void Unsubscribe(string channel)
{
channelMessageHandlers.Remove(channel);
}
}
Unity GUI components
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Chatbox : MonoBehaviour {
private string PlayerName;
private string _playerName;
private string chatText = "";
private List<string> messages = new List<string>();
private Vector2 scrollPosition = Vector2.zero;
/// <summary>
/// Initialize all the player preferences with Guestname<0-999> and the name can be changed by /OnGui
/// </summary>
void Start()
{
PlayerName = PlayerPrefs.GetString("PlayerName", "Guest" + Random.Range(0, 9999));
_playerName = PlayerName;
//subscribe to the specific room
PubNubWrapper.instance.Subscribe("Chatbox", HandleMessage);
}
/// <summary>
/// Handle message sends a string to the list of messages and deletes if over 100
/// </summary>
/// <param name="message"></param>
void HandleMessage(string message)
{
Debug.Log(message);
messages.Add(message);
if (messages.Count > 100)
messages.RemoveAt(0);
//Unity cmlamps the scroll value. Setting it sufficiently high will cause it to scroll to the bottom
scrollPosition.y = messages.Count * 100f;
}
/// <summary>
/// GUI references for changing the player's name
/// </summary>
void OnGUI()
{
_playerName = GUILayout.TextField(_playerName, GUILayout.Width(200f));
if (GUILayout.Button( "Change Name", GUILayout.Width( 200f ) ) )
{
//inform other players that the player has changed name
PubNubWrapper.instance.Publish(PlayerName + " changed their name to " + _playerName, "Chatbox");
//assign the new name
PlayerName = _playerName;
}
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.Width(Screen.width), GUILayout.Height(Screen.height - 75f));
{
// display each message
for (int i = 0; i < messages.Count; i++)
{
GUILayout.Label(messages[i]);
}
}
GUILayout.EndScrollView();
GUILayout.BeginHorizontal(GUILayout.Width(Screen.width));
{
chatText = GUILayout.TextField(chatText, GUILayout.ExpandWidth(true));
if(GUILayout.Button( "Send", GUILayout.Width(100f ) ))
{
//publish the message the player types as:
// [playername] : [message]
if (chatText.StartsWith("/me "))
{
chatText = chatText.Replace("/me", "");
PubNubWrapper.instance.Publish(PlayerName + chatText, "Chatbox");
}
else
{
PubNubWrapper.instance.Publish(PlayerName + ": " + chatText, "Chatbox");
}
// clear the textbox
chatText = "";
}
}
GUILayout.EndHorizontal();
}
/// <summary>
/// Quit the chat box when the player leaves the room and save it for them later
/// </summary>
void OnApplicationQuit()
{
PlayerPrefs.SetString("PlayerName", PlayerName);
}
// Update is called once per frame
void Update () {
}
}
The other thing about this is the webpage will understand the connection, except it won't get the publish and subscribe right. I would like to eventually do more projects so it seems the biggest bottleneck is Unity but I understand I can't do much to change the Asset.
'
I do not own the rights to the code, they are taken from the PubNub website and Unity Multiplayer Games textbook. I simply just made the connection of using a runnable server.

Related

How to manage client connections in a Blazor Server + ASP.NET Core API + SignalR project

I am working on a Blazor Server app over a SignalR connection with an ASP.NET Core API to send real-time updates from the server to clients. However, I am having a problem with managing user connections.
The uniqueness of the problem is that each tab opened in a browser by a user represents an individual connection on the SignalR server. This becomes a problem when a user has multiple tabs open with the application and each of them is generating a unique connection. This is because each tab is considered a unique session and therefore the SignalR server creates a new connection for each tab. For example:
If a user "User1" opens 3 tabs in their browser, 3 individual connections will be created for User1 on the server.
If another user "User2" opens 2 tabs in their browser, 2 more
connections will be created for User2 on the server.
And if I'm not logged in, and I open 3 tabs, it will create 3 more connections on
the server.
The desired environment, regardless of the number of tabs open, instead of duplicating connections:
User1 = 1 connection.
User2 = 1 connection.
Not logged = 1 connection.
My question is how can I effectively manage user connections so that there is only one connection per user/client/session instead of as many as opened tabs. Has anyone had a similar problem and knows how to solve it? I'm sorry if there is an usual easy know fix for this, but I'm looking around and I can't find something that fits exactly my behaviour; and I need some orientation on here instead of copy-paste some code, since conections managment it's a core feature and I'm not much familiar with these.
To clarifly some solutions I've tried are:
Sol. A) In the client: AddSingleton instead of AddScoped
Sol. B) In the client: Set the ConnectionId after hubConn.StartAsync()
Sol. B) In the server: Clients.Client(Context.ConnectionId).SendAsync() instead of Clients.All.SendAsync()
And to mention I didn't used services.AddHttpClient() w/
IClientFactory, but I dont know if it's needed at all or if it's involved in the problem.
Thank you for your time and help!
I provide code used in the connections below:
ASP.NET Core API - SERVER:
Program.cs
using ChartServer.DataProvider;
using ChartServer.RHub;
using SharedModels;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Add CORS Policy
builder.Services.AddCors(option => {
option.AddPolicy("cors", policy => {
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyHeader();
});
});
builder.Services.AddSignalR();
// Register the Watcher
builder.Services.AddScoped<TimeWatcher>();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors("cors");
app.UseAuthorization();
app.MapControllers();
// Add the SignalR Hub
app.MapHub<MarketHub>("/marketdata");
app.Run();
MarketController.cs
namespace ChartServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class MarketController : ControllerBase
{
private IHubContext<MarketHub> marketHub;
private TimeWatcher watcher;
public MarketController(IHubContext<MarketHub> mktHub, TimeWatcher watch)
{
marketHub = mktHub;
watcher = watch;
}
[HttpGet]
public IActionResult Get()
{
if(!watcher.IsWatcherStarted)
{
watcher.Watcher(()=>marketHub.Clients.All.SendAsync("SendMarketStatusData",MarketDataProvider.GetMarketData()));
}
return Ok(new { Message = "Request Completed" });
}
}
}
MarketHub.cs
namespace ChartServer.RHub
{
public class MarketHub : Hub
{
public async Task AcceptData(List<Market> data) =>
await Clients.All.SendAsync("CommunicateMarketData", data);
}
}
TimeWatcher.cs
namespace ChartServer.DataProvider
{
/// <summary>
/// This call will be used to send the data after each second to the client
/// </summary>
public class TimeWatcher
{
private Action? Executor;
private Timer? timer;
// we need to auto-reset the event before the execution
private AutoResetEvent? autoResetEvent;
public DateTime WatcherStarted { get; set; }
public bool IsWatcherStarted { get; set; }
/// <summary>
/// Method for the Timer Watcher
/// This will be invoked when the Controller receives the request
/// </summary>
public void Watcher(Action execute)
{
int callBackDelayBeforeInvokeCallback = 1000;
int timeIntervalBetweenInvokeCallback = 2000;
Executor = execute;
autoResetEvent = new AutoResetEvent(false);
timer = new Timer((object? obj) => {
Executor();
}, autoResetEvent, callBackDelayBeforeInvokeCallback, timeIntervalBetweenInvokeCallback);
WatcherStarted = DateTime.Now;
IsWatcherStarted = true;
}
}
}
Blazor Server app - CLIENT:
Program.cs
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using SignalsServer.Data;
using SignalsServer.HttpCaller;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri("https://localhost:7084/") });
builder.Services.AddScoped<MarketDataCaller>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
MarketDataCaller.cs
namespace SignalsServer.HttpCaller
{
public class MarketDataCaller
{
private HttpClient httpClient;
public MarketDataCaller(HttpClient http)
{
httpClient = http;
}
public async Task GetMarketDataAsync()
{
try
{
var response = await httpClient.GetAsync("marketdata");
if (!response.IsSuccessStatusCode)
throw new Exception("Something is wrong with the connection make sure that the server is running.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
public async Task GetMarketEndpoint()
{
try
{
var response = await httpClient.GetAsync("https://localhost:7193/api/Market");
if (!response.IsSuccessStatusCode)
throw new Exception("Something is wrong with the connection so get call is not executing.");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
throw ex;
}
}
}
}
ChartComponent.razor
#page "/chartui"
#using Microsoft.AspNetCore.SignalR.Client;
#using SharedModels
#using System.Text.Json
#inject IJSRuntime js
#inject SignalsServer.HttpCaller.MarketDataCaller service;
<h3>Chart Component</h3>
<div>
<div class="container">
<table class="table table-bordered table-striped">
<tbody>
<tr>
<td>
<button class="btn btn-success"
#onclick="#generateLineChartTask">Line Chart</button>
</td>
<td>
<button class="btn btn-danger"
#onclick="#generateBarChartTask">Bar Chart</button>
</td>
</tr>
</tbody>
</table>
<div id="market"></div>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Company Name</th>
<th>Volume</th>
</tr>
</thead>
<tbody>
#foreach (var item in MarketData)
{
<tr>
<td>#item.CompanyName</td>
<td>#item.Volume</td>
</tr>
}
</tbody>
</table>
<hr/>
<div class="container">
#ConnectionStatusMessage
</div>
</div>
</div>
#code {
private HubConnection? hubConn;
private string? ConnectionStatusMessage;
public List<Market> MarketData = new List<Market>();
public List<Market> MarketReceivedData = new List<Market>();
private List<string> xSource;
private List<int> ySource;
private List<object> source;
protected override async Task OnInitializedAsync()
{
xSource = new List<string>();
ySource = new List<int>();
source = new List<object>();
await service.GetMarketEndpoint();
hubConn = new HubConnectionBuilder().WithUrl("https://localhost:7193/marketdata").Build();
await hubConn.StartAsync();
if(hubConn.State == HubConnectionState.Connected )
ConnectionStatusMessage = "Connection is established Successfully...";
else
ConnectionStatusMessage = "Connection is not established...";
}
private int contador = 0;
private void MarketDataListener(string chartType)
{
hubConn.On<List<Market>>("SendMarketStatusData", async (data) =>
{
MarketData = new List<Market>();
foreach (var item in data)
{
Console.WriteLine($"Company Name: {item.CompanyName}, Volumn: {item.Volume}");
xSource.Add(item.CompanyName);
ySource.Add(item.Volume);
}
source.Add(ySource);
source.Add(xSource);
MarketData = data;
contador++;
Console.WriteLine($"CONTADOR: {contador}");
InvokeAsync(StateHasChanged);
await js.InvokeAsync<object>(chartType, source.ToArray());
xSource.Clear();
ySource.Clear();
});
}
private void ReceivedMarketDataListener()
{
hubConn.On<List<Market>>("CommunicateMarketData", (data) =>
{
MarketReceivedData = data;
InvokeAsync(StateHasChanged);
});
}
public async Task Dispose()
{
await hubConn.DisposeAsync();
}
async Task generateLineChartTask()
{
MarketDataListener("marketLineChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
async Task generateBarChartTask()
{
MarketDataListener("marketBarChart");
ReceivedMarketDataListener();
await service.GetMarketDataAsync();
}
}
FULL CODE: https://github.com/maheshsabnis/SignalRChartBlazor
In the signalr application, opening the page in the browser will generate a new connectionId, which is the default behavior.
We can maintain the ConnectionIds of each user through the following sample code.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Connections.Features;
using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;
using System.Diagnostics;
namespace SignalRMiddleawre.Hubs
{
/// <summary>
/// </summary>
[Authorize]
public partial class MainHub : Hub
{
#region Connection
/// <summary>
/// Manage Connected Users
/// </summary>
private static ConcurrentDictionary<string?, List<string>>? ConnectedUsers = new ConcurrentDictionary<string?, List<string>>();
/// <summary>
/// OnConnect Event
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
///
public override async Task OnConnectedAsync()
{
// Get HttpContext In asp.net core signalr
//IHttpContextFeature hcf = (IHttpContextFeature)this.Context.Features[typeof(IHttpContextFeature)];
//HttpContext hc = hcf.HttpContext;
//string uid = hc.Request.Path.Value.Split(new string[] { "=", "" }, StringSplitOptions.RemoveEmptyEntries)[1].ToString();
string? userid = Context.User?.Identity?.Name;
if (userid == null || userid.Equals(string.Empty))
{
Trace.TraceInformation("user not loged in, can't connect signalr service");
return;
}
Trace.TraceInformation(userid + "connected");
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
await Clients.All.SendAsync("ServerInfo", userid, userid + " connected, connectionId = " + Context.ConnectionId);
await base.OnConnectedAsync();
}
/// <summary>
/// OnDisconnected event
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public override async Task OnDisconnectedAsync(Exception? exception)
{
string? userid = Context.User?.Identity?.Name;
// save connection
List<string>? existUserConnectionIds;
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
existUserConnectionIds.Remove(Context.ConnectionId);
if (existUserConnectionIds.Count == 0)
{
List<string> garbage;
ConnectedUsers.TryRemove(userid, out garbage);
}
await base.OnDisconnectedAsync(exception);
}
#endregion
#region Message
/// <summary>
/// Send msg to all user
/// </summary>
/// <param name="userid"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task SendMessage(string msgType, string message)
{
await Clients.All.SendAsync("ReceiveMessage", msgType, message);
}
/// <summary>
/// Send msg to user by userid
/// </summary>
/// <param name="connectionId"></param>
/// <param name="message">message format : type-message </param>
/// <returns></returns>
public async Task SendToSingleUser(string userid, string message)
{
List<string>? existUserConnectionIds;
// find all the connectionids by userid
ConnectedUsers.TryGetValue(userid, out existUserConnectionIds);
if (existUserConnectionIds == null)
{
existUserConnectionIds = new List<string>();
}
existUserConnectionIds.Add(Context.ConnectionId);
ConnectedUsers.TryAdd(userid, existUserConnectionIds);
await Clients.Clients(existUserConnectionIds).SendAsync("ReceiveMessage", message);
}
#endregion
}
}

http://localhost:3000/api/forge/designautomation/workitems 500 (Internal Server Error) jquery.min.js:2 POST

I am trying out the step by step tutorial of design-automation in Revit, to modify-your-models from learn.autodesk.io . This code worked perfectly fine even a few days back but today I am suddenly facing this error. I tried recreating the entire project sample once again as per the tutorial but this error is not going away. Can anyone explain what is causing it?
The error log:
fail: Microsoft.AspNetCore.Server.Kestrel[13]
Connection id "0HM7HP2HS8PF1", Request id "0HM7HP2HS8PF1:00000002": An unhandled exception was thrown by the application.
Autodesk.Forge.Client.ApiException: Error calling UploadObject: Error while copying content to a stream. Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host..
at Autodesk.Forge.ObjectsApi.UploadObjectAsyncWithHttpInfo(String bucketKey, String objectName, Nullable`1 contentLength, Stream body, String contentDisposition, String ifMatch, String contentType)
at Autodesk.Forge.ObjectsApi.UploadObjectAsync(String bucketKey, String objectName, Nullable`1 contentLength, Stream body, String contentDisposition, String ifMatch, String contentType)
at forgeSample.Controllers.DesignAutomationController.StartWorkitem(StartWorkitemInput input) in E:\Test-2nd_Attempt\forgeSample\Controllers\DesignAutomationController.cs:line 275
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
DesignAutomationController.cs
using Autodesk.Forge;
using Autodesk.Forge.DesignAutomation;
using Autodesk.Forge.DesignAutomation.Model;
using Autodesk.Forge.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RestSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Activity = Autodesk.Forge.DesignAutomation.Model.Activity;
using Alias = Autodesk.Forge.DesignAutomation.Model.Alias;
using AppBundle = Autodesk.Forge.DesignAutomation.Model.AppBundle;
using Parameter = Autodesk.Forge.DesignAutomation.Model.Parameter;
using WorkItem = Autodesk.Forge.DesignAutomation.Model.WorkItem;
using WorkItemStatus = Autodesk.Forge.DesignAutomation.Model.WorkItemStatus;
namespace forgeSample.Controllers {
[ApiController]
public class DesignAutomationController: ControllerBase {
// Used to access the application folder (temp location for files & bundles)
private IWebHostEnvironment _env;
// used to access the SignalR Hub
private IHubContext < DesignAutomationHub > _hubContext;
// Local folder for bundles
public string LocalBundlesFolder {
get {
return Path.Combine(_env.WebRootPath, "bundles");
}
}
/// Prefix for AppBundles and Activities
public static string NickName {
get {
return OAuthController.GetAppSetting("FORGE_CLIENT_ID");
}
}
/// Alias for the app (e.g. DEV, STG, PROD). This value may come from an environment variable
public static string Alias {
get {
return "dev";
}
}
// Design Automation v3 API
DesignAutomationClient _designAutomation;
// Constructor, where env and hubContext are specified
public DesignAutomationController(IWebHostEnvironment env, IHubContext < DesignAutomationHub > hubContext, DesignAutomationClient api) {
_designAutomation = api;
_env = env;
_hubContext = hubContext;
}
// **********************************
//
/// <summary>
/// Names of app bundles on this project
/// </summary>
[HttpGet]
[Route("api/appbundles")]
public string[] GetLocalBundles() {
// this folder is placed under the public folder, which may expose the bundles
// but it was defined this way so it be published on most hosts easily
return Directory.GetFiles(LocalBundlesFolder, "*.zip").Select(Path.GetFileNameWithoutExtension).ToArray();
}
/// <summary>
/// Return a list of available engines
/// </summary>
[HttpGet]
[Route("api/forge/designautomation/engines")]
public async Task < List < string >> GetAvailableEngines() {
dynamic oauth = await OAuthController.GetInternalAsync();
// define Engines API
Page < string > engines = await _designAutomation.GetEnginesAsync();
engines.Data.Sort();
return engines.Data; // return list of engines
}
/// <summary>
/// Define a new appbundle
/// </summary>
[HttpPost]
[Route("api/forge/designautomation/appbundles")]
public async Task < IActionResult > CreateAppBundle([FromBody] JObject appBundleSpecs) {
// basic input validation
string zipFileName = appBundleSpecs["zipFileName"].Value < string > ();
string engineName = appBundleSpecs["engine"].Value < string > ();
// standard name for this sample
string appBundleName = zipFileName + "AppBundle";
// check if ZIP with bundle is here
string packageZipPath = Path.Combine(LocalBundlesFolder, zipFileName + ".zip");
if (!System.IO.File.Exists(packageZipPath)) throw new Exception("Appbundle not found at " + packageZipPath);
// get defined app bundles
Page < string > appBundles = await _designAutomation.GetAppBundlesAsync();
// check if app bundle is already define
dynamic newAppVersion;
string qualifiedAppBundleId = string.Format("{0}.{1}+{2}", NickName, appBundleName, Alias);
if (!appBundles.Data.Contains(qualifiedAppBundleId)) {
// create an appbundle (version 1)
AppBundle appBundleSpec = new AppBundle() {
Package = appBundleName,
Engine = engineName,
Id = appBundleName,
Description = string.Format("Description for {0}", appBundleName),
};
newAppVersion = await _designAutomation.CreateAppBundleAsync(appBundleSpec);
if (newAppVersion == null) throw new Exception("Cannot create new app");
// create alias pointing to v1
Alias aliasSpec = new Alias() {
Id = Alias, Version = 1
};
Alias newAlias = await _designAutomation.CreateAppBundleAliasAsync(appBundleName, aliasSpec);
} else {
// create new version
AppBundle appBundleSpec = new AppBundle() {
Engine = engineName,
Description = appBundleName
};
newAppVersion = await _designAutomation.CreateAppBundleVersionAsync(appBundleName, appBundleSpec);
if (newAppVersion == null) throw new Exception("Cannot create new version");
// update alias pointing to v+1
AliasPatch aliasSpec = new AliasPatch() {
Version = newAppVersion.Version
};
Alias newAlias = await _designAutomation.ModifyAppBundleAliasAsync(appBundleName, Alias, aliasSpec);
}
// upload the zip with .bundle
RestClient uploadClient = new RestClient(newAppVersion.UploadParameters.EndpointURL);
RestRequest request = new RestRequest(string.Empty, Method.POST);
request.AlwaysMultipartFormData = true;
foreach(KeyValuePair < string, string > x in newAppVersion.UploadParameters.FormData) request.AddParameter(x.Key, x.Value);
request.AddFile("file", packageZipPath);
request.AddHeader("Cache-Control", "no-cache");
await uploadClient.ExecuteAsync(request);
return Ok(new {
AppBundle = qualifiedAppBundleId, Version = newAppVersion.Version
});
}
/// <summary>
/// Helps identify the engine
/// </summary>
private dynamic EngineAttributes(string engine) {
if (engine.Contains("3dsMax")) return new {
commandLine = "$(engine.path)\\3dsmaxbatch.exe -sceneFile \"$(args[inputFile].path)\" $(settings[script].path)", extension = "max", script = "da = dotNetClass(\"Autodesk.Forge.Sample.DesignAutomation.Max.RuntimeExecute\")\nda.ModifyWindowWidthHeight()\n"
};
if (engine.Contains("AutoCAD")) return new {
commandLine = "$(engine.path)\\accoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\" /s $(settings[script].path)", extension = "dwg", script = "UpdateParam\n"
};
if (engine.Contains("Inventor")) return new {
commandLine = "$(engine.path)\\inventorcoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\"", extension = "ipt", script = string.Empty
};
if (engine.Contains("Revit")) return new {
commandLine = "$(engine.path)\\revitcoreconsole.exe /i \"$(args[inputFile].path)\" /al \"$(appbundles[{0}].path)\"", extension = "rvt", script = string.Empty
};
throw new Exception("Invalid engine");
}
/// <summary>
/// Define a new activity
/// </summary>
[HttpPost]
[Route("api/forge/designautomation/activities")]
public async Task < IActionResult > CreateActivity([FromBody] JObject activitySpecs) {
// basic input validation
string zipFileName = activitySpecs["zipFileName"].Value < string > ();
string engineName = activitySpecs["engine"].Value < string > ();
// standard name for this sample
string appBundleName = zipFileName + "AppBundle";
string activityName = zipFileName + "Activity";
//
Page < string > activities = await _designAutomation.GetActivitiesAsync();
string qualifiedActivityId = string.Format("{0}.{1}+{2}", NickName, activityName, Alias);
if (!activities.Data.Contains(qualifiedActivityId)) {
// define the activity
// ToDo: parametrize for different engines...
dynamic engineAttributes = EngineAttributes(engineName);
string commandLine = string.Format(engineAttributes.commandLine, appBundleName);
Activity activitySpec = new Activity() {
Id = activityName,
Appbundles = new List < string > () {
string.Format("{0}.{1}+{2}", NickName, appBundleName, Alias)
},
CommandLine = new List < string > () {
commandLine
},
Engine = engineName,
Parameters = new Dictionary < string, Parameter > () {
{
"inputFile",
new Parameter() {
Description = "input file", LocalName = "$(inputFile)", Ondemand = false, Required = true, Verb = Verb.Get, Zip = false
}
}, {
"inputJson",
new Parameter() {
Description = "input json", LocalName = "params.json", Ondemand = false, Required = false, Verb = Verb.Get, Zip = false
}
}, {
"outputFile",
new Parameter() {
Description = "output file", LocalName = "outputFile." + engineAttributes.extension, Ondemand = false, Required = true, Verb = Verb.Put, Zip = false
}
}
},
Settings = new Dictionary < string, ISetting > () {
{
"script",
new StringSetting() {
Value = engineAttributes.script
}
}
}
};
Activity newActivity = await _designAutomation.CreateActivityAsync(activitySpec);
// specify the alias for this Activity
Alias aliasSpec = new Alias() {
Id = Alias, Version = 1
};
Alias newAlias = await _designAutomation.CreateActivityAliasAsync(activityName, aliasSpec);
return Ok(new {
Activity = qualifiedActivityId
});
}
// as this activity points to a AppBundle "dev" alias (which points to the last version of the bundle),
// there is no need to update it (for this sample), but this may be extended for different contexts
return Ok(new {
Activity = "Activity already defined"
});
}
/// <summary>
/// Get all Activities defined for this account
/// </summary>
[HttpGet]
[Route("api/forge/designautomation/activities")]
public async Task < List < string >> GetDefinedActivities() {
// filter list of
Page < string > activities = await _designAutomation.GetActivitiesAsync();
List < string > definedActivities = new List < string > ();
foreach(string activity in activities.Data)
if (activity.StartsWith(NickName) && activity.IndexOf("$LATEST") == -1)
definedActivities.Add(activity.Replace(NickName + ".", String.Empty));
return definedActivities;
}
/// <summary>
/// Start a new workitem
/// </summary>
[HttpPost]
[Route("api/forge/designautomation/workitems")]
public async Task < IActionResult > StartWorkitem([FromForm] StartWorkitemInput input) {
// basic input validation
JObject workItemData = JObject.Parse(input.data);
string widthParam = workItemData["width"].Value < string > ();
string heigthParam = workItemData["height"].Value < string > ();
string activityName = string.Format("{0}.{1}", NickName, workItemData["activityName"].Value < string > ());
string browerConnectionId = workItemData["browerConnectionId"].Value < string > ();
// save the file on the server
var fileSavePath = Path.Combine(_env.ContentRootPath, Path.GetFileName(input.inputFile.FileName));
using(var stream = new FileStream(fileSavePath, FileMode.Create)) await input.inputFile.CopyToAsync(stream);
// OAuth token
dynamic oauth = await OAuthController.GetInternalAsync();
// upload file to OSS Bucket
// 1. ensure bucket existis
string bucketKey = NickName.ToLower() + "-designautomation";
BucketsApi buckets = new BucketsApi();
buckets.Configuration.AccessToken = oauth.access_token;
try {
PostBucketsPayload bucketPayload = new PostBucketsPayload(bucketKey, null, PostBucketsPayload.PolicyKeyEnum.Transient);
await buckets.CreateBucketAsync(bucketPayload, "US");
} catch {}; // in case bucket already exists
// 2. upload inputFile
string inputFileNameOSS = string.Format("{0}_input_{1}", DateTime.Now.ToString("yyyyMMddhhmmss"), Path.GetFileName(input.inputFile.FileName)); // avoid overriding
ObjectsApi objects = new ObjectsApi();
objects.Configuration.AccessToken = oauth.access_token;
using(StreamReader streamReader = new StreamReader(fileSavePath))
await objects.UploadObjectAsync(bucketKey, inputFileNameOSS, (int) streamReader.BaseStream.Length, streamReader.BaseStream, "application/octet-stream");
System.IO.File.Delete(fileSavePath); // delete server copy
// prepare workitem arguments
// 1. input file
XrefTreeArgument inputFileArgument = new XrefTreeArgument() {
Url = string.Format("https://developer.api.autodesk.com/oss/v2/buckets/{0}/objects/{1}", bucketKey, inputFileNameOSS),
Headers = new Dictionary < string, string > () {
{
"Authorization",
"Bearer " + oauth.access_token
}
}
};
// 2. input json
dynamic inputJson = new JObject();
inputJson.Width = widthParam;
inputJson.Height = heigthParam;
XrefTreeArgument inputJsonArgument = new XrefTreeArgument() {
Url = "data:application/json, " + ((JObject) inputJson).ToString(Formatting.None).Replace("\"", "'")
};
// 3. output file
string outputFileNameOSS = string.Format("{0}_output_{1}", DateTime.Now.ToString("yyyyMMddhhmmss"), Path.GetFileName(input.inputFile.FileName)); // avoid overriding
XrefTreeArgument outputFileArgument = new XrefTreeArgument() {
Url = string.Format("https://developer.api.autodesk.com/oss/v2/buckets/{0}/objects/{1}", bucketKey, outputFileNameOSS),
Verb = Verb.Put,
Headers = new Dictionary < string, string > () {
{
"Authorization",
"Bearer " + oauth.access_token
}
}
};
// prepare & submit workitem
// the callback contains the connectionId (used to identify the client) and the outputFileName of this workitem
string callbackUrl = string.Format("{0}/api/forge/callback/designautomation?id={1}&outputFileName={2}", OAuthController.GetAppSetting("FORGE_WEBHOOK_URL"), browerConnectionId, outputFileNameOSS);
WorkItem workItemSpec = new WorkItem() {
ActivityId = activityName,
Arguments = new Dictionary < string, IArgument > () {
{
"inputFile",
inputFileArgument
}, {
"inputJson",
inputJsonArgument
}, {
"outputFile",
outputFileArgument
}, {
"onComplete",
new XrefTreeArgument {
Verb = Verb.Post, Url = callbackUrl
}
}
}
};
WorkItemStatus workItemStatus = await _designAutomation.CreateWorkItemAsync(workItemSpec);
return Ok(new {
WorkItemId = workItemStatus.Id
});
}
/// <summary>
/// Input for StartWorkitem
/// </summary>
public class StartWorkitemInput {
public IFormFile inputFile {
get;
set;
}
public string data {
get;
set;
}
}
/// <summary>
/// Callback from Design Automation Workitem (onProgress or onComplete)
/// </summary>
[HttpPost]
[Route("/api/forge/callback/designautomation")]
public async Task < IActionResult > OnCallback(string id, string outputFileName, [FromBody] dynamic body) {
try {
// your webhook should return immediately! we can use Hangfire to schedule a job
JObject bodyJson = JObject.Parse((string) body.ToString());
await _hubContext.Clients.Client(id).SendAsync("onComplete", bodyJson.ToString());
var client = new RestClient(bodyJson["reportUrl"].Value < string > ());
var request = new RestRequest(string.Empty);
// send the result output log to the client
byte[] bs = client.DownloadData(request);
string report = System.Text.Encoding.Default.GetString(bs);
await _hubContext.Clients.Client(id).SendAsync("onComplete", report);
// generate a signed URL to download the result file and send to the client
ObjectsApi objectsApi = new ObjectsApi();
dynamic signedUrl = await objectsApi.CreateSignedResourceAsyncWithHttpInfo(NickName.ToLower() + "-designautomation", outputFileName, new PostBucketsSigned(10), "read");
await _hubContext.Clients.Client(id).SendAsync("downloadResult", (string)(signedUrl.Data.signedUrl));
} catch {}
// ALWAYS return ok (200)
return Ok();
}
/// <summary>
/// Clear the accounts (for debugging purpouses)
/// </summary>
[HttpDelete]
[Route("api/forge/designautomation/account")]
public async Task < IActionResult > ClearAccount() {
// clear account
await _designAutomation.DeleteForgeAppAsync("me");
return Ok();
}
}
/// <summary>
/// Class uses for SignalR
/// </summary>
public class DesignAutomationHub: Microsoft.AspNetCore.SignalR.Hub {
public string GetConnectionId() {
return Context.ConnectionId;
}
}
}
This error is from a [Storage API][1]. I can imagine that it may had a temporary issue. Can you retry to see if it works now?

Serilog HTTP Sink custom formatting for Logstash

I am using Serilog HTTP sink for logging to Logstash in my .Net Core Project. In startup.cs I have following code to enable serilog.
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Http("http://mylogstashhost.com:5000").Enrich.WithProperty("user", "xxx").Enrich.WithProperty("serviceName", "yyy")
.MinimumLevel.Warning()
.CreateLogger();
And this code sends logs to the given http address. I can see on fiddler that following json is being posted to the logstash and logstash returns "ok" message.
{"events":[{"Timestamp":"2018-10-19T18:16:27.6561159+01:00","Level":"Warning","MessageTemplate":"abc","RenderedMessage":"abc","user":"xxx","serviceName":"yyy","Properties":{"ActionId":"b313b8ed-0baf-4d75-a6e2-f0dbcb941f67","ActionName":"MyProject.Controllers.HomeController.Index","RequestId":"0HLHLQMV1EBCJ:00000003","RequestPath":"/"}}]}
But when I checked on Kibana, I can not see this log. I tried to figure out what causes it and i realized that if I send the json as following format I can see the Log.
{"Timestamp":"2018-10-19T18:16:27.6561159+01:00","Level":"Warning","MessageTemplate":"abc","RenderedMessage":"abc","user":"xxx","serviceName":"yyy","Properties":{"ActionId":"b313b8ed-0baf-4d75-a6e2-f0dbcb941f67","ActionName":"MyProject.Controllers.HomeController.Index" ,"RequestId":"0HLHLQMV1EBCJ:00000003","RequestPath":"/"}}
So Logstash doesnt like the event to be in Events{} and also it wants "user" and "ServiceName" tags out of "Properties". Is there a way to format my Json like this?
Ok after some research and help, basically to achieve custom formats, one should implement interfaces like ITextFormatter, BatchFormatter etc.
I could achieve the format i need, by modifying ArrayBatchFormatter a little:
public class MyFormat : BatchFormatter
{
/// <summary>
/// Initializes a new instance of the <see cref="ArrayBatchFormatter"/> class.
/// </summary>
/// <param name="eventBodyLimitBytes">
/// The maximum size, in bytes, that the JSON representation of an event may take before it
/// is dropped rather than being sent to the server. Specify null for no limit. Default
/// value is 256 KB.
/// </param>
public MyFormat(long? eventBodyLimitBytes = 256 * 1024): base(eventBodyLimitBytes)
{
}
/// <summary>
/// Format the log events into a payload.
/// </summary>
/// <param name="logEvents">
/// The events to format.
/// </param>
/// <param name="output">
/// The payload to send over the network.
/// </param>
public override void Format(IEnumerable<string> logEvents, TextWriter output)
{
if (logEvents == null) throw new ArgumentNullException(nameof(logEvents));
if (output == null) throw new ArgumentNullException(nameof(output));
// Abort if sequence of log events is empty
if (!logEvents.Any())
{
return;
}
output.Write("[");
var delimStart = string.Empty;
foreach (var logEvent in logEvents)
{
if (string.IsNullOrWhiteSpace(logEvent))
{
continue;
}
int index = logEvent.IndexOf("{");
string adjustedString = "{\"user\":\"xxx\",\"serviceName\" : \"yyy\"," + logEvent.Substring(1);
if (CheckEventBodySize(adjustedString))
{
output.Write(delimStart);
output.Write(adjustedString);
delimStart = ",";
}
}
output.Write("]");
}
}
I would like to extend #nooaa answer with this variation. Instead of manipulating the string to add new objects, I would suggest using Newtonsoft.Json.Linq. This way you can append, add or remove existing properties of the object itself.
Also, instead of doing output.write after each event, you can combine all the output from the events and do output.write once at the end (a bit of performance)
public override void Format(IEnumerable<string> logEvents, TextWriter output)
{
if (logEvents == null) throw new ArgumentNullException(nameof(logEvents));
if (output == null) throw new ArgumentNullException(nameof(output));
// Abort if sequence of log events is empty
if (!logEvents.Any())
{
return;
}
List<object> updatedEvents = new List<object>();
foreach (string logEvent in logEvents)
{
if (string.IsNullOrWhiteSpace(logEvent))
{
continue;
}
// Parse the log event
var obj = JObject.Parse(logEvent);
// Add New entries
obj["#source_host"] = obj["fields"]["MachineName"].Value<string>().ToLower();
// Remove any entries you are not interested in
((JObject)obj["fields"]).Remove("MachineName");
// Default tags for any log that goes out of your app.
obj["#tags"] = new JArray() { "appName", "api" };
// Additional tags from end points (custom based on routes)
if (obj["fields"]["tags"] != null)
{
((JArray)obj["#tags"]).Merge((JArray)obj["fields"]["tags"]);
((JObject)obj["fields"]).Remove("tags");
}
updatedEvents.Add(obj);
}
output.Write(JsonConvert.SerializeObject(updatedEvents));
}
Update
Release Notes v8.0.0
With latest release, you dont override the method anymore.
namespace Serilog.Sinks.Http.BatchFormatters {
public class MyCustomFormatter: IBatchFormatter {
public void Format(IEnumerable<string> logEvents, TextWriter output) {
...
}
}
}
you don't provide any Contructors for it either.
Add queueLimitBytes along with batchFormatter and textFormatter
WriteTo.Http(new Uri(),
batchFormatter: new MyCustomFormatter(),
queueLimitBytes: 50 * ByteSize.MB,
textFormatter: new ElasticsearchJsonFormatter());

Store class instance - Windows store application

I'm a bit new to programing a windows store app.So the question is how can I save an instance of a class in to an xml or binary file.I tried some code but it isn't working.
Hope that some one can steer me in the right direction .
You can serialize your instance by using this code
/// <summary>
/// Deserializes the XML.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xml">The XML.</param>
/// <returns>The instance</returns>
public static T DeserializeXml<T>(this String xml)
{
var bytes = Encoding.UTF8.GetBytes(xml);
using (var stream = new MemoryStream(bytes))
{
var serializer = new DataContractSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
}
}
/// <summary>
/// Serializes the specified instance.
/// </summary>
/// <param name="instance">The instance.</param>
/// <returns>Xml</returns>
public static String SerializeXml(this Object instance)
{
using (var stream = new MemoryStream())
{
var serializer = new DataContractSerializer(instance.GetType());
serializer.WriteObject(stream, instance);
stream.Position = 0;
using (var reader = new StreamReader(stream))
{
var result = "<?xml version='1.0' encoding='UTF-8' ?>";
result += reader.ReadToEnd();
return result;
}
}
}
Next step is to save the serialized instance text to a file.
var filename = "instance.txt";
var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);
var content = yourInstance.SerializeXml();
await FileIO.WriteTextAsync(file, content, Windows.Storage.Streams.UnicodeEncoding.Utf8);
Now there should be a file in your AppPackage-Local-Folder called instance.txt which contains the current instance serialized to xml.
You can use Windows.Storage to store any file, the usage is like IO operation. MSDN
IsolatedStorage is similar to this for Windows Phone apps.

Simulating MySql's PASSWORD() encryption using .NET in Windows 8

PASSWORD() according to MySQL documentation is a double SHA1 algorithm.
In Win32 I was using this method:
public string GenerateMySQLHash(string key)
{
byte[] keyArray = Encoding.UTF8.GetBytes(key);
SHA1Managed enc = new SHA1Managed();
byte[] encodedKey = enc.ComputeHash(enc.ComputeHash(keyArray));
StringBuilder myBuilder = new StringBuilder(encodedKey.Length);
foreach (byte b in encodedKey)
myBuilder.Append(b.ToString("X2"));
return "*" + myBuilder.ToString();
}
SHA1Managed object is not available in the Metro .net framework because the security stuff is now in Windows.Security.Cryptography and not in System.Security.Cryptography.
In the documentation, I see this example to obtain the SHA1 from a string:
public String HashMsg(String strMsg)
{
// Convert the message string to binary data.
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
// Create a HashAlgorithmProvider object.
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha1);
// Hash the message.
IBuffer buffHash = objAlgProv.HashData(buffUtf8Msg);
// Verify that the hash length equals the length specified for the algorithm.
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
// Convert the hash to a string (for display).
return CryptographicBuffer.EncodeToBase64String(buffHash);
}
But I need a double SHA1 algorithm. Any way to do this easy as in win32?
I finally found the solution :), I hope it help you:
/// <summary>
/// Reverse a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
/// <summary>
/// MySQL PASSWORD encryption
/// </summary>
/// <param name="strMsg"></param>
/// <returns></returns>
public String HashMsg(String strMsg)
{
// Convert the message string to binary data.
IBuffer buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(strMsg, BinaryStringEncoding.Utf8);
// Create a HashAlgorithmProvider object.
HashAlgorithmProvider objAlgProv = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha1);
// Hash the message.
IBuffer buffHash = objAlgProv.HashData(objAlgProv.HashData(buffUtf8Msg));
// Verify that the hash length equals the length specified for the algorithm.
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
byte[] arrByteNew;
CryptographicBuffer.CopyToByteArray(buffHash, out arrByteNew);
StringBuilder myBuilder = new StringBuilder(arrByteNew.Length);
foreach (var b in arrByteNew)
myBuilder.Append(b.ToString("X2"));
// Concat with the STRING REVERSED
String stringReversed = "*" + myBuilder.ToString() + ReverseString(strMsg);
buffUtf8Msg = CryptographicBuffer.ConvertStringToBinary(s3, BinaryStringEncoding.Utf8);
buffHash = objAlgProv.HashData(objAlgProv.HashData(buffUtf8Msg));
if (buffHash.Length != objAlgProv.HashLength)
{
throw new Exception("There was an error creating the hash");
}
CryptographicBuffer.CopyToByteArray(buffHash, out arrByteNew);
myBuilder = new StringBuilder(arrByteNew.Length);
foreach (var b in arrByteNew)
{
myBuilder.Append(b.ToString("X2"));
}
stringReversed = "*" + myBuilder.ToString();
return stringReversed;
}