Tuesday, September 16, 2014

Four Pitfalls of PHP's Comparison Operators: When to Use == Versus ===

Tips for choosing between '==' (equals or loose comparison) and '===' (identical) operators in php.


Equals or == for loose comparisons.
Identical or === for exact matches.
When choosing the correct operator you want to use in PHP you will want to consider whether you want PHP to try different ways to match your data or if a more strict or exact match is needed.  If your data is from a secure location or already validated you may not need the strict identical === operator, but if your data is from an outside source (HTTP GET/POST or user entered) you may want to be more precise depending on the situation.  Below are some example cases to keep in mind, starting with a look at the equals == operator.

1. Zero, false, empty strings, empty arrays, null are 'loosely' equal in PHP

Below are some base cases to keep in mind.  The equals operator uses type juggling, and 0 can become false or an empty string.

Comparisons to Zero
0 == ""                => True
"0" == false           => True
"0" == 0               => True
0 == false             => True

2. Watch out for string comparisons to 0 in PHP

Perhaps the strangest one is that any string with an equals comparison to 0 (as an integer) is True.  If you always are expecting to compare both sides with a string, you should strongly consider using the identical operator '==='.  Perhaps the strangest one is that any string with an equals comparison to 0.

String Comparisons
"str" == 0             => True  (This one is definitely weird)
"str" == 1             => False
"str" == "0"           => False
"strA" == "strB"       => False

3. Type juggling converts strings to numbers and vice-versa

When using the equals operator, the strings are converted to a numeric value when the string comparison match fails.

Numeric Comparisons
3 == 4                 => False
"3" == 3.0             => True
1 == "1"               => True

4. Blank strings are 'loosely' evaluated null

This example shows that you need to be careful if there is a potential for blank strings to be passed along as data.  Also, you may have expected that the string zero "0" would loosely evaluate to null since "0" loosely evaluated to false, but that is not the case.

Null Usage "" == null => True 1 == null => False null == null => True null == 0 => True null == "0" => False
null == false          => True

The Identical Operator removes the loose evaluations


The identical operator is great whenever you expect both sides of the comparison to have the same type and it will help keep you out of trouble.  In this case, it helps remove the guesswork from the comparison and your output is more likely to match what you expect.
Comparisons to Zero
0 === ""               => False
"0" === false          => False
"0" === 0              => False
0 === false            => False

String Comparisons
"str" === 0            => False
"str" === 1            => False
"str" === "0"          => False
"strA" === "strB"      => False

Numeric Comparisons
3 === 4                => False
"3" === 3.0            => False
1 === "1"              => False

Null Usage
"" === null            => False
1 === null             => False
null === null          => True
null === 0             => False
null === "0"           => False


null === false         => False

The Code


The PHP documentation for the comparison operators can be found here, and the chart that displays what the value of the comparisons should be is here.
Here is the sample code that generated the tests above:



Echo "Comparisons to Zero<br />";
eval_string('0 == ""');
eval_string('"0" == false');
eval_string('"0" == 0');
eval_string('0 == false');
echo "<br />String Comparisons<br />";
eval_string('"str" == 0');
eval_string('"str" == 1');
eval_string('"str" == "0"');
eval_string('"strA" == "strB"');
echo "<br />Numeric Comparisons<br />";
eval_string('3 == 4');
eval_string('"3" == 3.0');
eval_string('1 == "1"');
echo "<br />Null Usage<br />";
eval_string('"" == null');
eval_string('1 == null');
eval_string('null == null');
eval_string('null == 0');
eval_string('null == "0"');
eval_string('null == false');
echo ("<br />Now Check as the === operator which removes type juggling:<br />"); Echo "Comparisons to Zero<br />"; eval_string('0 === ""'); eval_string('"0" === false'); eval_string('"0" === 0'); eval_string('0 === false'); echo "<br />String Comparisons<br />"; eval_string('"str" === 0'); eval_string('"str" === 1'); eval_string('"str" === "0"'); eval_string('"strA" === "strB"'); echo "<br />Numeric Comparisons<br />"; eval_string('3 === 4'); eval_string('"3" === 3.0'); eval_string('1 === "1"'); echo "<br />Null Usage<br />"; eval_string('"" === null'); eval_string('1 === null'); eval_string('null === null'); eval_string('null === 0'); eval_string('null === "0"');
eval_string('null === false');
function eval_string($str) { $disp_str = str_pad($str,20," "); echo "$disp_str \t\t=> ".get_boolean_output(eval("return {$str};"))."<br />"; } function get_boolean_output($val) { if ($val == false) return "False"; else return "True"; }

