Android, XMLRPC and Drupal authentication

Events happening in the community are now at Drupal community events on www.drupal.org.
pol's picture

Hello,

I'm pretty new in the Android World and I just finished a website (http://drupal.org/node/777420).

I would like to make an android app out of it.

I've read alot and I'm using the 'Services' module with XMLRPC.

I'm struggling to do a user authentication and I don't understand the 7 arguments it needs.

Here is the Android Java code I'm using:

                final XMLRPCMethod method1 = new XMLRPCMethod("user.login", new XMLRPCMethodCallback() {
                    public void callFinished(Object result) {
                        testResult.setText(result.toString());
                        // returns:  result={user={uid=0, cache=0, hostname=81.243.167.37, session=, roles={1=anonymous user}}, sessid=4da293fe9394029b4e465b01d81c5fd5}
                    }
                });
                XMLRPCMethod method2 = new XMLRPCMethod("system.connect", new XMLRPCMethodCallback() {
                    public void callFinished(Object result) {
                        GlobalDataStore.sessid = (String) ((Map) result).get("sessid");
                        Object[] params = {
                             GlobalDataStore.sessid,GlobalDataStore.user,GlobalDataStore.password
                        };
                        method1.call(params);
                    }
                });
                method2.call();

The error here is:

D/Test    (  488): org.xmlrpc.android.XMLRPCFault: XMLRPC Fault: Missing required arguments. [code 1]

I've looked into documentation and I see that the method user.login needs 7 arguments:

  • string hash (required) : A valid API key.
  • string domain_name (required) : A valid domain for the API key.
  • string domain_time_stamp (required) : Time stamp used to hash key.
  • string nonce (required) : One time use nonce also used hash key.
  • string sessid (required) : A valid sessid.
  • string username (required) : A valid username.
  • string password (required) : A valid password.

I have the last 3 (sessid, username and password), how, where to get the others ?

Thanks!

Comments

Checkout DrupalCloud library

skyredwang's picture

Checkout DrupalCloud library for Android http://github.com/skyred/DrupalCloud

and I am always on IRC #drupal-services

Thank you for this link!

nebula_haze's picture

Thank you for this link!

Have a look at this PHP

bsenftner's picture

Have a look at this PHP example of xmlrpc authentication: http://drupal.org/node/774298

At the bottom of that link I show how the code at the top of the link is used, with the key bits being the following steps:

  1. first you do a system.connect() to get your session id as an anonymous user
  2. then you call user.login() with all the 7 parameters you've already identified

You actually work with two session ids - the anonymous one is required for login, and after successful login you get a new session id that is for all future uses during that session.

The username and password need to be a valid username and password to the drupal site you're connecting.

Things are getting better

pol's picture

Ok guys,

First, thanks to DrupalCloud, I've updated my code.
Things are getting better in a way that the server reply that the username/password are not good.

This is the code:

                final XMLRPCMethod method1 = new XMLRPCMethod("user.login", new XMLRPCMethodCallback() {
                    public void callFinished(Object result) {
                        Log.d(GlobalDataStore.TAG, "result=" + result.toString());
                        testResult.setText(result.toString());
                    }
                });
                XMLRPCMethod method2 = new XMLRPCMethod("system.connect", new XMLRPCMethodCallback() {
                    public void callFinished(Object result) {

                        final Long timestamp = new Date().getTime() / 100;
                        final String time = timestamp.toString();
                        GlobalDataStore.nonce = GetNonce(10);

                        StringBuilder sb = new StringBuilder();
                        sb.append(time);
                        sb.append(";");
                        sb.append(GlobalDataStore.domainname);
                        sb.append(";");
                        sb.append(GlobalDataStore.nonce);
                        sb.append(";");
                        sb.append("user.login");

                        Mac hmac;
                        try {
                            hmac = Mac.getInstance("HmacSHA256");
                            hmac.init(new SecretKeySpec(GlobalDataStore.drupalkey.getBytes(),hmac.getAlgorithm()));
                            hmac.update(sb.toString().getBytes());
                            GlobalDataStore.hash = new String(Hex.encodeHex(hmac.doFinal()));
                        } catch (NoSuchAlgorithmException e2) {
                            // TODO Auto-generated catch block
                            e2.printStackTrace();
                        } catch (InvalidKeyException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }                      
                       

                        GlobalDataStore.sessid = (String) ((Map) result).get("sessid");

                        Log.d(GlobalDataStore.TAG, "string to hash=" + sb.toString());
                        Log.d(GlobalDataStore.TAG, "hash=" + GlobalDataStore.hash);
                        Log.d(GlobalDataStore.TAG, "key=" + GlobalDataStore.drupalkey);
                        Log.d(GlobalDataStore.TAG, "timestamp=" + time);
                        Log.d(GlobalDataStore.TAG, "nonce=" + GlobalDataStore.nonce);
                        Log.d(GlobalDataStore.TAG, "sessid=" + GlobalDataStore.sessid);
                        Log.d(GlobalDataStore.TAG, "user=" + GlobalDataStore.user);
                        Log.d(GlobalDataStore.TAG, "password=" + GlobalDataStore.password);

                        Object[] params = {
                                GlobalDataStore.hash,GlobalDataStore.domainname,time,GlobalDataStore.nonce,"user.login",GlobalDataStore.drupalkey,GlobalDataStore.sessid,GlobalDataStore.user,GlobalDataStore.password
                        };
                                               
                        method1.call(params);
                       
                    }
                });
                method2.call();
                }

