Here is an xmlrpcConnector class for connecting to the xmlrpc server of the Services module.
It is not using Drupal methods making it usable from any non-Drupal websites as well.
It is using the XML-RPC for php library (version 3 beta) http://phpxmlrpc.sourceforge.net to download.

Change the require _once path at line 16 if you put the library in a different place (I put it just next to my file).
Don't forget to generate a key and set up the appropriate permissions for the methods you want to use.
I only coded methods for user manipulation so far, feel free to implement additional functionalities. The good thing is that it provides base functions that you can reuse to easily invoke Services methods.

Here is the class:

require_once('xmlrpc-3.0.0.beta/lib/xmlrpc.inc');

/**
 * 
 * @author arnaud
 * @version 1.0.0
 * 
 * A class providing a connection with the Drupal xmlrpc server
 * of the services module.
 * This class uses the library XML-RPC for PHP version 3.0.0.beta.
 * http://phpxmlrpc.sourceforge.net
 * 
 * Example: Login, create a new user, update its name, delete the user, logout.
 * 
 * require_once('xmlrpc_connector.inc');
 * 
 * $connector = new xmlrpcConnector('services/xmlrpc', 'my.drupal.website.com', 80, 'my_api_key', 'app_domain.com');
 * $connector->login('user', 'password');
 * $user_id = $connector->user_create('username', 'userpassword', 'email@email.com'); // this method returns the user id of the created user.
 * $connector->user_update($user_id, 'newname', 'newpass', 'newemail');
 * $connector->user_delete($user_id);
 * $connector->logout();
 */
class xmlrpcConnector {
  private $server;
  private $domain;
  private $app_domain;
  private $port;
  private $api_key;
  private $timestamp;
  private $nonce;
  private $hash;
  private $sessionId;
  private $userId;
  private $xmlrpc_client;
  
  /**
   * Instantiates an xmlrpcConnector
   * 
   * @param string $server Path to the xmlrpc server (usually: services/xmlrpc)
   * @param string $domain The domain of the Drupal website (mydomain.com)
   * @param int $port The port on which the server listens for conections (defaults to 80)
   * @param string $api_key The api key if necessary (not required for anonymous methods)
   * @param string $app_domain The authorized domain linked with the api key
   */
  public function __construct($server = null, $domain = null, $port = 80, $api_key = null, $app_domain = null) {
    if ($server == null || $domain == null) {
      throw new Exception('Invalid Constructor Arguments');
    }
    $this->server = $server;
    $this->domain = $domain;
    $this->port = $port;
    $this->api_key = $api_key;
    $this->app_domain = $app_domain;
    $this->connect();
  }

  /**
   * Login with a user account. Requires an api key and an athorized domain.
   * 
   * @param string $userName The user name.
   * @param string $password The password.
   */
  public function login($userName = null, $password = null) {
    if ($userName == null || $password == null) {
      throw new Exception('Illegal Arguments');
    } else if ($this->api_key == null) {
      die('Api key required to login');
    } else if ($this->app_domain == null) {
      die(' Application domain missing');
    }

    $this->setUpHash('user.login');
    $m = $this->getXmlrpcMsg('user.login', array(
        $this->hash,
        $this->app_domain,
        $this->timestamp,
        $this->nonce,
        $this->sessionId,
        $userName,
        $password));

    $r = $this->xmlrpc_client->send($m);

    if (!$r->faultCode()) {
      print 'logged in<br/>';
      $v = $r->value();

      //We only take the user id but there is more stuff
      $this->userId = $v['user']['uid'];

      // The session id changed during the login call
      $this->sessionId = $v['sessid'];
    } else {
      die(" An error occurred on user.login: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Logs out the user currently logged in.
   */
  public function logout() {
    $this->setUpHash('user.logout');
    
    $m = $this->getXmlrpcMsg('user.logout', array(
      $this->hash,
      $this->app_domain,
      $this->timestamp,
      $this->nonce,
      $this->sessionId
    ));
    
    $r = $this->xmlrpc_client->send($m);
    
    if (!$r->faultCode()) {
      print 'logged out<br/>';
    } else {
      die(" An error occurred on user.logout: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Deletes a user from the website. Requires to be logged in.
   * 
   * @param mixed $user_id The user id of the user to delete.
   */
  public function user_delete($user_id = null) {
    $this->setUpHash('user.delete');
    
    $m = $this->getXmlrpcMsg('user.delete', array(
      $this->hash,
      $this->app_domain,
      $this->timestamp,
      $this->nonce,
      $this->sessionId,
      $user_id
    ));
    
    $r = $this->xmlrpc_client->send($m);
    
    if (!$r->faultCode()) {
      print 'User ' . $user_id . ' deleted.<br/>';
    } else {
      die(" An error occurred on user.logout: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Creates a new user. Requires to be logged in.
   * 
   * @param string $name The name.
   * @param string $pass The password.
   * @param string $mail The e-mail address
   * @return string The user id of the created user.
   */
  public function user_create($name, $pass, $mail) {
    $this->setUpHash('user.save');
    
    $m = $this->getXmlrpcMsg('user.save', array(
      $this->hash,
      $this->app_domain,
      $this->timestamp,
      $this->nonce,
      $this->sessionId,
      array('name' => $name, 'pass' => $pass, 'mail' => $mail)
    ));
    
    $r = $this->xmlrpc_client->send($m);
    
    if (!$r->faultCode()) {
      print 'User ' . $name . ' created.<br/>';
      return $r->value();
    } else {
      die(" An error occurred on user.save: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Updates the account of a user.
   * 
   * @param mixed $user_id The user id used to identifie which user to modifie.
   * @param string $name New name
   * @param string $pass New password
   * @param string $mail New e-mail
   */
  public function user_update($user_id, $name, $pass, $mail) {
    $this->setUpHash('user.save');
    
    $m = $this->getXmlrpcMsg('user.save', array(
      $this->hash,
      $this->app_domain,
      $this->timestamp,
      $this->nonce,
      $this->sessionId,
      array('uid' => $user_id, 'name' => $name, 'pass' => $pass, 'mail' => $mail)
    ));
    
    $r = $this->xmlrpc_client->send($m);
    
    if (!$r->faultCode()) {
      print 'User updated. name: ' . $name . ' uid: ' . $user_id . '.<br/>';
    } else {
      die(" An error occurred on user.save: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Get a user from the website.
   * 
   * @param mixed $user_id The user id.
   * @return An array representing the Drupal user object.
   */
  public function user_get($user_id) {
    $this->setUpHash('user.get');
    
    $m = $this->getXmlrpcMsg('user.get', array(
      $this->hash,
      $this->app_domain,
      $this->timestamp,
      $this->nonce,
      $this->sessionId,
      $user_id
    ));
    
    $r = $this->xmlrpc_client->send($m);
    
    if (!$r->faultCode()) {
      print 'User ' . $user_id . ' fetched.<br/>';
      return $r->value();
    } else {
      die(" An error occurred on user.get: ".$r->faultString()."\n");
    }
  }

  public function __get($name) {
    return $this->$name;
  }

  public function __set($name, $value) {
    $this->$name = $value;
  }

  /**
   * Helper function.
   * Establishes the system connection to the website.
   */
  private function connect() {
    $m = $this->getXmlrpcMsg('system.connect');
    $this->xmlrpc_client =
      new xmlrpc_client($this->server,
                        $this->domain,
                        $this->port);

    $this->xmlrpc_client->return_type = 'phpvals';

    $r = $this->xmlrpc_client->send($m);
    if (!$r->faultCode()) {
      print 'connected<br/>';
      $v = $r->value();
      $this->sessionId = $v['sessid'];
    } else {
      die(" An error occurred on system.connect: ".$r->faultString()."\n");
    }
  }
  
  /**
   * Helper function
   * Transforms a method call with its argument into an xmlrpc message.
   * 
   * @param string $method The method to call
   * @param mixed $args The method arguments.
   */
  private function getXmlrpcMsg($method, $args = array()) {
    foreach ($args as &$argument) {
      if (is_array($argument)) {
        foreach ($argument as &$a) {
          $a = new xmlrpcval($a);
        }
        $argument = new xmlrpcval($argument, 'struct');
      } else {
        $argument = new xmlrpcval($argument);
      }
    }
    
    return new xmlrpcmsg($method, $args);
  }
  
  /**
   * Get the required hash code for a method.
   * 
   * @param string $method The method for which the hash code
   * is generated.
   */
  private function setUpHash($method) {
    $this->timestamp = (string) time();
    $this->nonce = uniqid('nonce_', true);
    
    $hash_parameters = array($this->timestamp,
                             $this->app_domain,
                             $this->nonce,
                             $method);
                             
    $this->hash = hash_hmac("sha256",
                            implode(';', $hash_parameters),
                            $this->api_key);
  }
}

Comments

hanish keloth’s picture

I have this error
connected
An error occurred on user.login : Invalid API key.

I used this code

require_once('xmlrpc_connector.php');

$connector = new xmlrpcConnector('webservice//services/xmlrpc', 'localhost', 80, '74a61ad7cd09022fe2a2e6bc83a45a72', 'localhost');
$connector->login('admin', '1234');
$user_id = $connector->user_create('username', 'userpassword', 'email@email.com'); // this method returns the user id of the created user.
$connector->user_update($user_id, 'newname', 'newpass', 'newemail');
$connector->user_delete($user_id);
$connector->logout();
arnaud.courbiere’s picture

Hey,

Sorry I have not been very reactive on this one (almost a year late :S).
Usually I was having this error when I was not setting my URLS correctly. Unfortunately, I don't have a setup right now to give an exact answer to any future visitor. However, here is a fresher version of this connector: https://github.com/ArnaudCourbiere/Drupal-XmlRpcConnector. Please feel free to fork, make pull requests etc to make it better.