Thursday, September 4, 2014

The Not So Sweet! Saga of King.com's Trademark Trolling

Five Reasons Not to Play Candy Crush Saga


Candy Crush Saga by King.com is the type of game that you love to hate. Maybe it's the constant spamming of Facebook messages from your friends inviting you to play it, but no... This is a story that began with the success of King's popular Facebook game Candy Crush Saga that turned mobile and a desire to protect the company that turns into greed.  Candy Crush Saga now generates millions of dollars per day for King.  Yes, around three to four million per day according to their 2013 IPO registration.  King has produced a highly polished, but wholly unoriginal game that makes the bulk of their wealth.  While credit should be given to King for the marketing success of their Candy Crush Saga game, the trademark abuse and opposition to small independent game developers is completely unnecessary.


Reason #1 - The CandySwipe Saga

Early color matching games - Snood, CandySwipe, and Bejeweled

Before there was Candy Crush Saga there were other colorful matching games. Does anyone remember Snood in the 90's or Bejeweled?  In 2010 Albert Ransom (a small independent developer) released and trademarked CandySwipe, a matching game.  Years later Candy Crush Saga was released on Facebook in April of 2012.  With the rising popularity of Candy Crush in 2013, people began to confuse CandySwipe and Candy Crush, so much so that the Candyswipe game began received negative complaints in the app store for being a Candy Crush clone, when in reality it was the Candy Crush game that appeared to be a clone, although with more polish than CandySwipe.  This caused Albert Ransom to file a complaint against the Candy Crush Saga trademark in which he tried to protect his own brand due to many graphic similarities between the two games.


Reason #2 - The Money Solution

Riccardo Zacconi - King.com

King was able to purchase a bankrupt company to get the European trademark for Candy, and a game in January of 2013 called Candy Crusher which was originally released on Blackberry all the way back in 2009.  These two purchases seemed to both solidify King's ownership of their brand name and prevent any prior art complaints from Ransom's CandySwipe.  Flush with cash from their 2013 popularity the company seemed to be able to fight any legal battle to protect their flagship game.



Reason #3 - All Your Trademarks Are Belong to Us


Trademarks are used for legal protection of company brands.  Let's say you want to sell a product that has a distinct brand name that no one else is allowed to use.  For example: if I open a store and want to sell hamburgers I could call it McDonald's Burgers.  If someone else tries to use a name too similar to attempt to confuse people such as MacDonald's Burgers, then that is trademark infringement.

When King started to gain popularity they trademarked some words related to the titles of their games like 'Candy Crush' and 'Candy Crush Saga'.

It makes sense for King to trademark the titles of their games so that people can't clone their product and claim it as their own.  When a trademark is registered the category of usage needs to be declared.  For example: A company like Time Inc, the producer of Time magazine can trademark the word 'Time' (TM# 86043259).  Their trademark covers things like entertainment services - magazines, journals, websites, and publications. How many of categories did they need to cover to protect their all important magazine brand? 14.  This means you could create a cleaning product called Time (if you really wanted to) but not an entertainment website.

What doesn't make sense are the categories covered by King's trademarks.  The Candy Crush Saga originally covers 37 different uses including: computer games, clothing, classes, fire extinguishers, scientific, nautical, & various other uses.  So, King plans on releasing Candy Crush branded fire extinguishers, really?

Not content to just have a trademark for the name of their products, 'Candy Crush' and 'Candy Crush Saga', but King has applied for trademarks for the individual words appearing in their games Saga (TM# 85482736, 86242335), Candy (TM# 85842584), Sweet! (TM# 86138537) and even Keyword (TM# 86191982).

How can King claim the trademark to use common words such as Saga, Keyword, and Candy?  Sure, the word saga appears in many of their games and candy in their most popular game.  King was able to purchase the European Union trademark for Candy in February of 2013 and then use that purchase to bolster support for their request for the US trademark for the word Candy.  King received approval for the US trademark for use of the word Candy in 2014.


Reason #4 - The Banner Saga


Around the same time that Candy Crush Saga was released on Facebook, an independent developer named Stoic had successfully funded their game 'The Banner Saga' on the popular funding platform Kickstarter.  



Candy Crush Saga           
Banner Saga
Graphics:
Candy Land Style


Viking Themed
Gameplay:
Puzzler

Role Playing Game

Story: Minimal Epic Story - based on player choices

This game has nothing in common with Candy Crush and is a truly original game, but in December of 2013 King filed a trademark complaint against Stoic apparently only because the game had the word 'saga' in the title.

What is a saga?  From Merriam-Webster, a saga is a long complicated story with many events.  Think of the story of Lord of the Rings or the Harry Potter series. The Banner Saga game has a long and complicated story based on player choices and is, in other words, a saga.


Reason #5 - Not So Sweet! 

Sweet! from CandySwipe

The most egregious trademark abuse by King is perhaps the one for the word 'Sweet!'.  King applied for a trademark of the word 'Sweet!' in December of 2013.  Looking at the scope of the trademark reveals that it covers too many things to list: from Malt Beer, Lithographic Stones to Cobblers' wax and Make-Up.  These are just some of the 2191 categories of items covered.  They can't realistically expect to be involved with the distribution and business of producing all these types of goods.  So, if a baker makes a pie that has 'Sweet!' somewhere on the label they can get sued for infringement by a software company?

On the same day the trademark for Candy was approved, King notified indie developer Benny Hsu that his game 'All Candy Casino Slots - Jewels Craze Connect: Big Blast Mania Land' was infringing on their candy trademark.  Similar to The Banner Saga, this game has nothing in common with Candy Crush Saga except that it features a word in common in its long title.  Granted in this case, Hsu may have been trying to capitalize on popularity of certain keywords to get an edge in a crowded app store.

Popular opinion in the gaming industry began to stir with antipathy for King. Game Jams were held by game developers to create games that featured candy. King posted an open letter on their website attempting to explain that they were just protecting their brand but failed to demystify the greedy breadth of the trademarks they were seeking (Sweet! - 2191 categories of use, Candy - 106 uses, Saga - 112 uses, Keyword - 305 uses).  The letter satisfied no one, and unable to save face the company abandoned the Candy trademark just 40 days after having it approved.


Epilogue


The CandySwipe issue was finally resolved between King and Ransom in April of 2014 when Ransom removed his complaint against the Candy Crush Saga trademark after reaching an understanding with King.  They decided that both games could coexist on the app store.

In August of 2014 after eight months of objection to The Banner Saga, King finally removed their opposition to the usage of saga in the title.  The game is now published on Steam.

Benny Hsu renamed his game to 'All Sugar Casino Slots - Jewels Craze Connect: Big Blast Mania Land' removing the word Candy from the title.

King still owns the European trademark for the word Candy and is awaiting approval for the expanded usage of their trademark for: Keyword, Saga, and Sweet!  In fact, if they receive final approval of the extremely broad scope they are requesting, it is likely the title of this blog post could be opposed by King.

Update

Candy Crush berry fruit snacks

So, just two days after posting this article I get home to find my wife had purchased some new fruity snacks to munch on.  I was quite shocked to find these sitting innocently on the kitchen table.

Wednesday, July 9, 2014

RHM picks for classes to take at the Amazon Summit NY

Right Handed Monkey is going to be at the New York Amazon Summit 7.10.2014!

I'm very excited to spend this time getting to know more about the Amazon platform and meet other startups and entrepreneurs in the area.  Let's do this, people.  Leave a comment if you're interested in meeting up at one of the social events.

My picks for the Amazon Summit 2014:


1. Deploy, Manage, and Scale Your Apps with OpsWorks and Elastic Beanstalk


Why: I picked this class because I want to learn about scaling my current system and what tools are available to make it grow.  Plus I want to make sure when I am programming a site that I do it in a way that is compatible with scaling (ie. Beanstalk.).

2. Scaling on AWS for the First 10 Million Users


Why: This is a given.  Everyone wants to know how to scale up their software so it can support as many users as possible.

3. Updating Security Operations for the Cloud, presented by Trend Micro


Why: Originally I considered 'Managing an Enterprise Class Hybrid Architecture', but the truth is I'm not managing mega-sized legacy infrastructures and won't be anytime soon.  My sites are lean and have simple needs, but may have to scale fast.  Plus my sites get lots of weird hits and spam from places like Russia and China.  Some extra security won't be that bad of an investment.

4. Building Mobile Apps on AWS


Why: Because, you had me at mobile.

5. AWS Summit Social and AWS Activate - Networking & Social Event for Startups


Why: Get to meet at greet the brains behind the organization.  

Hope to see you there!

Monday, June 2, 2014

PHPUnit - Migrate from PEAR Install to PHAR (Windows & Linux)

Migrate PHPUnit from PEAR toPHAR


You have installed PHPUnit with PEAR, but the installation method is migrating and you keep getting the error message:

You have installed PHPUnit via PEAR. This installation method is no longer supported and http://pear.phpunit.de/ will be shut down no later than December, 31 2014.

Please read http://phpunit.de/manual/current/en/installation.html and learn how to use PHPUnit from a PHAR or install it via Composer. 


It lets you know that you haven't installed it the right way, but now how do you correct the problem and remove the annoying error message? 


First remove it (Windows & Linux):

pear uninstall phpunit/PHPUnit



Get and install PHPUnit using the phar, Linux (or Amazon EC2) version:

wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit

#and in my case, the phpunit executable also needed to be placed herecp /usr/local/bin/phpunit /usr/bin/phpunit

Get and Install PHPUnit using the phar, Windows version:

Download the .phar file to somewhere you can run it with php.
  1. Open this address in a browser and save to your disk: https://phar.phpunit.de/phpunit.phar 
  2. Open the command line and go to the directory (cd {savepath}) you have saved the file in. 
  3. Execute tests with: php phpunit.phar testfile.php  
  4. Copy phpunit.phar to overwrite your existing phpunit file (replace c:\php\phpunit with your installed location).  Back up the original phpunit file just in case.
    copy c:\php\phpunit c:\php\phpunit.bakcopy phpunit.phar c:\php\phpunit

NOTE:

If you are getting the error below when running your tests:
Failed opening required 'PHPUnit/Autoload.php'
You may need to remove or comment out the Autoload include line .  This file has already been included in the .phar PHP Archive.

//require_once 'PHPUnit/Autoload.php';



Now, use this testing framework to check your code and help you feel confident when deploying your applications.


There are a great number of resources on the official PHPUnit page that you should check out if you want to learn more about PHPUnit and how to use it to your advantage - PHPUnit Presentations.

Monday, May 19, 2014

Great Code Syntax Highlighter for Blog Posting

How do you get nicely formatted code into your Blog that looks as good as Stack Overflow or at least as good as your IDE?  


You need an online syntax highlighter that converts your source code into pretty HTML code.  I liked the one at http://markup.su/highlighter/ and found the Slush and Poppies theme to be the most attractive.
  1. Paste code into the Online Syntax Highlighter
  2. Paste that code back into your blog as HTML (use HTML view in Blogger)
  3. Add a scrollbar using the "overflow: auto;" css setting to handle longer lines of text

Blogger Notes: If you don't want to see scrollbars, you might want to manually wrap columns at 70 characters to keep it looking nice, because Blogger doesn't mind letting lines extend beyond their section.

public class CTGTablePool {

    /**
    *
    * @param tableClass
    * @param c - Use application context if your process lives longer
    * than the activity
    * @return
    */
    public synchronized static < T > T getTable(Class < T > 
        tableClass, Context c) {
        
        self().checkInit();
        String hashKey = self().dbName + tableClass.getName();
        if (self().tableMap.get(hashKey) == null) {
            Utils.LogD(TablePool.class.getName(), 
                "Creating table connection for: " +
                tableClass.getName() + " in db: " + self().dbName);

            Constructor < T > construct;
            T tableInstance = null;
            try {
                construct = tableClass.getConstructor(Context.class,
                    String.class);
                tableInstance = (T)construct.newInstance(
                    c.getApplicationContext(), self().dbName);
                self().tableMap.put(hashKey, tableInstance);
            } catch (Exception e) {
                throw new RuntimeException(
                    "Could not create the table: " + 
                    tableClass.getName() + " in db: " + 
                    self().dbName + ", because of: " + e.toString());
            }
            return tableInstance;
        } else {
            Utils.LogD(TablePool.class.getName(), 
                "Reusing existing table connection for: " + 
                tableClass.getName() + " in db: " + self().dbName);
            return (T)self().tableMap.get(hashKey);
        }
    }

    /**
    * Reset all the database connections
    */
    public synchronized static void invalidate() {
        Utils.LogD(CTGTablePool.class.getName(), 
            "Invalidating database connections");
        self().tableMap.clear();
        self().tableVersionTable = null;
        TableManager.invalidate();
    }
}



Ok, now that looks ok, but it is a pain to manually wrap the lines to 70 chars.  Help!  Is there a better blogging tool that is more friendly to programmers?

Note: if you don't manually wrap it before pasting the code into the syntax highlighter, you get an odd looking line extending beyond the sample area.

    private void checkInit() {
        Assert.assertNotNull(CTGTablePool.class.getName()+".setDbName() needs to be called first!", self().dbName);
    }


You can modify the <pre> style to add a scroll bar instead which looks a little better.  Although I don't know why Blogger seems to think all I should get is this narrow space.  I don't think I am asking for too much, am I?
 <pre style="background: #f1f1f1; color: black; overflow: auto;"> 

private void checkInit() {
        Assert.assertNotNull(CTGTablePool.class.getName()+".setDbName() needs to be called first!", self().dbName);
    }


Friday, May 16, 2014

Easiest Way to Handle Spammers on Contact or Comment Forms

What is the easiest way to prevent spam in contact or comment forms on a website?


I have a contact form on one of my websites www.lutz-engr.com and was getting some actual people filling out the form for legitimate reasons, but the vast majority of the content was spam from countries such as Russia and from IP addresses reported from many other countries.  See my original post on "How to Handle Spammers".  Sometimes the forms are filled out in groups and I suspect there is either a system that can disguise its IP address or there is a network of spam controllers running on different computers.  Either way I always found that the spammed forms had one thing in common...

Spammers do not run javascript.


At least my spammers weren't using javascript.  So now I could turn this observation into an advantage and simply require users to be running javascript in order to submit a form on the website.  That's not too big a deal - I don't know any typical users that browse the web with javascript disabled.  Have you tried it?  It sucks!


This is what my form looks like in HTML.  Note I am using the PUT HTML verb here instead of the typical POST or GET.  The reason I use PUT is that my PHP page will not respond to the PUT method, so unless it is changed by the javascript to POST, activating the form won't be handled by the webserver.

<form action="" method="PUT" name="contact_form">
    Your Name: &nbsp;</div>
    <input type="text" name="name_field" size="35">*<br>
    Phone: &nbsp;</div>
    <input type="text" name="phone_field" size="35"><br>
    Email: &nbsp;</div>
    <input type="email" name="email_field" size="35">*<br>
    <input type="hidden" value="nojs (unused)" name="timedayjs" 
        id="timedayjs">
    <input type="submit" value="Submit" name="comment_submit_button" 
        id="comment_submit_button" disabled="disabled">
</form>

Change your form so that it is initially disabled in HTML

  • Form Method - Set to method="PUT" to disable, but changed to "POST" for javascript users
  • Form Action - Set to action="" to completely disable the form and when run, javascript puts the correct location here
  • Form Field 'timedayjs' - This is just a form field I used to capture if the user had run the javascript on the page.  It really could be named anything, but I choose this name to  throw anyone off who actually did take the time to look at the HTML code.
  • Submit Button - This I set to disabled="disabled" so a regular user that had javscript disabled would notice they couldn't click on the form.  Of course a spammer could just ignore this directive, but without the correct form method and form action the results still won't get sent to the server.

Enable your form using javascript


Below is the javascript code I use that requires the user to have javascript running which enables the form and to fixes the four configuration items that are disabled in the HTML code.  I am using jQuery so the functions go in the $(document).ready function to be executed after the page is ready.  If you don't use jQuery you could put the functions in a javascript setupForm() function and execute in on the name_field using something like onchange="setupForm()".

<script type="text/javascript">
    $(document).ready (function() {
      $('#timedayjs').val('set by js');
      document.contact_form.method = 'post';
      document.contact_form.action = 'index.php';
      document.getElementById('comment_submit_button').disabled=false;
    });
</script>


All of these actions has resulted in a drastic reduction of spamming through our contact us forms on the website.  Please try this code on your site.  Leave a note or +1 if you feel this was helpful. 

Wednesday, April 23, 2014

Android Networking Example and Tutorial (with easy retries)

This post explains how to use the WorxForUs Network framework to have an Android app that works robustly even in areas that have poor network connectivity. 

Background:

I wrote this library because none of the tutorials I found went clearly into the different types of faults that you can experience when developing web apps.  They worked fine to get you started, but what about if you are now on a mobile network and not all of your packets are getting through?  What happens if you socket times out?  What do I do if I need to handle cookies?  This framework is an attempt to address those issues along with adding a simple way to retry communications that did not go through properly.

Features:

  • Automatic cookie handling
  • Baked in network retries (just specify number of retries to allow before failing)
  • Easy to understand handling of the various types of network errors
  • Handles authenticated and unauthenticated HTTP requests

Accessing the Network with Retries (without authentication):

First, download the WorxForUs framework from the github page (or clone here).
Import the project into your Eclipse or Android Studio.

Check to see if the network is connected (you don't want to try and download something if the user has activated flight mode).

    //NetResult is an object to store the results
    NetResult netResult = null; 
    String url = "http://www.google.com/";
    //Load the values being posted
    List<NameValuePair> params = new ArrayList<NameValuePair>();    params.add(new BasicNameValuePair("q", "bears"));

    String serverResponse ="";
    //If the network is not currently connected, don't try to talk
    if (NetHandler.isNetworkConnected(con)) {
        netResult = NetHandler.handlePostWithRetry(url, params, NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);

        //get the server response as a string
        serverResponse = Utils.removeUTF8BOM(EntityUtils.toString(net_result.net_response_entity, Utils.CHARSET));
        //Notify the HTTP client that all data was read
        netResult.closeNetResult();
    }
What is this NetResult object ?  This object contains all the information you need to decode the response from the webserver. If netResult.net_response_entity is not null, then that is the response from the server.  Send that value to your handling routing.

    NetResult.net_success - Equals true when the server was successfully contacted (this says nothing about if your request was valid though).
    NetResult.net_error - Contains the error message or exception associated with the connection
    NetResult.net_error_type - Contains the name of the type of error that occurred (ie. HttpResponseException, SocketTimeoutException, SocketException, or IOException)
    NetResult.net_response_entity - This is the actual response from the server.  A common use is to run:
String consume_str = Utils.removeUTF8BOM(EntityUtils.toString(net_result.net_response_entity, Utils.CHARSET)); 
or if you want to capture JSON data
NetResult.handleGenericJsonResponseHelper(net_result, this.getClass().getName()); 
Note: the class name is passed for logging purposes only
Now net_result.object will contain your JSON objects parsed for you

After reading your data from the netResult.net_response_entity, you will need to call netResult.closeNetResult().  This function eventually calls HttpEntity.consumeContent() which releases any resources associated with that object.

Accessing the Network with Authentication:

 To call the network with authentication is simple once you have the authentication helper configured for your particular webserver.





// load authentication data
if (!AuthNetHandler.isAuthenticationSet()) {
    // passing the context here allows the system to update the preferences with a validated usernumber (if found)
    AuthNetHandler.setAuthentication(host, new MyAuthenticationHelper(con));
    NetAuthentication.loadUsernamePassword(username, password);
}
// if network is ready, then continue
// check if network was disabled by the user

if (NetHandler.isNetworkConnected(context)) {
    // if user has credentials - upload then download so data is not lost
    if (NetAuthentication.isReadyForLogin()) {
        netResult = AuthNetHandler.handleAuthPostWithRetry(url, params, num_retries);
        //...handle the netResult response here
        netResult.closeNetResult();
    } else {
        Log.e(this.getClass().getName(), "Did not attempt to login, no authentication info");
    }
}


When you have authenticated requests there are a few extra steps before you call  AuthNetHandler.handleAuthPostWithRetry(...).

First you will need to implement a class from NetAuthenticationHelper.  This will tell the framework how you are going to login, what messages are errors,  and basically how you are validating a login.   It may seem like a lot of extra code, but try to stick with it.  Having all your authentication code in one place can be extremely helpful.

public class MyAuthHelper implements NetAuthenticationHelper {
    @Override
    public String getLoginURL(String host) {
        return "https://myweb/remote_login_address.php";
    }


getLoginURL is simple the location where you expect the app to connect to when sending login parameters.  Just put the URL you need in here.

    @Override
    public void markAsLoginFailure(NetResult result) {

        result.object = new String("Login Error");
    }


or, in my case I use JSON objects

    @Override
    public void markAsLoginFailure(NetResult result) {
        try {
            result.object = new JSONObjectWrapper("Jsonstring");
        } catch (JSONExceptionWrapper e) {
            throw new RuntimeException("Could not parse default login failed JSON string.");
        }
    }

Put whatever your webserver responds with in here on a failed user login.  This section forces a result to be simulated as a login failure in the result.object variable.  Let's say you've identified a failed login, this output gets sent through validateLoginResponse(...) where the failed login will be identified.

    @Override
    public void markAsLoginSuccessFromCache(NetResult result) {

        result.object = new String("Login Successful");
    }


Put whatever your webserver could respond with on a successful login.  This section forces a result to be simulated as a login success in the result.object variable.

    @Override
    public String getLoginErrorMessage() { return "Could not login user"; }


Here put what you would like be passed as the error message to netResult.net_error when a user is not able to be logged in.

    @Override
    public int validateLoginResponse(NetResult netResult) {
        //Returns NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR
        //Check the login server result from netResult.object
        //to determine if your login was successful or not
        if (netResult.net_success) {
            String response = netResult.object;
            if (response.contains("not logged in indication")) {
                return NetAuthentication.NOT_LOGGED_IN;
            } else if (response.contains("login error indication"))) {
                return NetAuthentication.LOGIN_FAILURE;
            } else if (response.contains("cant parse data indication"))) {
                return NetAuthentication.SERVER_ERROR;
            }
        } else {
            return NetAuthentication.NETWORK_ERROR;
        }
        return NetAuthentication.NO_ERRORS;    
    }

validateLoginResponse(...)performs the bulk of the login checking, it determines if the user wasn't logged in, there was a login error (ie. wrong password), a server error, or network error.  Depending on what you expect from the server, you will send a response of NetAuthentication.NO_ERRORS, NETWORK_ERROR, or SERVER_ERROR.

    @Override
    public int peekForNotLoggedInError(NetResult netResult) {

        //... check the netResult.object for a login failure on a page that wasn't the login page.
    }


Similarly to the previous function, the peekForNotLoggedInError(...) checks for login errors, but on pages that are not the login page.  Consider the example where you have already logged in, but then check a different page to download some other data.  If your user's session is suddenly logged out, you will get an error that could look different than the one you get on the login page.  So that specific logic for unexpected login failure goes in here. 

    @Override
    public NetResult handleUsernameLogin(String host, String username, String password) {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        params.add(new BasicNameValuePair("uname_field", "username"));
        params.add(new BasicNameValuePair("pword_field", "password"));
       
        NetResult netResult = NetHandler.handlePostWithRetry(this.getLoginURL(host), params , NetHandler.NETWORK_DEFAULT_RETRY_ATTEMPTS);
        //save the result and close network stream
        consume_str = Utils.removeUTF8BOM(EntityUtils.toString(result.net_response_entity, Utils.CHARSET));
        netResult.object = consume_str;
        netResult.closeNetResult();
        return netResult;
    }


The handleUsernameLogin(...) function provides the actual fields and logic needed to send the request to the webserver.  Simply fill in your specific fields for login.

If you have a different request using a token, the handleTokenLogin(...) function can be used for that purpose.

Wow, if you've made it to the end of this tutorial, you are a real trooper and I salute you!

Notes:

HTTP Params are encoded with UTF-8.  If your webserver expects another character set, you will need to change the handlePost(...) routing in NetHandler to use that encoding scheme.