Archive

Author Archive

Running #Mac on #AWS

Running Mac on AWS isn’t that new, it’s been available since 2020, however, it’s a useful cost-saver for developers looking to create an iOS app without purchasing a Mac. Using this approach, you can create a remotely-accessible Mac that you can turn on and off when you need it. Theoretically, costing about $1 an hour (Effetively, the host needs to be reserved for 24 hours, so it’s more like $24 a day), which makes it more or less like the MacInCloud offering of $30 a day.

Before you can create a Mac Ec2 Instance, you need to create a Mac Dedicated host. Once this is created, you can create a Mac Ec2 Instance and apply it to the host. Once created, you can connect to it using a VNC Client (Assuming you want a GUI interface)

Copy/Pasting from AWS help, here is how you connect via VNC from Windows

2.    Run the following command to install and start VNC (macOS screen sharing SSH) from the Mac instance

sudo defaults write /var/db/launchd.db/com.apple.launchd/overrides.plist com.apple.screensharing -dict Disabled -bool false
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.screensharing.plist

3.    Run the following command to set a password for ec2-user:

sudo /usr/bin/dscl . -passwd /Users/ec2-user

4.    [ON YOUR WINDOWS PC] Create an SSH tunnel to the VNC port. In the following command, replace keypair_file with your SSH key path and 192.0.2.0 with your instance’s IP address or DNS name.

ssh -i keypair_file -L 5900:localhost:5900 ec2-user@192.0.2.0

Note: The SSH session should be running while you’re in the remote session.

5.    Using a VNC client, connect to localhost:5900.

And then you can go ahead and install XCode and your App source code.

Don’t forget to de-allocate your dedicated host after stopping your ec2 instance, otherwise you’ll have a huge bill at the end of the month!

The overall experience is much more “clunky” and slow than a real Mac, it’s not much of a substitute, and the risk that if you forget the de-allocate the dedicated host, then it’ll cost you more than a mac itself in AWS fees.

Advertisement
Categories: Uncategorized

A Waterfall pattern for C#

First, let’s paint a scenario – you can query multiple sources for the same data, with the hope that one of these sources will have a response.

These sources can respond in three possible ways;

1 . An answer.

2. An exception indicating that the answer is impossible, and there is no need to query other sources

3. An exception indicating that the source failed, but perhaps another source will work.

So, Let’s define an exception of case (2)

class ExpectedException : Exception
{
}

Now, define our three sources, which must have the same input and output types, in this case they will be strings, but they could be any types, as long as they are consistent.

static string SourceA(string input)
{
	throw new Exception("Some random error");
}

static string SourceB(string input)
{
	throw new ExpectedException();
}

static string SourceC(string input)
{
	return "OK";
}

Now, we define a Waterfall function, that will call a list of functions in order, until it reaches either case (1) or case (2)

private static T1 Waterfall<T1,T2>(IEnumerable<Func<T2, T1>> waterfallFunctions, T2 parameter)
{
	var waterfallExceptions = new List<Exception>();
	foreach (var waterfallFunction in waterfallFunctions)
	{
		try
		{
			var functionResult = waterfallFunction(parameter);
			return functionResult;
		}
		catch (ExpectedException)
		{
			throw;
		}
		catch (Exception e)
		{
		   waterfallExceptions.Add(e);
		}
	}
	throw new AggregateException(waterfallExceptions);
}

Which is called as follows

var result = Waterfall( new List<Func<string, string>>
{
	SourceA,
	SourceC,
	SourceB
},"hello");

This code is available on a public Github repo here: https://github.com/infiniteloopltd/Waterfall

Hope this helps someone!

Categories: Uncategorized

A pattern for dealing with #legacy code in c#

static string legacy_code(int input)
{
	// some magic process
	const int magicNumber = 7;

	var intermediaryValue = input + magicNumber;
	
	return "The answer is " + intermediaryValue;
}

When dealing with a project more than a few years old, the issue of legacy code crops up time and time again. In this case, you have a function that’s called from lots of different client applications, so you can’t change it without breaking the client apps.

I’m using the code example above to keep the illustration simple, but you have to imagine that this function “legacy_code(int)”, in reality, could be hundreds of lines long, with lots of quirks and complexities. So you really don’t want to duplicate it.