Here is what I get in the log:

04-29 09:32:33.265: DEBUG/NDP(453): string to hash=12725263532;example.com;T4FYp6RPQY;user.login
04-29 09:32:33.265: DEBUG/NDP(453): hash=dc7b33a17d670ba68a0a9475490cdf8f52c35a134cd9f57e3085d6528e897791
04-29 09:32:33.265: DEBUG/NDP(453): key=17fa1b2b4c1f0a49a8d02201b50a5a0e
04-29 09:32:33.265: DEBUG/NDP(453): timestamp=12725263532
04-29 09:32:33.275: DEBUG/NDP(453): nonce=T4FYp6RPQY
04-29 09:32:33.275: DEBUG/NDP(453): sessid=f41f177b5b3d6531667271bb074935eb
04-29 09:32:33.275: DEBUG/NDP(453): user=admin
04-29 09:32:33.275: DEBUG/NDP(453): password=adminpassword
04-29 09:32:33.456: DEBUG/dalvikvm(453): GC freed 1624 objects / 320120 bytes in 94ms
04-29 09:32:34.075: DEBUG/Test(453): error
04-29 09:32:34.075: DEBUG/Test(453): org.xmlrpc.android.XMLRPCFault: XMLRPC Fault: Nom d'utilisateur ou mot de passe incorrect. [code 1]
04-29 09:32:34.075: DEBUG/Test(453):     at org.xmlrpc.android.XMLRPCClient.callEx(XMLRPCClient.java:226)
04-29 09:32:34.075: DEBUG/Test(453):     at org.xmlrpc.Test$XMLRPCMethod.run(Test.java:266)
04-29 09:32:34.556: WARN/SurfaceFlinger(59): executeScheduledBroadcasts() skipped, contention on the client. We'll try again later...
04-29 09:32:37.646: DEBUG/dalvikvm(103): GC freed 2919 objects / 164520 bytes in 84ms

The user password, key and domain name has been changed of course and I'm using the good one in my code.

I tried to change the api key and I got an error saying that the api key is wrong, so I suppose the problem is coming from elsewhere...

-Pol-

REST/JSON

recidive's picture

In Android it's a lot easier to consume REST/JSON servers since there's build in support for JSON and simple HTTP requests.

We used REST server + this JSON output patch to expose a simple REST/JSON server to Android.

The client for logging the user in would be as easy as accessing this URL:

http://site.com/services/rest?method=system.connect

Parsing it's JSON serialized result, for getting the session id, then calling this

http://site.com/services/rest?method=user.login&sessid=<sessid>&username=<username>&password=<password>

passing user credentials and session id from system.connect.

The result will be either an object representing the user or an error.

All this is for Services 1.x, though.

If you use at least 2.x of

voxpelli's picture

If you use at least 2.x of the REST server no hack is needed for JSON output.

I succeded

pol's picture

I succeded to get it working thanks to DrupalCloud sources.

I'll forget XMLRPC !

I still have a question in my mind, is it possible to post images (CCK Fields) through those services and using POST ?

Is there a limit in size ?

Thanks!

-Pol-

iPhone

irishgringo's picture

any new hints for iPHONE

I'm working on a similar

hunt3r's picture

I'm working on a similar android app, any luck posting photos?

Similar problem, but not the same. Help!!!

m3n0R's picture

I'm trying to connect my Android App with Drupal Services. There is an error on the Log Console, but I think I've done everything well. Here is the code. Can you have a look please?

public void onCreate(Bundle savedInstanceState) {

    //http://thetimebird6.atenealabs.com/?q=services/xmlrpc
    //http://thetimebird6.atenealabs.com/xmlrpc.php

    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main);
    TextView session = (TextView)findViewById(R.id.session);

    DrupalXmlRpcService service = new DrupalXmlRpcService("thetimebird6.atenealabs.com","24aeee489fae05abaca3cd9cee2a04fe","http://thetimebird6.atenealabs.com/services/xmlrpc");
    if(service.Connect() == true) {
            session.setText(service.Login("user", "pass"));
            service.Logout();
    }
    else{
            session.setText("incorrect");
    }
}

and log error is :

07-29 12:42:09.852: WARN/System.err(288): org.xmlrpc.android.XMLRPCFault: XMLRPC Fault: Server error. Requested method system.connect not specified. [code -32601]
07-29 12:42:09.862: WARN/System.err(288): at org.xmlrpc.android.XMLRPCClient.callXMLRPC(XMLRPCClient.java:387)
07-29 12:42:09.862: WARN/System.err(288): at org.xmlrpc.android.XMLRPCClient.call(XMLRPCClient.java:145)
07-29 12:42:09.872: WARN/System.err(288): at com.ttb.drupal.DrupalXmlRpcService.Connect(DrupalXmlRpcService.java:56)
07-29 12:42:09.872: WARN/System.err(288): at com.ttb.preset.onCreate(preset.java:47)
07-29 12:42:09.872: WARN/System.err(288): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
07-29 12:42:09.872: WARN/System.err(288): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
07-29 12:42:09.872: WARN/System.err(288): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
07-29 12:42:09.872: WARN/System.err(288): at android.app.ActivityThread.access$2200(ActivityThread.java:119)
07-29 12:42:09.882: WARN/System.err(288): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
07-29 12:42:09.882: WARN/System.err(288): at android.os.Handler.dispatchMessage(Handler.java:99)
07-29 12:42:09.882: WARN/System.err(288): at android.os.Looper.loop(Looper.java:123)
07-29 12:42:09.892: WARN/System.err(288): at android.app.ActivityThread.main(ActivityThread.java:4363)
07-29 12:42:09.892: WARN/System.err(288): at java.lang.reflect.Method.invokeNative(Native Method)
07-29 12:42:09.892: WARN/System.err(288): at java.lang.reflect.Method.invoke(Method.java:521)
07-29 12:42:09.892: WARN/System.err(288): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
07-29 12:42:09.892: WARN/System.err(288): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
07-29 12:42:09.902: WARN/System.err(288): at dalvik.system.NativeStart.main(Native Method)

I would be so grateful if you could help me!
Thank you!

Call drupal services methos in android

gitto's picture

this code function in java but not android , somebody anyone know why?, i need run in android

public class startactivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.submit);

    btn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            //TextView user = (TextView) findViewById(R.id.user);
            //TextView passw = (TextView) findViewById(R.id.password);
            try {
                llamar();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

public static void llamar() throws Exception {
    DrupalXmlRpcService service = new DrupalXmlRpcService(
            "midominio.com",
            "1b5b4d55a28dda2f603b49f70a6f50fbg",
            "http://midominio.com/services/xmlrpc");
    service.connect();
    service.login("user", "345678");
    DrupalNode node = new DrupalNode();
    node.setType(DrupalNode.TYPE_STORY);
    node.setTitle("Java code");
    node.setBody("insert java"); //+new Date().toGMTString());
    service.nodeSave(node);
    service.logout();
    System.out.println("done");

}

}

help in connecting phonegap with drupal site

himamurthy's picture

i have installed phonegap in android mobile now it need to connect to drupal site .
the app from android mobile inserting an text should automatical up date in drupal site in server .
any once can please send me the link

With Regard

Hima.J
Web Developer
CompIndia Pvt Ltd

Services

Group organizers

Group categories

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: