Monday 17 January 2011

Funny Violence BlackBerry Version - part 1

Funny Violence is a BlackBerry device application developed on Koprol API. The basic functionality is this:

  • 1. The user logs into the application with his Koprol credentials.
  • 2. He has the ability to search for a specific location or to choose one from the “Top Places Nearby” section
  • 3. The user has a range of funny weapons.
  • 4. On the selected place he sees all users that checked in.
  • 5. He can "Attack" his friends on Koprol with these weapons.
  • 6. The user can select a weapon at any time.
  • 7. After the attack, a stream is published under the specific location in the form “@attacked_user, you have just been sheep'ed by @attacker_user' at specific_location”.


One of the first things that this application needed was a library for XAuth, because the BlackBerry device cannot play the HTTP re-direct game normally played in the "3-legged" OAuth model. As there isn't a BlackBerry library for XAuth, Oauth Signpost for BlackBerry needed to be adapted.

Now, the Signpost source files, under oauth → signpost → basic there's a file called DefaultOAuthProvider.java which extends AbstractOAuthProvider. We'll talk about AbstractOAuthProvider later. In DefaultOAuthProvider.java, you'll se that the request is created and different request properties are set. Now, we'll create a new class, DefaultXAuthProvider.java, which will extend AbstractXAuthProvider (later in this post). The first thing we'll change is the constructor. Oauth uses requestTokenEndpointUrl, String accessTokenEndpointUrl and authorizationWebsiteUrl as parameters (see Oauth docs). For XAuth we need requestTokenEndPointUrl, which is bassically the url that accesses the access token directly (XAuth basically short-circuits Oauth) and the username and password used to login into the target server. So the code will look like this:

public DefaultXAuthProvider(String requestTokenEndpointUrl, String username,
            String password) {
        super(requestTokenEndpointUrl, username, password);
}

Now, let's set some request parameters. They may vary given the service provider, but in this case (Koprol), we need to set the following:

connection.setRequestProperty("Connection", "keep-alive");
connection.setRequestProperty("Content-type", "application/x-www-form-urlencoded");

Now, let's move on to oauth → signpost → signature in the Signpost source codes and open the AuthorizationHeaderSigningStrategy.java. Here, the request is signed and the parameters are added to the request header. We'll add some extra parameters to the header (according to XAuth docs): XAUTH_MODE, XAUTH_USERNAME and XAUTH_PASSWORD. I've defined these values in oauth → signpost → Oauth.java. I'll paste the modification from this file too, but first, our new request parameters:

if (requestParameters.containsKey(OAuth.XAUTH_MODE)) {
        sb.append(", ");
        sb.append(requestParameters.getAsHeaderElement(OAuth.XAUTH_MODE));
}
if (requestParameters.containsKey(OAuth.XAUTH_USERNAME)) {
        sb.append(", ");
        sb.append(requestParameters.getAsHeaderElement(OAuth.XAUTH_USERNAME));
}
if (requestParameters.containsKey(OAuth.XAUTH_PASSWORD)) {
        sb.append(", ");
        sb.append(requestParameters.getAsHeaderElement(OAuth.XAUTH_PASSWORD));
}

The previous code goes right after

sb.append(OAuth.toHeaderElement(OAuth.OAUTH_SIGNATURE, signature));

Now, the modifications from Oauth.java:

public static final String XAUTH_MODE = "x_auth_mode";
public static final String XAUTH_MODE_VALUE = "client_auth";
public static final String XAUTH_USERNAME = "x_auth_username";
public static final String XAUTH_PASSWORD = "x_auth_password";

Ok, let's move on to AbstractOauthProvider.java which we talked about at the beginning of this post. You'll find it under oauth → signpost. Here, normally, the token is requested and retrieved. Of course, for XAuth we need to make some changes, so, we'll create a new file AbstractXauthProvider.java, based on AbstractOauthProvider.java.

Here, we only need accessTokenEndpointUrl, username and password so we can get rid of requestTokenEndpointUrl and authorizationWebsiteUrl. Next, get rid of callbackUrl parameter from retrieveRequestToken method.

Next, retrieveRequestToken will change to retrieveAccessToken with consumer as parameter. Inside this method we'll call the retrieveToken method which will be modified as follows: after

if (this.listener != null) {
        this.listener.prepareSubmission(request);
}

we'll add:

HttpConnection connection = (HttpConnection)request.unwrap();
            
StringBuffer requestBody = new StringBuffer();
requestBody.append(OAuth.XAUTH_MODE + "=" + OAuth.XAUTH_MODE_VALUE + "&");
requestBody.append(OAuth.XAUTH_USERNAME + "=" + username + "&");
requestBody.append(OAuth.XAUTH_PASSWORD + "=" + password);
byte[] body = requestBody.toString().getBytes();
connection.setRequestProperty("Content-Length", "" + body.length);
         
OutputStream os = null;
try {
    os = connection.openOutputStream();
    os.write(body);
} finally {
    try{
        if(os != null) {
            os.close();
        }
} catch(IOException e) {}
}

We created a new connection and added the specific XAuth parameters.

Finally, we're handling the exceptions:

public String getAuthorizationWebsiteUrl() {
        throw new RuntimeException("Method is not supported");
    }   
    public String getRequestTokenEndpointUrl() {
        throw new RuntimeException("Method is not supported");
    }   
    public void retrieveAccessToken(OAuthConsumer consumer, String oauthVerifier)
            throws OAuthMessageSignerException, OAuthNotAuthorizedException,
            OAuthExpectationFailedException, OAuthCommunicationException {
        throw new RuntimeException("Method is not supported");
    }   
    public String retrieveRequestToken(OAuthConsumer consumer,
            String callbackUrl) throws OAuthMessageSignerException,
            OAuthNotAuthorizedException, OAuthExpectationFailedException,
            OAuthCommunicationException {
        throw new RuntimeException("Method is not supported");
    }   
    public void setOAuth10a(boolean isOAuth10aProvider) {
        throw new RuntimeException("Method is not supported");
    }

Finally, there's a very important configuration that we need to make in order for the headers to not be altered prior their arrival to the server. Edit the following file: C:\Program Files\Research In Motion\BlackBerry Email and MDS Services
Simulators\MDS\config\rimpublic.property (the location may vary depending on your installation path. In my case, the path is: C:\eclipse\plugins\net.rim.ejde.componentpack5.0.0_5.0.0.25\components\MDS\config\rimpublic.property) and change this line:

application.handler.http.AuthenticationSupport = true

so it'll look like this:

application.handler.http.AuthenticationSupport = false

End of Part 1. Exciting continuations coming to you soon

No comments:

Post a Comment