Monday, 28 November 2011

Setting up KVM on Ubuntu 10.04 (Lucid Lynx)

After doing a KVM install on Debian Squeeze and trying to get a VM up and running, the hassle convinced me to go back to Ubuntu and their vm-builder package, which allow ones to create VMs relatively easy once the setup is complete. There is a vm-builder port for Debian, though that only works for building older versions of Ubuntu and I want to run the latest, Ubuntu 11.10 (Oneiric Ocelot).

Starting with a bare-metal Ubuntu 10.04 LTS (Lucid Lynx) 64-bit, below is the list of commands and instructions to install and set up the KVM. Details on these instructions can be read in the Ubuntu community documentation, KVM Installation and KVM Networking:

  • sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils

  • virsh -c qemu:///system list (To verify installation, should have no errors)

  • sudo apt-get install libcap2-bin

  • sudo setcap cap_net_admin=ei /usr/bin/qemu-system-x86_64

  • sudo vi /etc/network/interfaces
    • Original file:
      # The loopback network interface
      auto lo
      iface lo inet loopback

      # The primary network interface
      auto eth0
      iface eth0 inet static
      address 10.10.3.140
      broadcast 10.10.3.143
      netmask 255.255.255.248
      gateway 10.10.3.137

      # default route to access subnet
      up route add -net 10.10.3.136 netmask 255.255.255.248 gw 10.10.3.137 eth0

    • Modified file:
      # The loopback network interface
      auto lo
      iface lo inet loopback

      # device: eth0
      auto eth0
      iface eth0 inet manual

      # The primary network interface
      auto br0
      iface br0 inet static
      address 10.10.3.140
      broadcast 10.10.3.143
      netmask 255.255.255.248
      gateway 10.10.3.137
      bridge_ports eth0
      bridge_stp off
      bridge_fd 9
      bridge_hello 2
      bridge_maxage 12


      # default route to access subnet
      up route add -net 10.10.3.136 netmask 255.255.255.248 gw 10.10.3.137 eth0
      up route add -net 10.10.3.136 netmask 255.255.255.248 gw 10.10.3.137 br0

  • sudo /etc/init.d/networking restart

  • Running ifconfig lists the following interfaces br0, eth0, lo, virbr0

This completes the KVM installation and creation of a bridge for the VMs. Up next is replacement of the vm-builder. The one in the Ubuntu packages is faulty and also will not allow you to install Ubuntu 11.10 (Oneiric Ocelot). So I updated to the latest, downloading the source, building and installing it. The steps below can be found in this accepted answer:

  • sudo apt-get install bzr

  • sudo apt-get install epydoc (big install here, ~400mb)

  • bzr branch lp:ubuntu/vm-builder ubzr-vm-builder

  • cd ubzr-vm-builder

  • fakeroot debian/rules binary

  • sudo dpkg -i ../*vm-builder*.deb


With that, everything is installed and vm-builder is ready to run. The easiest way is to use a script so that vm creation can be set once and repeated as desired. The only changes required being hostname, ip and maybe memory. Obtain the Ubuntu 11.10 64-bit server iso and put it in the same place as the script. The directory I used is ~/vm/basekvm:

  • cd ~/vm/basekvm

  • sudo vi create_vm.sh
    • File:
      #!/bin/bash

      # Configure this before running the command
      HOSTNAME=myhostname
      MEMORY=2048
      IP=192.168.122.10
      # -- End of configuration

      vmbuilder kvm ubuntu \
      --destdir=/var/lib/libvirt/images/$HOSTNAME \
      --ip=$IP \
      --hostname=$HOSTNAME \
      --mem=$MEMORY \
      --suite=oneiric \
      --flavour=virtual \
      --arch=amd64 \
      --iso=/root/vm/basekvm/ubuntu-11.10-server-amd64.iso \
      --mirror=http://de.archive.ubuntu.com/ubuntu \
      --libvirt=qemu:///system \
      --domain=localdomain \
      --part=/root/vm/basekvm/vmbuilder.partition \
      --bridge=virbr0 \
      --gw=192.168.122.1 \
      --mask=255.255.255.0 \
      --user=myusername \
      --name=myname \
      --pass=mypassword \
      --tmpfs=- \
      --addpkg=vim-nox \
      --addpkg=acpid \
      --addpkg=unattended-upgrades \
      --addpkg=openssh-server \
      --firstboot=/root/vm/basekvm/fboot.sh \
      -o

  • sudo chmod 700 create_vm.sh

  • sudo vi fboot.sh (Optional)
    • File:
      # This script will run the first time the virtual machine boots
      # It is ran as root.

      # Expire the user account
      passwd -e administrator

      # Install openssh-server
      apt-get update
      apt-get install -qqy --force-yes openssh-server

  • sudo chmod 777 fboot.sh

  • sudo vi vmbuilder.partition
    • File:
      root 8000
      swap 4000
      ---
      /var 8000

  • cd ~/vm

  • ln -s /var/lib/libvirt/images/ images


The create_vm.sh is basically a template script. You can modify it to accept console input so that you don't need to go and edit the file values, that is left for another time. The symbolic link shows the directory where the VM disk images are located once created. Below is how you would use it to create a VM:

  • sudo cp basekvm/create_vm.sh create_vm_myvmname.sh

  • sudo vi create_vm_myvmname.sh. Edit the HOSTNAME, IP and MEMORY as desired

  • sudo ./create_vm_myvmname.sh

  • virsh start myvmname


And that's it! A VM has been successfully created and started up. Give it a few minutes and then you can log in through ssh using the information in the script. If the ssh is slow to connect, try this.

Friday, 25 November 2011

KVM host with gateway guest using port-forwarding

Using the 3 rules listed here and below, a KVM host can forward all http and ssh traffic to a specified gateway guest VM:

iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.1:80
iptables -t nat -I PREROUTING -p tcp --dport 22 -j DNAT --to-destination 10.0.0.2:22
iptables -I FORWARD -m state -d 10.0.0.0/24 --state NEW,RELATED,ESTABLISHED -j ACCEPT

To make it permanent, requires one to go through this page and use the following commands:

sudo sh -c "iptables-save -c > /etc/iptables.rules" (after applying the 3 commands above)
sudo vi /etc/network/if-pre-up.d/iptablesload

The /etc/network/if-pre-up.d/iptablesload file will have the following text:

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0

The KVM host will now have VM creation as its sole focus. Ensure the host's ssh port have been changed to make it accessible from outside, otherwise it can be accessed from the gateway guest.

All redirection and VM access are transferred to the the gateway guest. The guest will need to install nginx so it can act as a http proxy for other VMs. All ssh access use the gateway guest as a stepping stone to the other VMs.

Following the 3 rules, it is found the traffic essentially loops back to the gateway guest. This makes it incapable of reaching the other VMs. Applying 1 more rule after the 3 above solves this. The rule accepts all packets from the VM ip range and does not do any forwarding:

iptables -t nat -I PREROUTING -p tcp --source 10.0.0.0/24 -j ACCEPT

Monday, 7 February 2011

A Certain Minimal QR Scanner iPhone app

A QR Code is similar to a barcode, except it contains more information and looks like a pixellated square Rorschach test.

There are a number of free QR readers in the appstore like NeoReader and RedLaser. I especially like the scanning GUI for RedLaser, which seems more streamlined than any of the apps I have tried.

A number of these apps use the open-source ZXing ("Zebra Crossing") scanner library. The library is in Java but has a number of ports which include iPhone.

To start things off, you need to download or checkout the source. Create a new "View-Based Application" project in Xcode. Inside the iphone folder of the source, follow the README on how to include the ZXingWidget project into yours.

Pay attention to the instructions, especially the direct dependency, header search path and the fact the file with the ZXing has to be a .mm instead of .m. If it does not build, you're probably missing something. You can look at the sample projects to see how they include the widget.

One last thing before moving on to the code, put the beep-beep.aiff file from the ScanTest project into your project. This is to get audio confirmation of a scan.

Inside the sole viewController of your project:

#import "ZXingWidgetController.h"
#import "QRCodeReader.h"
#import "ResultParser.h"
#import "URLResultParser.h"
#import "ResultAction.h"

- (void)viewDidLoad {
[super viewDidLoad];
[ResultParser registerResultParserClass:[URLResultParser class]];
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
ZXingWidgetController *widController =
[[ZXingWidgetController alloc] initWithDelegate:self showCancel:NO OneDMode:NO];
QRCodeReader *qrcodeReader = [[QRCodeReader alloc] init];
NSSet *readers = [[NSSet alloc] initWithObjects:qrcodeReader,nil];
[qrcodeReader release];
widController.readers = readers;
[readers release];
NSBundle *mainBundle = [NSBundle mainBundle];
widController.soundToPlay =
[NSURL fileURLWithPath:[mainBundle pathForResource:@"beep-beep" ofType:@"aiff"] isDirectory:NO];
[self presentModalViewController:widController animated:YES];
[widController release];
}

#pragma mark -
#pragma mark ZXingDelegateMethods
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)resultString {
[self dismissModalViewControllerAnimated:YES];
ParsedResult *parsedResult = [[ResultParser parsedResultForString:resultString] retain];
NSArray *actions = [[parsedResult actions] retain];

if ([actions count] == 1) {
ResultAction *theAction = [actions objectAtIndex:0];
[theAction performActionWithController:self shouldConfirm:YES];
} else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Text Found:"
message:resultString
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
}
}

The code in viewWillAppear is lifted off from the sample projects. This sets up the scanning video camera with the appropriate reader. If a scan is successful, the delegate method didScanResult will execute. The result is parsed to see if it is a URL. You set which parser to use in viewDidLoad. A parsed result can have default actions associated with it, the URLResultParser opens up the url in Safari as default. Otherwise the result is treated as text and displayed.

This app can now scan QR codes and open up URLs in Safari. There are a number of other things you can add to this, eg you can switch out the ResultParser with a UniversalResultParser that includes all the parser classes. You should take a look in the Classes folder of the ZXingWidget project to see what is available.

Wednesday, 7 April 2010

Setting up mail for your Ubuntu server

Need your bare-bones Ubuntu server to send out email notification and the like? Well, first you need to check what your hostname is and modify accordingly:

hostname -f

So my server's hostname is microdude, you might also get it as microdude.localdomain. You'll want to convert it into an FQDN (Fully Qualified Domain Name). That means I need to change my microdude to microdude.favoritemedium.com.

sudo vi /etc/hostname
sudo vi /etc/hosts


The output of hosts:
127.0.0.1 localhost
127.0.1.1 microdude.localdomain microdude


Would now be:
127.0.0.1 localhost
127.0.1.1 microdude.favoritemedium.com microdude


Reboot and check that the hostname has been changed to microdude.favoritemedium.com:

sudo reboot
hostname -f


After that, you're supposed to set up RDNS (Reverse Domain Name System), this helps avoid your mails falling prey to the all-powerful spam filter. You can check on this RDNS with dig which can be installed from this package:

sudo apt-get install dnsutils

This is optional, though if your mails start going into the spam folder, you'd best revisit this in the links provided.

Now to install the actual mail agent onto the server with a one-liner operation:

sudo tasksel install mail-server

The process stops at two points for user input. The first is to pick the postfix configuration for the server, select the default Internet site. The second is to set the main domain name, this should be already be filled with microdude.favoritemedium.com and all you need to do is accept.

Once finished, the postfix daemon should be running now. You need to know about two files, the main configuration file and the mail log. You don't need to mess around with the config file, since that should be set up correctly. The mail log is useful to check on whether mails have been sent or other information. Their locations:

/etc/postfix/main.cf
/var/log/mail.log


Now you'll want to test whether mails can be sent out. An email can be typed out in the terminal using the mail command:

mail testing@mailaddress.com
Subject: Do not panic, this is a test
Panic panic panic panic panic
.
Cc:


Check the email account you sent the mail to and you should see that it is present.

Congratulations, you can now spam to your heart's content =^_^=.

From:

Tuesday, 6 April 2010

Moving Drupal from one host to another

You've got a Drupal site running and now want to move it to another server which is bare-bones. This server is luckily running Ubuntu, which makes installing the rest of the LAMP (Linux, Apache, MySQL and PHP) stack a one-line operation:

sudo tasksel install lamp-server

The install process will stop at one point to ask for the password of the root user for MySQL. Once the install completes, use the MySQL command-line client to add in the same database (an empty one) and user that Drupal uses.

Use mysqldump to dump out the Drupal database data into an sql file. Load it in the new database, populating it. You'll want to tar up the whole directory where the Drupal files reside (not forgetting the .htaccess) and expand it out in the same location within the new machine. You may (not) need to edit sites/default/settings.php or .htaccess for any host information changes. settings.php is also where you edit the database login information, if you decide to use different ones.

With Apache running, you should be able to hit the new Drupal site. It looks to be running but there are a few gotchas. If you are (most likely) running Clean URLs, the links don't work anymore. You'll need to disable it to get navigation working at http://hostname/?q=admin/settings/clean-urls, maybe going through http://hostname/?q=user to log in first. To set up Clean URLs again, you need to run this line:

sudo a2enmod rewrite

And edit /etc/apache2/sites-available/default, changing the AllowOverride None to AllowOverride All inside the directory with the path /var/www, or where the Drupal files live. Restart the server and you find that you can enable Clean URLs now.

The other gotcha is that the status report is complaining it can't find the PHP GD library. Apparently PHP library is installed but not configured so you run these lines:

sudo apt-get install php5 (optional?)
sudo apt-get install php5-gd

Lastly, cron needs to be set up to update the Drupal site. I use curl so the lines are:

sudo apt-get install curl
crontab -e
0 * * * * curl --silent --compressed http://localhost/cron.php

The last line is done inside the vi editor so after that, save and quit.

Congratulations! Your Drupal site is now running fine on the new machine.

Tuesday, 30 March 2010

Google Analytics not showing Firefox and Internet Explorer hits, only Safari and Chrome

If you find your Google Analytics is not showing any results for Firefox and Internet Explorer and everything is correct with no problems on the Javascript side, you might want to take a closer look at the GA code. It might be that all the code is in one script block like so:


<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape('%3Cscript src="'+gaJsHost+'google-analytics.com/ga.js" type="text/javascript"%3E%3C/script%3E'));

try {
var pageTracker = _gat._getTracker('UA-XXXXXXX-X');
pageTracker._trackPageview();
}
catch (err) {}
</script>


The code that GA gives you to use is no mistake, you need to separate them out into two blocks:


<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape('%3Cscript src="'+gaJsHost+'google-analytics.com/ga.js" type="text/javascript"%3E%3C/script%3E'));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker('UA-XXXXXXX-X');
pageTracker._trackPageview();
}
catch (err) {}
</script>


The first code block is responsible for downloading the ga.js. That file needs to be loaded up before the second code block can execute properly. If they are in the same block, the try code will fail and no exception or alert is given.

Below are three separate posts covering the same question:


The last comment here lets you know what to look for in troubleshooting Google Analytics. The GA cookies are named __utma, __utmb, __utmc, __utmz, and __utmv and if you do not have any of them, GA is not working.

Tuesday, 4 August 2009

Groovy Tutorial for MongoDB

Java Tutorial (http://www.mongodb.org/display/DOCS/Java+Tutorial)

Since Groovy is based on Java, you can make use of the Java driver for MongoDB as well as go through the tutorial shown above. The code can be used as-is or modified to look more groovy.

Create a groovy file (mongo.groovy) and place the Java driver in the same directory. Edit it so the code looks like:

this.class.classLoader.rootLoader.addURL(new File("mongo-0.6.jar").toURL())

import com.mongodb.*

DBAddress address = new DBAddress("localhost", "mydb")
Mongo db = new Mongo(address)
db.authenticate('testUser', 'testPassword')

DBCollection coll = db.getCollection("testCollection")

BasicDBObject doc = new BasicDBObject()
doc.put("name", "MongoDB")
doc.put("type", "database")
doc.put("count", 1)

BasicDBObject info = new BasicDBObject()
info.put("x", 203)
info.put("y", 102)
doc.put("info", info)

coll.insert(doc)

for(i in 1..100) {
coll.insert(new BasicDBObject().append("i", i))
}

println coll.getCount()

DBCursor cursor = coll.find()
while(cursor.hasNext()) {
println cursor.next()
}

You can see it's just a rehash of part of the Java tutorial, the output will be a count of the number of inserted objects followed by the objects themselves.

The first line of code loads the jar file, this is useful if you're not looking to put the jar in a classpath or one of groovy's configured library paths like '~/.groovy/lib'.

Running the script is accomplished by 'groovy mongo.groovy' in the terminal. You can also type in 'groovyConsole' to load the console then opening the script and running inside it.

Installing MongoDB on Ubuntu 9.04 Jaunty Jackalope

For the installation, I decided to build MongoDB (http://www.mongodb.org/) from source. The documentation regarding this is thorough and getting it running was a cinch. A Javascript engine called Spider Monkey is used and you need to build it with UTF8 support.

The documents referred to:
http://www.mongodb.org/display/DOCS/Building
http://www.mongodb.org/display/DOCS/Building+Spider+Monkey
http://www.mongodb.org/display/DOCS/Building+for+Linux

The all-in-one command line version:
Dependencies:
sudo apt-get install curl tcsh git-core scons g++
sudo apt-get install libpcre++-dev libboost-dev libmozjs-dev

Spider Monkey installation:
curl -O ftp://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz
tar zxvf js-1.7.0.tar.gz
cd js/src
export CFLAGS="-DJS_C_STRINGS_ARE_UTF8"
make -f Makefile.ref
sudo JS_DIST=/usr make -f Makefile.ref export

MongoDB installation:
git clone git://github.com/mongodb/mongo.git
scons all
sudo scons --prefix=/opt/mongo install

Finishing up:
add /opt/mongo/bin to your path
create a /data/db directory structure and chown it to your user or add read-write permission.
http://www.howtogeek.com/howto/ubuntu/how-to-add-a-program-to-the-ubuntu-startup-list-after-login/
System -> Preferences -> Startup Applications -> Startup Programs
click add, click browse and select /opt/mongo/bin/mongod
add run to the end of the line so that it reads '/opt/mongo/bin/mongod run'
enter name and comment then save

And you're done. mongodb will start up with no authentication as default at localhost:27017, the web information interface is at localhost:28017. You can use mongo to enter the interactive shell, similar to mysql's shell client.

Some links to get you started:
http://www.mongodb.org/display/DOCS/Overview+-+The+MongoDB+Interactive+Shell
http://www.mongodb.org/display/DOCS/File+Based+Configuration
http://www.mongodb.org/display/DOCS/Command+Line+Parameters
http://www.mongodb.org/display/DOCS/Security+and+Authentication
http://www.mongodb.org/display/DOCS/Manual

Tuesday, 17 March 2009

Delegation for iphone-google-maps-component

The iphone-google-maps-component is a way for you to use Google Maps on the iPhone through the use of an extended UIWebView and an html file on a server somewhere that does the map loading.

The present code overrides the default delegate UIWebViewDelegate with its own, MapWebViewDelegate. You cannot use the setCenterWithLatLng to establish the starting location until the view has finished loading. With the override, the webViewDidFinishLoad delegate method is unavailable.

Going by this post, the following code changes will allow the use of both delegates.

MapWebView.h
1) Change protocol definition.
@protocol MapWebViewDelegate < UIWebViewDelegate >

MapWebView.m
2) Remove the synthesize line.
//@synthesize delegate;

3) Replace with own methods, to hook into super.delegate.

- (id ) delegate {
return (id )super.delegate;
}

- (void) setDelegate :(id ) delegate {
super.delegate = delegate;
}


webViewDidFinishLoad should be accessible, as well as the other methods in the UIWebViewDelegate.

Another way of setting the starting location is through the loadMap method in MapWebView.h. The URL string can accept two more variables, latitude and longitude.

Change this:
NSString *urlStr = [NSString stringWithFormat:
@"http://www.wenear.com/iphone-test?width=%d&height=%d&zoom=%d",
width, height, DEFAULT_ZOOM_LEVEL];


To this:

NSString *urlStr = [NSString stringWithFormat:
@"http://www.wenear.com/iphone-test?width=%d&height=%d&latitude=%f&longitude=%f&zoom=%d",
width, height, location.coordinate.latitude, location.coordinate.longitude, DEFAULT_ZOOM_LEVEL];


Here, location is a CLLocation, part of the CoreLocation framework. The loadMap method is publicly defined in the interface and it accepts location as its variable. The call in didMoveToSuperview should be properly handled.

The latitude and longitude are doubles and can be hardcoded instead.

Monday, 9 February 2009

XML and the Copyright symbol, ©

For anyone parsing an XMl file and wondering what in the world is an org.xml.sax.SAXParseException: Invalid byte 1 of 1-byte UTF-8 sequence error message, check to see whether the XML file has any special characters like the copyright symbol, ©. The default encoding of UTF-8 will choke on these.

Solved my woes by changing the encoding to <?xml version="1.0" encoding="iso-8859-1"?> to get the character to display correctly. You can also filter it out or replace the symbol with its character reference.

Monday, 5 January 2009

Google Friend Connect & Facebook Connect

Google Friend Connect

A Google, Yahoo, AIM or OpenID account can be used for authentication.

You start it off with logging into your Google account and going to http://www.google.com/friendconnect/.
  • From there, click on 'Set up a new site' and enter your site name and url.
  • You'll be given two files, canvas.html and rpc_relay.html, to copy onto your web site.
  • The last step is testing for those two files.
After that, it's a matter of choosing the gadgets you wish to add, customizing it and then copying the code generated into one of your site pages. The code is normal html and javascript.

From the current gadgets available, you can add on to your site to make it more 'sociable', like knowing the site members, adding in a chat box and reviewing/rating stuff.

You can create your own custom gadgets from these sites:
http://code.google.com/apis/gadgets/
http://code.google.com/apis/opensocial/

Facebook Connect

Only a Facebook account can be used for authentication.

Login into your Facebook account and follow the instructions located here:
http://wiki.developers.facebook.com/index.php/Trying_Out_Facebook_Connect

The main page for developers for Facebook Connect is http://developers.facebook.com/connect.php.

Facebook uses it's own Facebook Markup Language (FBML), in contrast to normal HTML, to facilitate creation of all Facebook applications. Facebook Connect is therefore embedded into user pages using FBML.

Facebook Connect is more of an expansion of Facebook onto other sites, depending on the application, it allows for data on a user site to be displayed back in Facebook.

HOW TO: Add Facebook Connect to Your Blog in 8 Minutes

Tuesday, 2 December 2008

Clickpass plugin for Grails, Part 2

Go to Clickpass and create an account. Once in the account, go to the "My Details" tab and check the "Show software developer tools" under "Additional Preferences". There should now be a "developer" tab present, select it and push the "Create new site" button.

1. Register site
  • Type in the site name.
  • URL: http://localhost:8080/makedo. This is for running a grails app on your own machine named makedo.
  • Description and logo are optional.

2. Log users in
  • OpenID trust root: http://localhost:8080.
  • begin_openid_login: http://localhost:8080/makedo/openid/login?success_controller=
    clickpass&success_action=welcome&error_controller=&error_action=
    . This link is where Clickpass will access, providing the OpenID details, after one has passed the bar that should be in the app.
  • Leave the OpenID parameter label alone.
  • Submission method: POST.
  • SSL is optional.
  • Once you have saved the changes, the page will change and display a choice of clickpass bars and the html code for it down below. Copy the html code into _clickpass.gsp.

3. Merge accounts
  • begin_add_openid_to_user URL: http://localhost:8080/makedo/clickpass/merge. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. If the user selects to login a pre-existing account, the username and password he provides will be sent to this link. If successful the OpenID will be associated with that account.
  • user_id label: Username. This is for the login page Clickpass will provide before accessing the URL above. Merely cosmetic.
  • Adding the connect bar is optional.

4. Register new signups
  • process_openid_registration_url: http://localhost:8080/makedo/clickpass/register. This is after the authentication and the check whether the OpenID belongs to any user on the database, bringing him to the registration form. The user provides information to Clickpass which will be passed along to the link. If successful a new user account with the associated OpenID will be created and saved in your app.
  • new_openid_completion_url: http://localhost:8080/makedo/clickpass/welcome. This is for when authentication is successful and all other things have been taken cared of, e.g. registration or merging.
  • The others are optional except for the fields. Changes in the fields may require changes in the register method of the Clickpass controller.
  • The URL for the registration form should be left for last as it changes due to the fields. Once you're satified and have saved the changes, you can copy and paste the string into urlString in the welcome method of the Clickpass controller.
Aftermath
Once the settings are done, you can use Clickpass.

Clickpass plugin for Grails, Part 1

Clickpass works by handling the OpenID authentication and sending it back to the site. This means that any Grails app using this plugin must also install the OpenID plugin. Also the site will need to be registered with Clickpass and some settings need to be set online. When the authenticated details are sent back, the user is logged in and sent to the welcome page, sent to a form for details to complete registration or asked to login to attach the OpenID to an existing user account.
  • grails create-plugin clickpass.
  • Add ClickpassController.groovy to grails-app/controller.
  • Add User.groovy to grails-app/domain. This is a dummy file required for the plugin to be packaged.
  • Add ClickpassTagLib.groovy to grails-app/taglib.
  • Add _clickpass.gsp to grails-app/views.
  • Add welcome.gsp to grails-app/views/clickpass

ClickpassController.groovy
class ClickpassController {
def openidService

def index = { response.sendError(404) }

def welcome = {
def user = null

if (openidService.isLoggedIn(session)) {
user = User.findByOpenid(openidService.getIdentifier(session))
if (user == null) {
def urlString = "http://www.clickpass.com/process_new_openid?
site_key=Xr2zelexGn&process_openid_registration_url=
http%3A%2F%2Flocalhost%3A8080%2Fsocialr%2Fclickpass%2F
register&new_openid_completion_url=http%3A%2F%2F
localhost%3A8080%2Fsocialr%2Fclickpass%2Fwelcome&
requested_fields=nickname%2Cemail&required_fields=&
nickname_label=Nickname&email_label=Email"
redirect(url:urlString)
}
}
}

def merge = {
def user = User.findByUsernameAndPassword(params.user_id, params.password)
def urlredirect =
java.net.URLDecoder.decode(params.clickpass_merge_callback_url) +
"&userid_authenticated=false"

if (user == null) {
redirect(url:urlredirect)
}

user.openid = openidService.getIdentifier(session)

if (user.save()) {
redirect(controller:'clickpass',action:'welcome')
}
else {
redirect(url:urlredirect)
}
}

def register = {
def user = new User()
user.username = params.nickname
user.email = params.email
user.openid = openidService.getIdentifier(session)
user.password = ''

if (user.save()) {
flash.message = "Thank you for signing up."
redirect(controller:'clickpass', action:'welcome')
}
else {
flash.message = "There is a problem signing up."
redirect(controller:'clickpass', action:'index')
}
}
}
  • index: Nothing for the index to do so set up a 404.
  • welcome: When a user is authenticated by OpenID, a search is made to see whether that OpenID is in the database, if not then redirect to the registration page. The urlString is obtained from Clickpass so this needs to be changed for each site
  • merge: A user, authenticated by OpenID, who wants to attach the ID to his account.
  • register: Create a new user account with the OpenID and save it.

User.groovy
Nothing in it except for the class declaration.

ClickpassTagLib.groovy
class ClickpassTagLib {
static namespace = "clickpass"

def bar = { attrs, body ->
try {
out << render(template:"/clickpass", contextPath:"${pluginContextPath}")
}
catch(Exception e) {
log.error(e)
}
}
}
  • The namespace allows one to use it, i.e <clickpass:foobar/>, in the code instead of the generic <g:foobar/>
  • For usage in an app, all that is required is <clickpass:bar/> to be inserted into the html.
  • The / in the template name is necessary to revert the path to the project base, otherwise it would go looking in the main app views folder.
  • The contextPath:"${pluginContextPath}" is not mentioned in the documention but after setting the path to the base, this sets it to the correct plugin views folder.

_clickpass.gsp
The html for this is generated by Clickpass, after registering your site, you can just copy and paste. This creates the Clickpass login bar.

welcome.gsp
<html>
<head>
<link rel="openid.server" href="http://www.clickpass.com/openid_server" />
<link rel="openid.delegate" href="http://clickpass.com/public/iseec" />
<title>Welcome</title>
</head>
<body>
<h1>Welcome</h1>
<openid:identifier />
<openid:ifLoggedIn>
<openid:logoutLink>Logout</openid:logoutLink>
</openid:ifLoggedIn>
</body>
</html>
  • This is a simple welcome page after the user successfully logs in and his account is verified.
  • One can replace it with their own or redirect from this page.
  • The OpenID is displayed as well as a logout link.

Aftermath
  • grails package-plugin to get it all into a zip file.
  • grails install-plugin /path/to/plugin/grails-example-0.1.zip to install it into an app.

Friday, 24 October 2008

IPhone MoviePlayer Sample Code 4

MyOverlayView

Interface

Instance methods:
  • -(void)awakeFromNib;
  • -(void)dealloc;
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

Imports:
  • #import "MyViewController.h"
-(void)awakeFromNib
Rotate the overlay 90 degrees clockwise then translate it to the centre of the screen. This will match the orientation the movie player uses, full-screen landscape mode.

-(void)dealloc
Call super dealloc.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Similar to the MyImageView implementation. This creates a OverlayViewTouchNotification and fires it off.

IPhone MoviePlayer Sample Code 3

MyImageView

Interface

Imports:
  • #import "MyViewController.h"
Instance variables:
  • mViewController
Instance methods:
  • -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;

Implementation

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
Receives a set with the touches and the event. Gets a UITouch from the set and check it's phase. If it's UITouchPhaseBegan, play the movie.

Thursday, 23 October 2008

IPhone MoviePlayer Sample Code 2

MyViewController

Interface

Imports:
  • #import < MediaPlayer/MediaPlayer.h >
  • #import "MyOverlayView.h"
The line 'extern NSString * const OverlayViewTouchNotification' creates a global constant that will be used for touches to the overlay view. Any class importing MyViewController can access this variable.

Instance variables:
  • mMoviePlayer (MPMoviePlayerController)
  • mMovieURL (NSURL)
  • mOverlayView (MyOverlayView)
Instance methods:
  • -(NSURL *)movieURL;
  • -(void)initMoviePlayer;
  • -(IBAction)playMovie:(id)sender;
  • -(void)overlayViewTouches:(NSNotification *)notification;
  • -(IBAction)overlayViewButtonPress:(id)sender;

Implementation

Imports:
  • #import "MoviePlayerAppDelegate.h"
Initialise constant OverlayViewTouchNotification with the string 'overlayViewTouch'.

#pragma is a compiler directive. However the compiler ignores it if #pragma mark is used. #pragma mark helps with the organisation of your methods in the Xcode dropdown list.

'#pragma mark -' divvies up the methods by creating a spacer line while '#pragma mark Movie Player Routines' creates a bold label with the text 'Movie Player Routines'.

Movie Player Routines
- (void) moviePreloadDidFinish:(NSNotification*)notification
- (void) moviePlayBackDidFinish:(NSNotification*)notification
- (void) movieScalingModeDidChange:(NSNotification*)notification

These are events. When they are triggered is evident from the names. There is no code inside them.

-(void)initMoviePlayer
  • Register MPMoviePlayerContentPreloadDidFinishNotification (named after the definition in MPMoviePlayerController.h) with NSNotificationCenter.
  • The movie player object, mMoviePlayer, is created by allocating MPMoviePlayerController and initialising it with movieURL.
  • Register MPMoviePlayerPlaybackDidFinishNotification and MPMoviePlayerScalingModeDidChangeNotification, giving mMoviePlayer as the notification sender.
  • Obtain the MoviePlayerAppDelegate object and use its instance variables to set mMoviePlayer.
  • Register OverlayViewTouchNotification.
-(IBAction)playMovie:(id)sender
  • Play mMoviePlayer.
  • Get the application windows in an array.
  • Get the movie player window.
  • Add mOverlayView as the subview of the movie player window.
View Controller Routines
-(NSURL *)movieURL
  • Check mMovieURL whether it's null or not.
  • Passing will return mMovieURL.
  • Failing will get the main bundle, look for 'Movie.m4v', check it and convert the path to NSURL before assigning it to movieURL.
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation
-(void)didReceiveMemoryWarning
-(void)overlayViewTouches:(NSNotification *)notification
-(IBAction)overlayViewButtonPress:(id)sender

Trivial or empty methods.

-(void)dealloc
Remove all registered notifications and release the movie player.

IPhone MoviePlayer Sample Code

Deconstructing the MoviePlayer app to see how the Media Player Framework ticks.

MoviePlayer has 4 classes:
  • MoviePlayerAppDelegate
  • MyImageView
  • MyOverlayView
  • MyViewController
A new IPhone application automatically creates 2 classes, with project name MPC, MPCAppDelegate and MPCViewController. Therefore, MyImageView and MyOverlayView are additions while MyViewController has been renamed.

MoviePlayerAppDelegate

Interface

There are 2 different ways of including a file/class:
  • #import "MyViewController.h"
  • @class MyViewController;
The interface lists 3 instance variables of type NSInteger aside from the default UIWindow and ViewController:
  • backgroundColor
  • controlMode
  • scalingMode
The @property declares the accessor methods for the 5 variables. It has attributes which modify the accessors.

Example: @property (nonatomic, retain) UIWindow *window;

nonatomic means the accessor returns the value directly, the default is to lock, get value, unlock which is meant for multithreaded environments.

assign, retain or copy must be explicitly set if garbage collection is not used. assign is the default and means the setter uses a simple assignment. retain keeps the object variable in-memory and cannot be deallocated without your permission. copy means that a copy of the object should be used for assignment and the previous one is sent a release message.

3 instance methods:
  • -(void)setUserSettingsDefaults;
  • -(void)applicationDidFinishLaunching:(UIApplication *)application;
  • -(void)dealloc;

Implementation

3 NSString keys:
  • kScalingModeKey = @"scalingMode"
  • kControlModeKey = @"controlMode"
  • kBackgroundColorKey = @"backgroundColor"
@synthesize generates the accessors according to the attributes listed. If implemented manually, the code must follow what has been specified in the @property. @synthesize will only generate accessors that don't exist so if a variable has a setter already, then only a getter will be generated.

-(void)setUserSettingsDefaults
Sets up a test calling NSUserDefaults to see if there's a match. Passing will set the scalingMode, controlMode and backgroundColor variables. Failing will create the values based on the settings bundle info and then set the variables:
  • Get the path to the main bundle of the project.
  • Append Settings.bundle to the path using stringByAppendingPathComponent, which takes care of adding separators.
  • Append Root.plist to the path, same as before.
  • Create a dictionary using dictionaryWithContentsOfFile, giving the path to Root.plist.
  • Create the prefSpecifierArray array from the dictionary using the key, PreferenceSpecifiers, which is a tag in Root.plist.
    • Root.plist is an XML file with dict, array and key tags. May be an easy way to read XML.
  • Declare 3 NSNumber variables and a dictionary, prefItem.
  • Loop prefSpecifierArray with the current value going into prefItem.
    • Set keyValueStr with the dictionary using the key, 'Key'.
    • Set defaultValue with the dictionary using the key, 'DefaultValue'.
    • Check if keyValueStr is equal to any NSStrings specified above.
    • Set the numbers with defaultValue if any is correct.
  • Create a dictionary containing the default values and their keys.
  • Register the dictionary with NSUserDefaults using registerDefaults.
  • Updates and writes to the persistent domains using synchronize.
-(void)applicationDidFinishLaunching:(UIApplication *)application
Add the ViewController's view as a subview of the UIWindow. Call self with setUserSettingsDefaults to get the default movie player settings. Lastly, call the ViewController with initMoviePlayer to create a movie player object.

-(void)dealloc
Release the UIWindow and the ViewController. Call the super's dealloc method.

Tuesday, 21 October 2008

Objective-C Classes

Objective-C (Obj-C) is C with Object Oriented Programming tacked on. The object syntax takes some time to get used to, as it is decidedly different from the normal C++/Java syntax.

There are two parts for an object in Obj-C, which are the interface and the implementation. The parts should be stored separately, the interface in a header file (*.h) and the implementation is a *.m file.

The interface stores the instance variables and the prototypes for the class and instance methods. The implementation defines those methods.

Sample code:

*** CounterController.h ***
#import < Cocoa/Cocoa.h >
@interface CounterController : NSObject {
IBOutlet id display_update;
int count;
}
- (IBAction)minus_button_pushed:(id)sender;
- (IBAction)p10_button_pushed:(id)sender;
- (IBAction)plus_button_pushed:(id)sender;
@end

*** CounterController.m ***
#import "CounterController.h"
@implementation CounterController
- (IBAction)minus_button_pushed:(id)sender {
count--;
[display_update setIntValue:count];
}
- (IBAction)p10_button_pushed:(id)sender {
count+=10;
[display_update setIntValue:count];
}
- (IBAction)plus_button_pushed:(id)sender {
count++;
[display_update setIntValue:count];
}
@end


Things of note:
  • Obj-C uses import instead of include.
  • The class CounterController is a subclass of NSObject.
  • Inside the brackets are the instance variables display_update and count.
  • The IBOutlet before display_update means the display_update is an output on the GUI display in the Interface Builder.
  • id is a general type, meaning any object.
  • (IBAction)minus_button_pushed:(id)sender translates to IBAction minus_button_pushed(id sender).
  • The - before the methods classify them as instance methods, + for class methods.
  • The brackets [] indicate messaging, [display_update setIntValue:count] translates to display_update.setIntValue(count).


http://www.gmonline.demon.co.uk/cscene/CS7/CS7-05.html
http://en.wikipedia.org/wiki/Objective-C
http://www.otierney.net/objective-c.html

Friday, 22 August 2008

Grails - JSecurity plugin

JSecurity is a Grails plugin that adds authentication and authorization to a web application. First we go about creating a project and installing the plugin:

grails create-app project_name
grails install-plugin jsecurity

Then we use the quick-start option, available once the plugin is installed, to generate JSecurity domain classes, controller and realm.

grails quick-start

We can follow the JSecurity Quick Start guide to create a functional security application or use the files present to customize and only take what is required.

Authentication


For the login authentication, we'll need the User class, the Auth controller and the DbRealm.

  • The User class will need a username and password field. The password field must be hashed, SHA1 is the default.

  • For the Auth controller, the important methods are signIn and signOut. For signIn, the important lines are the authToken definition, the jsecSecurityManager login and the try-catch block.

  • Authentication occurs in the DbRealm. The important lines in the authenticate method are the account definition and the credentialMatch. You may customize the authToken class by changing the static line, replacing the default with your own class.

  • credentialMatch defaults to SHA1 hashing, to change it, modify grails-app/conf/spring/resources.groovy and add the line credentialMatcher(org.jsecurity.authc.credential.hashMatcher). Replace hashMatcher with the hash class of your choice.


Roles


Roles depend on the hasRole method of the realm. This method accepts a principal, usually a username, and a rolename. The output is a simple true or false boolean, making the logic inside the method completely up to the programmer. The classes generated by the quick-start are not all necessary. We can easily use a variable in the user class to determine the role, modifying the hasRole method as required, thus no additional classes are needed.

For this project, we are using the default hasRole method and keeping the Role as well as the UserRoleRel class. This, along with the User class, allows us to have users and roles, assigning the users to a role in a many-to-one relationship. Refer to the JSecurity Quick Start guide for the way to bootstrap it.

Authorization


Having users and roles, we can now set up proper access control by creating a security filter file, grails-app/conf/SecurityFilters.groovy. Filter rules cannot overlap in actions, just modify the accessControl to include the desired roles. The rules return true or false, allowing or denying access.

Security Filter Example

import org.jsecurity.SecurityUtils
// Customise the interceptor behaviour by implementing onNotAuthenticated() and onUnauthorized() methods in your filters class.
// Default behaviour remains as before (redirect to login page for an unauthenticated user, redirect to unauthorized page for unauthorized access).
class SecurityFilters {
def filters = {
// Ensure that all controllers and actions require an authenticated user,
// except for a few controllers
auth(controller: "*", action: "*") {
before = {
// Exclude these controllers.
if ((controllerName == "search" && actionName == "unboundSearch") || (controllerName == "business" && actionName == "show") ||
(controllerName == "user" && actionName == "signup")) return true

// This just means that the user must be authenticated. He does
// not need any particular role or permission.
accessControl { true }
}
}

// Roles return true or false
// Add reviews to businesses. Only Administrators or Users can do this
addReview(controller: "review", action: "create") {
before = {
accessControl {
role("Administrator") || role("User")
}
}
}

// Add pets to users
addPets(controller: "pet", action: "(create|save)") {
before = {
accessControl {
def user = User.findByUserName(SecurityUtils.subject.principal)
def allowAccess = false

if (role("Administrator")) {
allowAccess = true
}
else if (params.id.toLong() == user.id) {
allowAccess = role("Business User") || role("User")
}
return allowAccess
}
}
}
}
}

Permissions


Permissions offer finer access control, like read, update, delete. Roles can be considered as named collections of permissions, like administrator, user, superuser. Nothing was done with permissions, it was entirely a role-based affair. Refer to the documentation for usage.

Links


Wednesday, 16 July 2008

GSP training

Brought Fauzan up to speed on the project work that's been done, explaining the search controller and the gsp code.

He made a simplified version of the search page, recreating the basic functionality.

The CSS was not touched on. I figure one can just strip it from the page and into a file then figure out where to put and call the file.

Similarly the javascript, dealing with the Google map functionality, may be moved to a file and referred to. It may not work due to the gsp tags inside the code.