Intercepting #Ajax requests in #CEFSharp (Chrome for C#)

diagram7

The webbrowser control for .NET is a version of IE. That version of IE can be configured via the registry, but it’s still IE, and Chrome is the daddy of browsers. So, if you want to embed a Chrome browser in a C# application, then using CEFSharp is the way to go.

Firstly, you need to install the NUGET package as follows;

Install-Package CefSharp.WinForms

You then will have to change your project to either x86 or x64, since this won’t work as AnyCPU.

Define a reference to ChromiumWebBrowser in your app as follows;

private static ChromiumWebBrowser browser;

Then initialise it using the code;

CefSharpSettings.LegacyJavascriptBindingEnabled = true;
var settings = new CefSettings();
Cef.Initialize(settings);
var strInitialUrl = “http://www.yourwebsite.com”;
browser = new ChromiumWebBrowser(strInitialUrl)
{
RenderProcessMessageHandler = new RenderHandler()
};
InteractionHandler.browser = browser;
var jsInterface = new jsMapInterface
{
onHookEvent = InteractionHandler.HookEventHandler
};
browser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true;
browser.JavascriptObjectRepository.Register(“jsInterface”, jsInterface, isAsync: true, options: BindingOptions.DefaultBinder);
Controls.Add(browser);
browser.Dock = DockStyle.Fill;

Now, I have introduced a few new classes here, that haven’t been defined yet, but we’ll get to them

  • RenderHandler
  • jsMapInterface
  • InteractionHandler

So, let’s start with RenderHandler. This will be used to inject Javascript into the page once it is loaded. The Javascript will call out to jsMapInterface to record the Ajax event.

class RenderHandler : IRenderProcessMessageHandler
{
public void OnContextReleased(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
}

public void OnFocusedNodeChanged(IWebBrowser browserControl, IBrowser browser, IFrame frame, IDomNode node)
{
}

public void OnUncaughtException(IWebBrowser browserControl, IBrowser browser, IFrame frame, JavascriptException exception)
{
}

void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
var strScript = File.ReadAllText(@”inject.js”);
frame.ExecuteJavaScriptAsync(strScript);
}
}

This class does nothing apart for waiting for OnContextCreated to fire, and read the content of Inject.js and put it into the page.

Inject.js contains the following

document.addEventListener(‘DOMContentLoaded’,
function () {
(function(open) {
XMLHttpRequest.prototype.open = function(method, url, async, user, pass) {
this.addEventListener(“readystatechange”,
function() {
if (this.readyState === 4) {
window.jsInterface.hook(this.responseText, method, url, async, user, pass, “Context”);
}
},
false);
open.call(this, method, url, async, user, pass);
};
})(XMLHttpRequest.prototype.open);
});

This file should be set to “Copy Always”. Note the window.jsInterface.hook – this is the call back to the C# Code.

Next we define jsInterface

public class jsMapInterface
{
public Action<hookEvent> onHookEvent = null;

public void hook(string message, string method, string url, bool async, string user, string pass, string context) // Must be lowercase!
{
if (onHookEvent != null)
{
onHookEvent(new hookEvent()
{
message = message,
url = url,
async = async,
user = user,
pass = pass,
context = context
});
}
}

public class hookEvent
{
public string message {get; set;}
public string url {get; set;}
public bool async {get; set;}
public string user {get; set;}
public string pass {get; set;}
public string context { get; set; }
}
}

A very important point to note here that the method MUST be lowercase!

And Finally, we define InteractionHandler as follows;

class InteractionHandler
{
public static ChromiumWebBrowser browser;

public static void HookEventHandler(jsMapInterface.hookEvent hookEventArgs)
{

// And your AJAX response will be caught here!

}
}

You can set a breakpoint on the comment above, to log, or otherwise handle your ajax response that you capture.

Categories: Uncategorized

Capture #Ajax requests with #webbrowser control in C#

web-browser

If you embed a webbrowser control in a C# application, you can interact with third party websites in ways that you would not be permitted to within a standard webpage. One limit though, is although you can detect page changes, you can’t detect ajax calls. Which is one thing you may like to read.

But, a workaround is to inject javascript that hooks into the XMLHttpRequest.open method, and then reports this back to the host application.

First you need a function to inject Javasript into the page;

var strJs = File.ReadAllText(“inject.js”);
webBrowser.Document.InvokeScript(“eval”, new object[] { strJs });

Here is the injected javascript:

(function (open) {

XMLHttpRequest.prototype.open = function (method, url, async, user, pass) {

this.addEventListener(“readystatechange”, function () {
if (this.readyState == 4) {
window.external.Hook(this.responseText);
}
}, false);

open.call(this, method, url, async, user, pass);
};

})(XMLHttpRequest.prototype.open);

Where window.external.Hook  is going to be defined in C# as follows;

[ComVisible(true)]
public class ScriptManager
{
public void Hook(string message)
{
System.Diagnostics.Debug.WriteLine(message);
}
}

You pass a ScriptManager instance to the webbrowser control as follows;

webBrowser.ObjectForScripting = new ScriptManager();

Now, whenever an ajax call is made, it is written to the debug console.

To read more about IE in C#, check out this article:

http://www.webtropy.com/articles/InternetExplorer.asp?Internet%20explorer

Categories: Uncategorized

Hello World on #tvOS

IMG_4989

Creating a Hello World app for TVOS is so easy it barely needs a blog post, but the basics are always easy. I just created a TVOS single view app in XCode, connected the Apple TV via USB-C, and ran. The icon appeared on the Apple TV, and it worked straight away.

If you want the code; for whatever reason – check it out here:

https://github.com/infiniteloopltd/helloworld-tvos

The tiny bit of code I added was;

   @IBAction func btnClicked(_ sender: UIButton) {

        lblHello.text = “Well hello there!”;

    }

    @IBOutlet weak var lblHello: UILabel!

Categories: Uncategorized

Supporting #IE10 on #AWS #Cloudfront

ie10-1

IE10 may have less than 5% worldwide usage, but if you want to have a inclusive website, then perhaps you shouldn’t confront them with this error message (above) when you try to serve them HTTPS content. – Maybe they should updgrade. but as a developer, that’s not your choice to make.

If you use Amazon Cloudfront over HTTPS, then by default, TLS 1.0 is not supported, and by default, TLS 1.1 and TLS 1.2 are not supported on IE 10. So, if you can’t get your user to upgrade, then you have to make cloudfront more inclusive (and yes. less secure). But if you’re just serving images, then perhaps it’s not a big issue.

So, to fix this, go to Cludfront, and click on your distribution, click edit, and change your security policy to TLSv1_2016 and Edit.

ie10-2

Wait for your distribution to deploy, and you can use SSLscan to verify that TLS 1.0 is now supported, along with TLS 1.1 and TLS 1.2

 

 

Categories: Uncategorized

Make an HTTP request via #TCP over an Authenticated #proxy in C#

working-behind-proxy-network

If you ever find yourself reverting to writing HTTP code using TCPClient, do a double-think, since there is very few situations where you genuinely need to resort to writing TCP/IP code. You can do an Authenticated proxy call using standard HTTP calls, by just setting the networkCredentials on a WebProxy class.

However, if you’ve read this caveat, and you still insist that you need to write code at TCP/IP level, then here’s some code that demonstrates how to make a simple HTTPS get request via an Authenticated HTTP Proxy (Luminati)

The authentication token, which I’ve omitted below, for obvious reasons is a base64 encoded version of your username : your password .

var tcp = new TcpClient(“zproxy.luminati.io”, 22225);
var stream = tcp.GetStream();

var connect = Encoding.ASCII.GetBytes(“CONNECT icanhazip.com:443 HTTP/1.0\n” +
“proxy-authorization: Basic XXXXXX==\n\n”);
stream.Write(connect, 0, connect.Length);
var rawStream = new StreamReader(stream);
rawStream.ReadLine();
var ssl = new SslStream(stream);
ssl.AuthenticateAsClient(“www.icanhazip.com”, null, SslProtocols.Tls12, false);
var strPostString = “GET / HTTP/1.0\r\n” +
“Host:www.icanhazip.com\r\n” +
“\r\n”;
var send = Encoding.ASCII.GetBytes(strPostString);
ssl.Write(send, 0, send.Length);
var sr = new StreamReader(ssl);
var str = sr.ReadToEnd();
tcp.Close();
ssl.Close();
return str;

 

Categories: Uncategorized

Determine the age of a #Spanish Vehicle through it’s number plate

Categories: Uncategorized

Creating a #macOS App using HTML and #Javascript

Screenshot 2018-10-06 at 20.38.30

You can create great macOS apps with Swift, and if you’ve got the time to learn a new language, then this is the way to go. But if you already have something cool made with HTML and Javascript, then you can turn it into a macOS app with a few simple steps.

TL;DR;

If you want to skip to the end, you can just clone the GIT repo at https://github.com/infiniteloopltd/UnderTheCryptOSX

But, if you fancy going step by step, – open up XCode and create a new macOS / Cocoa app. Drop a WKWebView onto the form, and set it’s constraints so that it fills the view. Then drag an outlet called;

@IBOutlet weak var webkitview: WKWebView!

You’ll need to add a reference to WebKit as follows;

import WebKit

Then in the viewDidLoad, add the following lines

let url = Bundle.main.url(forResource: “index”, withExtension: “html”)!
webkitview.loadFileURL(url, allowingReadAccessTo: url)
let request = URLRequest(url: url)
webkitview.load(request)

You can then go Add files to … and add your index.html to the project. Now, if you run this now, you may get a white page. To get around this, you will need to go to the project > Capabilities > App Sandbox and click “Outgoing connections (Client)”

Screenshot 2018-10-06 at 20.44.41

Another thing that you may have to do, or else the app may get rejected by apple, is to exit the app when the window is closed. you do this in AppDelegate.swift as follows;

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool
{
return true
}

 

Categories: Uncategorized

Sending multiple attachments with #SMTPJS

smtpjs

UPDATE:

Check out the V3 version of the API for a much easier way to do this:

https://blog.dotnetframework.org/2018/12/07/smtpjs-now-supports-multiple-attachments-replyto-and-sender-name/

 

One of the limitations of SMTPJS.com is that you can only send one attachment, this blog post demonstrates a way of sending multiple attachments, by zipping them into one file. It also keeps the size of your email down also.

This uses the JSZip library, which I’ve loaded via the cloudflare CDN. Note the mime type used: application/x-zip-compressed this is important, so that SMTPJS will send the file as a attachment named file.zip, rather than the default file.txt

<html>
src="https://smtpjs.com/v2/smtp.js">
src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.js">
language="javascript">
function uploadFileToServer()
{
  var zip = new JSZip();
  var numberOfFiles = event.srcElement.files.length;
  var numberOfProcessedFiles = 0;
  for(var i=0; inumberOfFiles; i++)
  {
    (function(i){
     var file = event.srcElement.files[i];
     var reader = new FileReader();
     reader.readAsBinaryString(file);
     reader.onload = function () {
        numberOfProcessedFiles++;
        zip.file(file.name,btoa(reader.result), {base64: true} );
        if (numberOfProcessedFiles == numberOfFiles)
        {
          zip.generateAsync({type:"base64"})
          .then(function(content) {
            var datauri = "data:application/x-zip-compressed;base64," + content;
            Email.sendWithAttachment("YOUR@EMAIL.com",
                "THEIR@EMAIL.com",
                "This is the Subject ",
                "This is the Body - see file.zip attachment!",
                "****SMTP HOST****",
                "****SMTP USERNAME****",
                "****SMTP PASSWORD****",
                datauri,
                function done(message)
                {
                   alert("Message sent OK")
                }
              );
          });
        }
     };
     })(i);
  }
}

<input type="file" id="fileupload" onchange="uploadFileToServer()" multiple/>
</html>


 

Categories: Uncategorized

Charging a credit card with #Stripe in C#

twitter

This has been demoed a million times before, but just to show how ridiculously simple it is to bill a card with stripe, here’s a code example in C#

You first open package manager and type

Install-Package Stripe.net

Then use the following code:

var strStripeSecretKey = “sk_live_xxxxxxxxxxxxxx”;
var strCardNumber = “xxxxxxxxxxxxxxxxx”; // Long card number
var strCvCNumber = “xxx”;
var intExpiryMonth = 1;
var intExpiryYear = 2019;
var requestOptions = new StripeRequestOptions() { ApiKey = strStripeSecretKey };
var chargeService = new StripeChargeService();
var chareResponse = chargeService.Create(new StripeChargeCreateOptions()
{
Amount = 1000, // In pence – i.e. £10
Currency = “GBP”,
SourceCard = new SourceCard()
{
Number = strCardNumber,
Cvc = strCvCNumber,
ExpirationMonth = intExpiryMonth,
ExpirationYear = intExpiryYear
}
}, requestOptions);

And that’s all there is!  – wow…

 

Categories: Uncategorized

#Javascript Error handling with #Chakra in C#

Chakras Woman Rainbow Background

Microsoft’s ChakraCore DLL allows you run Javascript within C# applications using IE’s Javascript interpreter, without having to spawn an exernal process like PhantomJS. This has great applications for situations where you might want to evaluate a user-provided mathematical equation, perhaps like within a spreadsheet, or a statistical application.

The Hello world example can be cloned from Github here; https://github.com/Microsoft/Chakra-Samples/tree/master/ChakraCore%20Samples/Hello%20World/Windows/C%23

You’ll also need to get the built version of the DLL from Nuget here;

Install-Package Chakra.Core

I noticed that I had to copy the DLL from bin\x86 into the bin folder when running an application as 32 bit (A console app), and copy bin\x64 into the bin folder when running under IIS (64 bit)

One thing that I had to build myself in the C# code, was error handling. If the Javascript expression was buggy, then the result was null, with no information about why the Javascript had failed (for example, I noticed that btoa() and atob() were missing in Chakra – for base64 encoding/decoding).

So, to capture the error message, you need to add the following lines after RunScript

varerror = Native.JsRunScript(script, currentSourceContext++, “”, outresult);
JavaScriptValueexception;
Native.JsGetAndClearException(outexception);
JavaScriptValueexceptionString;
Native.JsConvertValueToString(exception, outexceptionString);
varstrEx = JavascriptValueToString(exceptionString);

Where JavascriptValueToString is defined as follows;

private static string JavascriptValueToString(JavaScriptValuevalue)
{
// Project script result in JS back to C#.
IntPtr resultPtr;
UIntPtr stringLength;
Native.JsStringToPointer(value, outresultPtr, outstringLength);
stringresultString = Marshal.PtrToStringUni(resultPtr);
return resultString;
}
Categories: Uncategorized