Android Tutorial User registration using httputils2, php, MySql and mail

When you join a forum you have to define a username and type in your email address and a password. After that you receive an email to finish the registration. Today I want to show the process how to implement this with b4a and php.

Workflow

  1. User types in username, mail address and password
  2. The data will be sent to a php script
  3. The script will check if the mail address is already in use
  4. Otherwise it will insert the data in a table with a random number generated
  5. An email will be sent to the user containing this random number with a link to click on
  6. Clicking on the link the php script will be called again
  7. The script checks if the mail adress and random number matches
  8. If yes, it will update the table and the user is registered
  9. User can now login

The MySqlTable

It's a very simple table with the following culumns

name (the username)
mail (email address)
t (timestamp, with this we can add some functions like automatic logoff, etc.)
regnr (the random number will be stored here)
status (is used in the different stages of registration like "mail is sent" or "registered")
pw (password)
online (online status)

The app

Create 3 Edittext Views (name, mail address and password) and 3 buttons (register, login, logout)

REGISTER

B4X:
Sub RegButton_Click
     
    Dim register As HttpJob
    register.Initialize("register", Me)
    register.Download2("http://www.yourdomain.com/registerlogin.php", _
      Array As String("Action", "Register", "Name", RegName.Text, "Mail", RegMail.Text, "PW", RegPW.Text))
End Sub

Here we just start a httpjob with the parameters name, mail address and password and send it to the script

B4X:
$action = $_GET["Action"];

switch ($action) {
    case "Register":
        $mail=$_GET["Mail"];
        $name = $_GET["Name"];
        $pw= $_GET["PW"];
      
        $q = mysql_query("SELECT * FROM Users Where mail = '$mail'");
        $count = mysql_num_rows($q);
      
      
        if ($count == 0)
            {
          
            $randomnumber = mt_rand(111111, 999999);
            $res = mysql_query("Insert into Users (name, mail, regnr, status, pw, online) VALUES ('$name', '$mail', $randomnumber, 'M', '$pw', 'N')");
            mail('$mail','Your registration',
                'Please klick on this link to finish the registration process: ' .
                  'www.yourdomain.com/registerlogin.php?Action=Mail&Mail=' . $mail . '&RegNr=' . $randomnumber,
                  "From: [email protected]");
            print json_encode ("Mail");
            }          
        else
            print json_encode ("MailInUse");
      
      
        break;

First it checks if the mail address is in use. If not, an email is sent with a random number.

To The app we will send back a single value "Mail" or "MailInUse". So we can check what thr script did:

B4X:
Sub JobDone(Job As HttpJob)
    ProgressDialogHide
    If Job.Success Then
    Dim res As String, action As String
        res = Job.GetString
        Log("Back from Job:" & Job.JobName )
        Log("Response from server: " & res)
      
        Dim parser As JSONParser
        parser.Initialize(res)
      
      
      
        Select Job.JobName
                      
            Case "register"
                action = parser.NextValue
                If action = "Mail" Then
                    Msgbox("A mail was sent to " & RegMail.Text & ". Please click on the link to finish registration", "Registration")
                    
                End If
              
                If action = "MailInUse" Then
                    Msgbox("The mail address " & RegMail.Text & " is already in use", "Registration")
                End If
...

The user will be informed if the Mail is in use or if he got an email

The email looks like:

B4X:
Please klick on this link to finish the registration process: www.yourdomain.com/registerlogin.php?Action=Mail&[email protected]&RegNr=547057

As we know there is no difference calling a php scrip by our app via httputils2 or by typing it directly in the browser (clicking on the link does the same)

In the script:

B4X:
$action = $_GET["Action"];
switch ($action) {

case "Mail":
        $regnr = $_GET["RegNr"];
        $mail=$_GET["Mail"];
      
        $q = mysql_query("SELECT * FROM Users Where mail = '$mail' and regnr = $regnr and status = 'M'");
        $count=mysql_num_rows($q);
      
      
      
        if ($count == 0)
            {
            print json_encode ("This registration is not valid / mail address ist already registered");
            }          
        else {
            $res=mysql_query("Update Users SET Status = 'R' where Mail = '$mail' and RegNr =$regnr");
            echo("$mail is registered now :-)");
            }
        break;

The status is first set to "M" (=mail sent) to "R" (= registered) when the link is clicked

By using a random number we add security to the process because nobody can know the number and it will be send to the given mail address only. Typing the link in the browser without knowing the number will not work :)

After that I have added a login sub in my app and added the counterpart in the php in the same way.
 
Last edited:

jahswant

Well-Known Member
Licensed User
Longtime User
I created this project and tested the apk i have the same error while doing a register or a login action.
B4X:
LogCat connected to: 7a500ae8
--------- beginning of /dev/log/main


--------- beginning of /dev/log/system


** Activity (main) Create, isFirst = true **


** Activity (main) Resume **
** Service (httputils2service) Create **
** Service (httputils2service) Start **
** Activity (main) Pause, UserClosed = false **
** Activity (register) Create, isFirst = true **
** Activity (register) Resume **
java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)


    at libcore.io.IoBridge.open(IoBridge.java:406)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:370)
    at anywheresoftware.b4a.samples.httputils2.httputils2service._hc_responsesuccess(httputils2service.java:130)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.BA$3.run(BA.java:320)
    at android.os.Handler.handleCallback(Handler.java:605)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4517)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
    at libcore.io.IoBridge.open(IoBridge.java:390)
    ... 16 more
java.lang.RuntimeException: java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:201)
    at anywheresoftware.b4a.BA$3.run(BA.java:320)
    at android.os.Handler.handleCallback(Handler.java:605)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4517)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)
    at libcore.io.IoBridge.open(IoBridge.java:406)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:370)
    at anywheresoftware.b4a.samples.httputils2.httputils2service._hc_responsesuccess(httputils2service.java:130)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    ... 10 more
Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
    at libcore.io.IoBridge.open(IoBridge.java:390)
    ... 16 more
 

ArminKH

Well-Known Member
I created this project and tested the apk i have the same error while doing a register or a login action.
B4X:
LogCat connected to: 7a500ae8
--------- beginning of /dev/log/main

--------- beginning of /dev/log/system


** Activity (main) Create, isFirst = true **


** Activity (main) Resume **
** Service (httputils2service) Create **
** Service (httputils2service) Start **
** Activity (main) Pause, UserClosed = false **
** Activity (register) Create, isFirst = true **
** Activity (register) Resume **
java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)


    at libcore.io.IoBridge.open(IoBridge.java:406)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:370)
    at anywheresoftware.b4a.samples.httputils2.httputils2service._hc_responsesuccess(httputils2service.java:130)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    at anywheresoftware.b4a.BA$3.run(BA.java:320)
    at android.os.Handler.handleCallback(Handler.java:605)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4517)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
    at libcore.io.IoBridge.open(IoBridge.java:390)
    ... 16 more
java.lang.RuntimeException: java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:201)
    at anywheresoftware.b4a.BA$3.run(BA.java:320)
    at android.os.Handler.handleCallback(Handler.java:605)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4517)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.io.FileNotFoundException: /data/data/my.computerise.registeruser/cache/2: open failed: ENOENT (No such file or directory)
    at libcore.io.IoBridge.open(IoBridge.java:406)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:88)
    at anywheresoftware.b4a.objects.streams.File.OpenOutput(File.java:370)
    at anywheresoftware.b4a.samples.httputils2.httputils2service._hc_responsesuccess(httputils2service.java:130)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
    ... 10 more
Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
    at libcore.io.Posix.open(Native Method)
    at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
    at libcore.io.IoBridge.open(IoBridge.java:390)
    ... 16 more
Are u using same job for multiple download?if yes you should dim a new job if u use that on for loop
And other reason is you are remove file after download by using job.release
So you should dim a new job for each download(u can dim job with same name) or delete job.release at jobdobe
 

Niek Wegman

New Member
Licensed User
Help! i'm fairly new to mysql and when trying this example i constantly get the "This registration is not valid \/ mail address is already registered"-message, i have changed the PHP files to connect to my host and uploaded the files onto my own server, also i changed the b4a code to match my server. whenever i try to register a new user, nothing changes in the database, have i set that up wrong?
 

nwhitfield

Active Member
Licensed User
Longtime User
This is really useful. Also worth reminding people to think carefully about the messages you display to the user, depending on the context. If it's a game, or a shopping thing, then it probably doesn't matter if you can enter an email and be told "Already in use"

For more sensitive things, you may want to consider a more vague response, that is exactly the same as you might give for, say, an invalid input. Why? Because otherwise, people can use the registration form to determine if someone already has an account. With a dating site, for example, someone might enter their partner's address, and then accuse them of cheating if it's already registered.

Also, since end users are submitting information to the form, it would be a good idea to use either mysql_real_escape_string or use the MySQLi extension in PHP and prepared queries, which helps prevent any possible SQL injection attacks.

Assuming you've set up your database connection as $mydb:

B4X:
$newuser = $mydb->stmt_init() ; 
if ( $newuser->prepare("INSERT INTO Users (name, mail, regnr, status, pw, online) VALUES (?, ?, ?, 'M', ? 'N')") {
  $newuser->bind_params('ssis',$name,$mail,$randomnumber,$pw) ;
  $newuser->execute() ;
}

This is also very similar to how you bind parameters for SQL queries in B4x, of course, so perhaps a little more familiar to some users.
 
Top