Amazon SNS custom platform specific payloads for apns

The SNS documentation explains that you can specify custom payloads per platform, by specifying the platform now. The APNS sandbox and production APNS require different keys.

APNS and APNS_SANDBOX are separate platforms when specifying the payloads. Additional pitfall was that I was specifying the default message, which is automatically converted into {aps: alert: “message” }}, so messages were coming in at the iOS client, but not the ones I was expecting.

Also, the payload for each application must be json, but properly escaped.

##Example payload from the documentation

{
    "default": "This is the default message which must be present when publishing a message to a topic. The default message will only be used if a message is not present for one of the notification platforms.",
    "APNS": "{\"aps\":{\"alert\": \"Check out these awesome deals!\",\"url\":\"www.amazon.com\"} }",
    "GCM": "{\"data\":{\"message\":\"Check out these awesome deals!\",\"url\":\"www.amazon.com\"}}",
    "ADM": "{ \"data\": { \"message\": \"Check out these awesome deals!\",\"url\":\"www.amazon.com\" }}"
}

When using APNS in sandbox mode you need to specify APNS_SANDBOX instead of APNS, this is not clear from the documentation as you can see in the example.

The list of Applications in the Console will also make this more clear, see the screenshot below:

Leave a Comment

Clean build with ninja

When building the Chromium project I was looking for a way to restart the entire build. This means removing all output files, so that every buildstep is executed again. When a build is started the need to recompile the files is normally determined automatically (by date for example).

The build tool used by chromium is called ninja. After some searching I found that there is an option to execute so-called sublevel tools via the -t option. It turns out that there is a subtool called clean which will remove all output files.

ninja -t clean

Leave a Comment

Make image clickable without using jQuery

For a secondcrack blog I found myself needing to be able to make the images in the post clickable so that they could be opened in a bigger size. It was a simple use case, so I did not feel like including external libraries to accomplish this (jquery or other image libraries). As it turns out, all images are available via the document attribute images. The solution posted below iterates over all the images, filters them based on the fact that they have /media in the source (only those needed to be clickable) and sets an onclick event.

//open images in a new window
var images = document.images;
for(i = 0, imagesLength = images.length; i < imagesLength; i++) {
    if (images[i].src.indexOf('/media') !== -1) {
        images[i].setAttribute('onclick', 'window.location.href = \'' + images[i].src + '\';');
}
}

Leave a Comment

Two ways to delete files based on age in Linux

I have found that there are two different ways to clean up a folder, by removing older files. One methods removes the files or folders based on their age, regardless of how many there are. I use this for example to delete backup folders, beyond their age.

Remove using tail

This method assumes that there is one file for every day, for example for backups. The command below removes all files order than 30 days. It is important there is only file per day, because else the tail results would not be correct. The advantage here is that if the backup fails, the older backup files will not be removed because of their age.

ls -1c ./ | tail -n +30 | xargs rm -rf

Remove using find

This command selects all files with a modification time older than 2 days and removes them.

0 15 * * * find ~/files/* -mtime +2 -exec rm {} \;

Leave a Comment

PHP Curl SSL on Windows

When developing on Windows I regularly found myself using the line below to circument SSL errors on Windows when using Curl in PHP:

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

If I do not set these curl options, the request would fail with the message: Details: SSL3_GET_SERVER_CERTIFICATE:certificate verify failed. This is a
warning you normally only expect when connecting to a host with a self-signed certificate or something else. But on Windows this also happens for correct
certificates, because the certificate chain cannot be established. This can be solved in two steps:

  1. Download file with root certificates from http://curl.haxx.se/docs/caextract.html
  2. Add the lines below to your php.ini file (where the path is where you downloaded the file in step 1.

    [PHP_CURL]
    curl.cainfo=c:\apps\php\cacert.pem

This post is based on a solution in a stackoverflow post.

Leave a Comment

Kanboard Trello Import tool

Recently I found kanboard, an open source tool which allows you to organize projects
and tasks on a board with columns. Until now I was using Trello. My preference is not to use
Cloud Services for my private data. Kanboard is a perfect alternative to Trello for the functionality that I use.

I wrote a small script to import a Trello json export via the Kanboard json RPC interface. This can be found
at github.

Leave a Comment

Amazon Elastic Transcoder Notifications

The Elastic Transcoder can be configured via the Simple Notification Service (SNS) to send notifications when a transcoding job changes
it state (new, in progress, completed, etc.). The details of these notifications are not defined clearly in the documentation, which is
why I list them in this post. These are actual notification from my set up in AWS. See the documentation
on how to set up the notifications.

Each notification has the default attributes every notification has. The Message attribute has a JSON encoded string that contains the
Elastic Transcoder specific information. See examples below, I only use the COMPLETED and ERROR type of notifications.

From the data the URL to the video can be deduced, if you know the configured output bucket for the pipeline in the Elastic Transcoder.

Completed

{
  "Type" : "Notification",
  "MessageId" : "dd1bf740-2a0a-5173-a792-ba70434938b4",
  "TopicArn" : "arn:aws:sns:eu-west-1:778005704149:ddnl-transcoding-dev",
  "Subject" : "Amazon Elastic Transcoder has finished transcoding job 1421240053904-6ozpim.",
  "Message" : "{\n  \"state\" : \"COMPLETED\",\n  \"version\" : \"2012-09-25\",\n  \"jobId\" : \"1421240053904-6ozpim\",\n  \"pipelineId\" : \"1419424647588-9osc91\",\n  \"input\" : {\n    \"key\" : \"uploads/0ad92625/ffb5d317/cef636ae/20150113101221.video.mp4\"\n  },\n  \"outputKeyPrefix\" : \"video/uploads/0ad92625/ffb5d317/cef636ae\",\n  \"outputs\" : [ {\n    \"id\" : \"1\",\n    \"presetId\" : \"1419429926952-dmb9yx\",\n    \"key\" : \"9946d18c.mp4\",\n    \"thumbnailPattern\" : \"9946d18c-{count}\",\n    \"status\" : \"Complete\",\n    \"duration\" : 4,\n    \"width\" : 480,\n    \"height\" : 480\n  } ],\n  \"userMetadata\" : {\n    \"todReplyId\" : \"5\"\n  }\n}",
  "Timestamp" : "2015-01-14T12:54:27.285Z",
  "SignatureVersion" : "1",
  "Signature" : "xxx",
  "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-xxx.pem",
  "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=xxx"
}

Message JSON

{
    "state": "COMPLETED",
    "version": "2012-09-25",
    "jobId": "1421240053904-6ozpim",
    "pipelineId": "1419424647588-9osc91",
    "input": {
        "key": "uploads/0ad92625/ffb5d317/cef636ae/20150113101221.video.mp4"
    },
    "outputKeyPrefix": "video/uploads/0ad92625/ffb5d317/cef636ae",
    "outputs": [{
        "id": "1",
        "presetId": "1419429926952-dmb9yx",
        "key": "9946d18c.mp4",
        "thumbnailPattern": "9946d18c-{count}",
        "status": "Complete",
        "duration": 4,
        "width": 480,
        "height": 480
    }],
    "userMetadata": {
        "replyId": "5"
    }
}

Error

{
  "Type" : "Notification",
  "MessageId" : "6bc8acdb-8b39-5304-a8c7-e1ebec43d0ae",
  "TopicArn" : "arn:aws:sns:eu-west-1:778005704149:ddnl-transcoding-dev",
  "Subject" : "The Amazon Elastic Transcoder job 1421239606502-mfglq3 has failed.",
  "Message" : "{\n  \"state\" : \"ERROR\",\n  \"errorCode\" : 3002,\n  \"messageDetails\" : \"3002 2f870169-d5b7-4d5a-b042-a79498c8b428: The specified object could not be saved in the specified bucket because an object by that name already exists: bucket=ddnl-transcoding, key=video/uploads/0ad92625/ffb5d317/cef636ae9946d18c.mp4.\",\n  \"version\" : \"2012-09-25\",\n \"jobId\" : \"1421239606502-mfglq3\",\n  \"pipelineId\" : \"1419424647588-9osc91\",\n  \"input\" : {\n    \"key\" : \"uploads/0ad92625/ffb5d317/cef636ae/20150113101221.video.mp4\"\n  },\n  \"outputKeyPrefix\" : \"video/uploads/0ad92625/ffb5d317/cef636ae\",\n  \"outputs\" : [ {\n    \"id\" : \"1\",\n    \"presetId\" : \"1419429926952-dmb9yx\",\n    \"key\" : \"9946d18c.mp4\",\n    \"thumbnailPattern\" : \"9946d18c-{count}\",\n    \"status\" : \"Error\",\n    \"statusDetail\" : \"3002 2f870169-d5b7-4d5a-b042-a79498c8b428: The specified object could not be saved in the specified bucket because an object by that name already exists: bucket=ddnl-transcoding, key=video/uploads/0ad92625/ffb5d317/cef636ae9946d18c.mp4.\",\n    \"errorCode\" : 3002\n  } ],\n  \"userMetadata\" : {\n    \"todReplyId\" : \"5\"\n  }\n}",
  "Timestamp" : "2015-01-14T12:46:50.326Z",
  "SignatureVersion" : "1",
  "Signature" : "xxx",
  "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-xxx.pem",
  "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=xxx"
}

Message JSON

{
    "state": "ERROR",
    "errorCode": 3002,
    "messageDetails": "3002 2f870169-d5b7-4d5a-b042-a79498c8b428: The specified object could not be saved in the specified bucket because an object by that name already exists: bucket=ddnl-transcoding, key=video/uploads/0ad92625/ffb5d317/cef636ae9946d18c.mp4.",
    "version": "2012-09-25",
    "jobId": "1421239606502-mfglq3",
    "pipelineId": "1419424647588-9osc91",
    "input": {
        "key": "uploads/0ad92625/ffb5d317/cef636ae/20150113101221.video.mp4"
    },
    "outputKeyPrefix": "video/uploads/0ad92625/ffb5d317/cef636ae",
    "outputs": [{
        "id": "1",
        "presetId": "1419429926952-dmb9yx",
        "key": "9946d18c.mp4",
        "thumbnailPattern": "9946d18c-{count}",
        "status": "Error",
        "statusDetail": "3002 2f870169-d5b7-4d5a-b042-a79498c8b428: The specified object could not be saved in the specified bucket because an object by that name already exists: bucket=ddnl-transcoding, key=video/uploads/0ad92625/ffb5d317/cef636ae9946d18c.mp4.",
        "errorCode": 3002
    }],
    "userMetadata": {
        "replyId": "5"
    }
}

Leave a Comment

Simple javscript development using http-server

When developing small javascript projects I find the overhead of configuring a
virtual host in Apache or nginx cumbersome. The http-server module in node.js allows
you to easily start a webserver within your project folder.

Make sure node.js is installed and install the following module:

npm install http-server -g

The -g option install the module as a global one, meaning that you can start the
webserver from every folder. You can now go to any folder you’d like and issue the
following command:

http-server

This will result in the following output:

Starting up http-server, serving ./ on: http://0.0.0.0:8080
Hit CTRL-C to stop the server

You can now access all files in the folder via http://127.0.0.1/ in your webbrowser.

More information on this module can be found on the http-server project page. There
are several configuration options for configuration the port, ip address, etc.

Leave a Comment

Mysql remove on update current timestamp property

The MySQL management tool I use automatically creates tables with column of the time ‘timestamp’ with the property
“ON UPDATE CURRENT_TIMESTAMP”. This property means that when a record is updated this column is automatically updated
to the current time. This behavior can be unwanted. You can check whether a column has this property by issuing a ‘desc’ command:

DESC `tag`

Which in this example will result in:

Field   Type    Null    Key Default Extra
id  int(10) unsigned    NO  PRI NULL    auto_increment
label   varchar(25) NO      NULL    
key varchar(25) NO      NULL    
specificDate    date    YES     NULL    ON UPDATE CURRENT_TIMESTAMP 

The solution to this problem is to redefine the column manually without specifying this special property. It is possible to specify
a different default value than ‘CURRENT_TIMESTAMP’.

ALTER TABLE `tag`
    CHANGE `specificDate` `specificDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP;

Based on stackoverflow.

Leave a Comment