Simplest Drupal iPhone App

Maybe not the most simple, but pretty close. This app posts a node to Drupal Services using XML-RPC. We thought it would be useful for anyone interested in building an iPhone app that interacts with Drupal and wanted a basic starting point. It provides two text fields and a submit button. Enter some text and press submit and the fields get posted to the Drupal site (the URL is hardcoded into the app, not very useful).

On the Drupal end, you will need the Services, the XML-RPC server, and the Node Service modules enabled (these are all bundled with Services). In addition you will have to disable keys and sessid in Services (Site Building > Services > Settings). Also you have to allow anonymous access to services, and allow anonymous creation of story nodes (both in User management > Permissions). So you really do not want to do this on any kind of production site.

The iPhone calls the node.save method by posting the following XML:

<methodName>node.save</methodName>
<params>
<param><value><struct>
<member><name>type</name><value><string>story</string></value></member>
<member><name>title</name><value><string>title text</string></value></member>
<member><name>body</name><value><string>body text</string></value></member>
</struct></value></param>
</params></methodCall>

The iPhone app provides two ways to accomplish this. The first (commented out in the code) creates a HTTP POST request with the XML above as the body. This is not very useful since you have to provide the raw XML, but it shows how to execute an HTTP POST which is useful for many other things. The second uses XML-RPC functionality borrowed from the open-source wordpress app (which itself is a version of the Cocoa XML-RPC Framework ). This makes it much easier to formulate and execute XML-RPC calls, and the whole thing boils down to this:

    XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithHost:[NSURL URLWithString: @"http://192.168.1.14/drupal-6.6/services/xmlrpc"]];
NSMutableDictionary *postParams = [NSMutableDictionary dictionary];
[postParams setObject:@"story" forKey:@"type"];    
[postParams setObject:titleString forKey:@"title"];  // title input from iphone
[postParams setObject:bodyString forKey:@"body"];    // body input from iphone
[request setMethod:@"node.save" withObject:postParams];
XMLRPCResponse *nodeSaveResponse = [XMLRPCConnection sendSynchronousXMLRPCRequest:request];

The full app below. Developed with iPhone SDK 2.2.

AttachmentSize
simpleNodeTitleBody-XMLRPC.zip899.47 KB

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

I forgot....

And if I do:

[nodeSaveResponse object];

I get:

Error Domain=come.effigent.iphone.parseerror Code=-1 UserInfo=0x557a10 "Parse Error. Please check input values."

How to extrapolate the return value from the XML String????

Hi, sorry for my bad English I'm Learning it.
I've tried your code with my simple server. I do the call to the remote method and I get a response, but when I do:

[nodeSaveResponse source];

I receive a NSString that contains an XML with many tags and the return value of the remote method that I've called. Like this:

<?xml version="1.0" encoding="UTF-8"?>Ciriciao gente!!!!!! Sono il server!!!

How can extrapolate only the return value from this formatted string?

Good to have a central repository for this code

It would be great to have a central place to go for these apps...

APIkey SHA256 lead

Hey Owen-

Here's a link to a website which might do the trick for the APIkey SHA256 stuff.
http://hollowout.blogspot.com/2009/01/recovering-cisco-ipsec-vpn-group.html

Cheers!
Daniel

Attempt at the SHA256

does not work however :)

Any ideas? I got the code for converting from hex to string from here:

http://www.cocoabuilder.com/archive/message/cocoa/2007/11/29/194188

-(NSString *)getSignedKey {

unsigned char buffer[[self.key length] + 1];
buffer[[self.key length]] = '\0';
strncpy(buffer, [self.key UTF8String], [self.key length]);

NSString *inputString = [[NSString alloc] initWithFormat:@"%@;%@;%@;%@",self.timestamp, self.domain, self.nonce, self.method];

CC_SHA256([inputString UTF8String],[inputString lengthOfBytesUsingEncoding:NSUTF8StringEncoding],buffer);

NSData *hashedData = [NSData dataWithBytes:buffer length:32];

return [hashedData stringWithHexBytes];
}

SHA256 signing?

Anyone written any objC code for creating the signing key for the Services module using the HMAC-SHA256 algorithm?

I wrote it in Python and Actionscript already, but am new to ObjC and haven't cracked that one yet. Was wondering if anyone else already had :)

The CommonCrypto library

The CommonCrypto library might do it. also any regular C library that provides the functionality might be used without too much trouble.

Would you be willing to post the Python and Actionscript? (could be helpful in the Services handbook)

here's actionscript: function

here's actionscript:

function getAPIHash(domain:String,method:String,key:String,timestamp:String,nonce:String):String {

var SIGN_TEST = timestamp+ ';' + domain + ';' + nonce + ';' + method;

var priv_key = key;
var hmac = com.hurlant.crypto.Crypto.getHMAC('hmac-sha256');
var skey:ByteArray = com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(priv_key));
var dataArray:ByteArray = com.hurlant.util.Hex.toArray(com.hurlant.util.Hex.fromString(SIGN_TEST));
var sha1_sig_byte = hmac.compute(skey, dataArray);
var sig = com.hurlant.util.Hex.fromArray(sha1_sig_byte);

return sig;
}

requires the hurlant crypto library (http://code.google.com/p/as3crypto/)

Python:

import sys
import os
import xmlrpclib
from time import time
import hashlib
import hmac

"""
callAPI
server - xmlrpclib server object
domain - domain of the server allowed to use API (key lookup)
method - method name
key - preshared API key
arguments - list of arguments
@ result from XMLRPC method call
"""

def callAPI(server,domain,method,key,arguments):
timestamp = str(int(time()))
nonce = hashlib.md5(timestamp).hexdigest()
params = "%s;%s;%s;%s" % (timestamp,domain,nonce,method)
h = hmac.new(key,params,hashlib.sha256)
newparams = [h.hexdigest(),domain,timestamp,nonce] + arguments

#call the method that is in the callback
method = getattr(server,method)
# call with API key stored in original request
return method(*newparams)

I'm not a whiz at objc, so need some help writing the code for that :)

What do you guys think about

What do you guys think about making a http://groups.drupal.org/iPhone for sharing your example code?

People might even collaboratively build apps too!

I think this is a good idea.

I think this is a good idea.
There is a Drupal group that exists for the iPhone, and we have been trying to get the maintainer to change the name to just iPhone (right now it is http://groups.drupal.org/optimizing-themes-modules-iphone-use). You should go there and ask them to change the name to reach a wider audience !

Sure!

We have been trying to get the other iphone group's name changed, but so far we can't get the owner of the group to respond, so we may start a new group.

We also have http://drupal.org/project/iphone, which is for code collaboration and issue reporting...

Security

It is reasonably easy getting user-authenticated sessions working with Services, keeping everything wide open is really not a reasonable way to have things work. I have some code I can shoot you if you like, feel free to email me.

Authenticated sessions

Heyrocker, please post your code... or let me know how you can be emailed...

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options