Now, imagine, that as an output, I want to have just the intermediary value, not the string “The answer is …”. My client could parse the number out of the string, but that’s a horrible extra step to put on the client.

Otherwise you could create “legacy_code_internal()” that returns the int, and legacy_code() calls legacy_code_internal() and adds the string. This is the most common approach, but can end up with a rat’s nest of _internal() functions.

Here’s another approach – you can tell me what you think :

static string legacy_code(int input, Action<int> intermediary = null)
{
	// some magic process
	const int magicNumber = 7;

	var intermediaryValue = input + magicNumber;

	if (intermediary != null) intermediary(intermediaryValue);

	return "The answer is " + intermediaryValue;
}

Here, we can pass an optional function into the legacy_code function, that if present, will return the intermediaryValue as an int, without interfering with how the code is called by existing clients.

A new client looking to use the new functionality could call;

int intermediaryValue  = 0;
var answer = legacy_code(4, i => intermediaryValue = i);
Console.WriteLine(answer);
Console.WriteLine(intermediaryValue);

This approach could return more than one object, but this could get very messy.

Categories: Uncategorized

Using #AppsScript to call an #API from Google Sheets

AppsScript is a Macro programming language that you can use within Google Sheets to automate data entry. In this case, we want to take one cell, which contains a vehicle registration number, and run it through an API, in order to extract details, like the make / model / VIN of that car. The API providing this is available here; https://www.carregistrationapi.in (India) – Many other countries are available, like the US, UK, Australia, South Africa, etc., but this particular example is for India.

To Access the App Script “IDE” press Extensions > App-Script on your google sheet. Here I am using the code as follows:


var USERNAME = "***USERNAME GOES HERE ****";
var PASSWORD = "*** PASSWORD GOES HERE ****";


function CheckIndia(plate,property,subproperty)
{
  var userProperties = PropertiesService.getUserProperties();
  var cacheHit = userProperties.getProperty(plate);
  if (cacheHit != null)
  {
      var cacheData = JSON.parse(cacheHit);
      if(subproperty == null)
      {
        return cacheData[property];
      }
      else
      {
        return cacheData[property][subproperty];
      }
  }
  // Sample UP14CT0093
  var url = 'https://www.regcheck.org.uk/api/json.aspx/CheckIndia/' + plate;
  var headers = {
    "Authorization" : "Basic " + Utilities.base64Encode(USERNAME + ':' + PASSWORD)
  };
  var params = {
    "method":"GET",
    "headers":headers
  };
   var response = UrlFetchApp.fetch(url,params);
   var json = response.getContentText();
   userProperties.setProperty(plate,json);
   var data = JSON.parse(json);   
   if(subproperty == null)
   {
      return data[property];
   }
   else
   {
      return data[property][subproperty];
   }
}

The USERNAME / PASSWORD variables need to be taken from your registration on https://www.carregistrationapi.in .

The Function “CheckIndia” is what you call from within the Google Sheet Cell, such as:

=CheckIndia(A2,”Description”)

Here, in the example above, A2 is the cell that contains the license plate, and “Description” is the make + model of the vehicle.

There is a level of internal caching built-in so that the API is not called every single time a call is made, if the call was made elsewhere on the sheet.

Categories: Uncategorized

Determine the country code from country name in C#

If you are trying to determine the country code (“IE”) from a string like “Dublin, Ireland”, then generally the best approach is to use a Geolocation API, such as Google Geocode, or Here maps, or the plethora of others. However, if speed is more important than accuracy, or the volume of data would be too costly to run through a paid API, then here is a simple script in C# to determine the country code from a string

https://github.com/infiniteloopltd/CountryISOFromString/

The code reads from an embedded resource, which is a CSV of country names. Some of the countries are repeated to allow for variations in spelling, such as “USA” and “United States”. The list is in English only, and feel free to submit a PR, if you have more variations to add to this.

It’s called quite simply as follows;

var country = Country.FromString("Tampere, Pirkanmaa, Finland");
Console.WriteLine(country.code);
Categories: Uncategorized

Send email from #SQL server using a #CLR function

Send Email SQL CLR

Send Email from a SQL CLR function

Although you can, and should use sp_send_dbmail to send email from SQL server, it’s often not quite as flexible as you need it to be. So here is a .NET CLR Function that you can install in your MSSQL server, in order to send an email using whatever additional configuration that you need.

You need to run these commands before installing the assembly

EXEC sp_changedbowner 'sa'
ALTER DATABASE <your-database> SET trustworthy ON


CREATE ASSEMBLY [SendEmailCLR]
    AUTHORIZATION [dbo]
    FROM 0x4D5A90000300000004000000FFFF000.....
    WITH PERMISSION_SET = UNSAFE;
  
CREATE PROCEDURE [dbo].[SendEmail]
@smtpServer NVARCHAR (MAX) NULL,
@smtpUsername NVARCHAR (MAX) NULL, 
@smtpPassword NVARCHAR (MAX) NULL, 
@from NVARCHAR (MAX) NULL, 
@to NVARCHAR (MAX) NULL, 
@subject NVARCHAR (MAX) NULL, 
@body NVARCHAR (MAX) NULL
AS EXTERNAL NAME [SendEmailCLR].[SendEmailCLR].[SendEmail]

The full binary string is redacted here to save space, but you can get this from https://github.com/infiniteloopltd/SendEmailSQLCLR/blob/master/bin/Debug/SendEmailCLR_4.publish.sql

Categories: Uncategorized

Verify an Email address without sending an Email via an #API for free

One of these two email addresses is valid : gianluca.91@gmail.com or gianluca.92@gmail.com – how can you tell which one? Regexes will say both are valid, even a DNS MX lookup will say that @gmail.com is valid.

Here’s the trick: https://avatarapi.com/avatar.asmx?op=VerifyEmail

It’s a free API, that does not require registration, or authentication, and does not store the email addresses supplied to it. It does not send an email, but just checks the mailbox.

Here is a result for gianluca.92@gmail.com

<EmailVerificationResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://avatarapi.com/">
<Verification>FAIL</Verification>
<MailExchange>alt4.gmail-smtp-in.l.google.com.</MailExchange>
<SmtpResponse>550-5.1.1 The email account that you tried to reach does not exist. Please try</SmtpResponse>
</EmailVerificationResponse>

And here is the result for gianluca.91@gmail.com

<EmailVerificationResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://avatarapi.com/">
<Verification>SUCCESS</Verification>
<MailExchange>alt4.gmail-smtp-in.l.google.com.</MailExchange>
<SmtpResponse>250 2.1.5 OK hf21-20020a17090aff9500b001bc3052777csi2002522pjb.42 - gsmtp</SmtpResponse>
</EmailVerificationResponse>

It also works with every email host, not just Gmail. However, some mail exchangers do not give information on their mailboxes, in which case the result can be inconclusive.

Categories: Uncategorized

Decoding binary #WebSockets data using C#

On some websites, you may notice data being exchanged between server and client, with no evident Ajax calls being made, in which case there may be activity on the WebSockets (WS) channel, and in this channel, if you are greeted by a jumble of binary data, then you may feel like giving up, but you may find it is easier to decode than you think.

The first clue I noticed was that there was a request header called

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Where deflate is a compression mechanism, which is similar to GZip, and can be decoded easily in C#, First step, though is to view the binary data as base64, so you can copy & paste it, then using this function;

public static byte[] Decompress(byte[] data)
{
	MemoryStream input = new MemoryStream(data);
	MemoryStream output = new MemoryStream();
	using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
	{
		dstream.CopyTo(output);
	}
	return output.ToArray();
}

Which is called as follows;

var binInput = Convert.FromBase64String(b64Input);
var bDeflate = Decompress(binInput);
var output = Encoding.UTF8.GetString(bDeflate);

And from there, you see much more familiar JSON text.

Categories: Uncategorized

Handling #SCA (Strong Customer Authentication) with #Stripe and C#

Strong customer Authentication is where your bank (card issuer) will show a popup during your purchase to offer an added level of protection for the consumer, to make sure it really is you, and not someone who’s cloned your card details.

It’s also a massive headache for developers!

Massive disclaimer: This was my approach at fixing it, follow Stripe documentation unless you’re really stuck!

So, capturing a customer (cus_xxxx) token is unchanged, what changes when you try to subscribe that customer to a subscription, then it fails. Here I’m adding “allow_incomplete” to allow the subscription to return data even if the customer has SCA enabled.

  const string strUrl = "https://api.stripe.com/v1/subscriptions";
  var postData = "customer=" + customerId;
  postData += "&items[0][price]=" + priceReference;
  postData += "&payment_behavior=allow_incomplete"; // NEW*SCA*

If we check the return value, and the status says incomplete then we have to jump through a few more hoops before we can get the money from the customer.

First, we get the latest_invoice value from the return, and call the stripe API “https://api.stripe.com/v1/invoices/<id here>” and from the response, we get the payment_intent id.

Then with the payment_intent id, we call the stripe API : “https://api.stripe.com/v1/payment_intents/<id here>” and get the client_secret

Now, we pass the client_secret back to our client side, and then we need to call the Stripe SDK, which is included as follows;

<script src="https://js.stripe.com/v3/"></script> <!-- SCA -->

Then it’s initialized as follows; (I’m using JQuery here)

   $(init);
    var stripe = {};
    function init() {
        stripe = Stripe("pk_test_xxxxxxxxxxx"); // SCA
    }

Then once we get the client_secret back somehow; we call the javascript

stripe
.confirmCardPayment(data)
.then(function (result) {
// Handle result.error or result.paymentIntent
console.log(result);
alert("SCA OK!");
});

And it works in test anyway! – let me know if you spot any glaring issues.

Categories: Uncategorized

Automate #Google login using C# and MasterDev ChromeDevTools

Automating a Chrome login in C# is difficult, because Google is clever enough to detect if you are using an embedded WebBrowser – either the embedded InternetExplorer Control, or CEFSharp, and probably others. It’s also a multi-step process, so you have to get the synchronisation right.

So, this is where ChromeDevTools by MasterDev comes in; The original repo is here:

https://github.com/MasterDevs/ChromeDevTools

I’ve forked this, and made my own changes and fixes here;

https://github.com/infiniteloopltd/ChromeDevTools/blob/master/source/Sample/Program.cs

How MasterDev ChromeDevTools differs from CEFSharp, is that chrome is spawned in a separate process, and it uses WebSockets (WebSocket4Net.WebSocket) to communicate between the host application in C# and Chrome. This also means you can run it in AnyCPU mode, and you don’t have to worry about x86 or x64 CPU types. Oh, and of course, Google can’t detect that you’re listening in to what Chrome is doing.

Some organisational changes I made to the sample code, so that it would run in a synchronous manner was this helper function WaitForEvent<T> which effectively does nothing until a given Event is received from the browser

private static void WaitForEvent<T>(IChromeSession chromeSession) where T : class
{
	var waiter = new ManualResetEventSlim();
	chromeSession.Subscribe<T>(eventFired =>
	{
		waiter.Set();
	});
	waiter.Wait();
}

This method will block indefinitely if the event is never received, but that’s another issue.

I also added this helper method to run Javascript on the page, since I would be calling it repeatedly;

private static async Task<string> ExecuteJavascript(IChromeSession chromeSession, string javaScriptToExecute)
{
	var evalResponse = await chromeSession.SendAsync(new EvaluateCommand
	{
		Expression = javaScriptToExecute,
	});
	if (evalResponse.Result.ExceptionDetails != null)
	{
		return evalResponse.Result.ExceptionDetails.ToString();
	}
	return evalResponse.Result.Result.Value == null ? "" : evalResponse.Result.Result.Value.ToString();
}

Here, I am returning everything as a string, regardless of type – even errors. I’d leave it up to the client application to deal with unexpected values. In my case, I didn’t even need return values.

Everything else is quite straightforward. It’s a matter of navigating to the login page, wait for the page to load, enter the username, press next, wait for the next frame to load, then enter the password, press next, wait for a login, then I can process the page.

Of course, Anything unexpected will make the process hang indefinitely, so this is in no way robust, but it’s a proof of concept, that I hope it helps someone.

Categories: Uncategorized
%d bloggers like this: