Domic,
Thanks for the report, fixed in distributed version now.
Domic,
Thanks for the report, fixed in distributed version now.
Matt Dees
The function below uses the fast-mode option to call API2-functions:
$args is optional, when used it must be an labelled array, f.e. array('domain' => $domain)
Example:PHP Code:public function api2_query($username, $module, $function, $args = null) {
$call = array( user => $username,
cpanel_xmlapi_module => $module,
cpanel_xmlapi_func => $function,
cpanel_xmlapi_apiversion => '2');
if (is_array($args)) {
foreach($args as $tag => $data) {
$call[$tag] = $data;
}
}
return $this->xmlapi_query('cpanel', $call);
}
It's also possible to build general functions within' the class, f.e.PHP Code:$data = $xmlapi->api2_query($username, 'Park', 'listparkeddomains');
echo '<pre>';
print_r($data);
echo '</pre>';
Example:PHP Code:// This API function displays a list of all parked domains for a specific user.
public function listparkeddomains($username) {
if (!isset($username)) {
error_log("listparkeddomains requires that a username is passed to it");
return false;
}
return $this->api2_query($username, 'Park', 'listparkeddomains');
}
Note 1:PHP Code:$data = $xmlapi->listparkeddomains($username);
echo '<pre>';
print_r($data);
echo '</pre>';
This function replaces the current 'api2_query'-function and makes the 'build_api2_call()'-function unnessecary.
See fast mode for more information about fast mode for API1 en AP2 calls.
Note 2:
API1 en API2 functions always need the username of a hosting-account since these are cPanel-functions. This means that for some functions (f.e. park or unpark) a feature must be enabled for that account to be able to work. XML_API and JSON_API are WHM-functions (root).
Last edited by XenomediaBV; 09-03-2009 at 12:53 PM.
The 'xmlapi_query'-function uses the 'simplexml_load_string'-function. This function can stop executing your php-code if an error occures (f.e. the parameter of this function doesn't contain a valid xml-string).
Use the following code anywhere in your php-code to disable libxml errors:
See function.libxml-use-internal-errors for more information.PHP Code:// enable user error handling
libxml_use_internal_errors(true);
Last edited by XenomediaBV; 09-03-2009 at 08:51 AM.
This version of the 'xmlapi_query'-function uses a 'unserialize_xml'-function, which I found on php.net. It converts a SIMPLEXML OBJ into a usefull array. This way the result of the 'xmlapi_query'-function can be used directly within' your php-code.
PHP Code:public function xmlapi_query($function, $calls = array()) {
if (!$function) {
error_log("xmlapi_query() requires a function to be passed to it");
return;
}
$args = http_build_query($calls,'','&');
$query = $this->protocol . $this->host . ":" . $this->port . "/xml-api/" . $function . "?" . $args;
if ($this->debug) {
print "\n\nQUERY:\n" . $query . "\n\n";
}
curl_setopt($this->curl, CURLOPT_URL, $query);
$result = curl_exec($this->curl);
if ($result == false) {
error_log("curl_exec threw error \"" . curl_error($this->curl) . "\" for $query");
}
if ($this->return_xml) {
if ($this->debug) {
print "RAW XML:\n\n$result\n\n";
}
return $result;
}
else {
if ($this->debug) {
print "RAW XML:\n\n$result\n\n";
$dump = simplexml_load_string($result);
if ($dump) {
print "SIMPLEXML OBJ:\n\n" . var_dump($dump) . "\n\n";
}
}
return $this->unserialize_xml($result);
}
}
/* bool/array unserialize_xml ( string $input [ , callback $callback ] )
* Unserializes an XML string, returning a multi-dimensional associative array, optionally runs a callback on all non-array data
* Returns false on all failure
* Notes:
* Root XML tags are stripped
* Due to its recursive nature, unserialize_xml() will also support SimpleXMLElement objects and arrays as input
* Uses simplexml_load_string() for XML parsing, see SimpleXML documentation for more info
*/
private function unserialize_xml($input, $callback = null, $recurse = false) {
// Get input, loading an xml string with simplexml if its the top level of recursion
$data = ( (!$recurse) && is_string($input) ) ? simplexml_load_string($input) : $input;
// Convert SimpleXMLElements to array
if ($data instanceof SimpleXMLElement) {
$data = (array) $data;
}
// Recurse into arrays
if (is_array($data)) {
foreach ($data as &$item) {
$item = $this->unserialize_xml($item, $callback, true);
}
}
// Run callback and return
return (!is_array($data) && is_callable($callback))? call_user_func($callback, $data): $data;
}
PHP Code:// This API function allows you to change bandwidth limits for cPanel accounts.
public function limitbw($username, $bwlimit) {
if (!isset($username) || !isset($bwlimit)) {
error_log("limitbw requires that an username and bwlimit are passed to it");
return false;
}
return $this->xmlapi_query('limitbw', array('user' => $username, 'bwlimit' => $bwlimit));
}
PHP Code:// This API function allows you to edit a user's disk space quota.
public function editquota($username, $quota) {
if (!isset($username) || !isset($quota)) {
error_log("editquota requires that an username and quota are passed to it");
return false;
}
return $this->xmlapi_query('editquota', array('user' => $username, 'quota' => $quota));
}
Usage changepackage:PHP Code:// This API function allows you to change the hosting plan associated with a cPanel account.
public function changepackage($username, $pkg) {
if (!isset($username) || !isset($pkg)) {
error_log("changepackage requires that an username and pkg are passed to it");
return false;
}
return $this->xmlapi_query("changepackage", array('user' => $username, 'pkg' => $pkg));
}
PHP Code:$data = $xmlapi->changepackage('username', 'somepkgname');
Usage listftpwithdisk:PHP Code:// This API2 function allows you to list ftp-users associated with a cPanel account.
public function listftpwithdisk($username) {
if (!isset($username)) {
error_log("listftpwithdisk requires that username is passed to it");
return false;
}
return $this->api2_query($username, 'Ftp', 'listftpwithdisk');
}
PHP Code:$data = $xmlapi->listftpwithdisk('username');
Usage getdiskusage:PHP Code:// This API2 function allows you to view the diskusage of a emailaccount.
// $args = array('domain' => $email_domain, 'login' => $email_username)
public function getdiskusage($username, $args) {
if (!isset($username) || !isset($args)) {
error_log("getdiskusage requires that an username and args are passed to it");
return false;
}
if (is_array($args) && (!isset($args['domain']) || !isset($args['login']))) {
error_log("getdiskusage requires that args at least contains an email_domain and email_username");
return false;
}
return $this->api2_query($username, 'Email', 'getdiskusage', $args);
}
PHP Code:$data = $xmlapi->getdiskusage('username', array('domain' => 'example.com', 'login' => 'johndo'));
Last edited by XenomediaBV; 09-03-2009 at 12:50 PM.
The function below uses the fast-mode option to call API1-functions:
$args is optional, when used it must be an basic array, f.e. array('username', 'password')
The entries of the $args array must be in the defined sequence (see documentation API1)!
For example:PHP Code:public function api1_query($username, $module, $function, $args = null) {
$call = array( user => $username,
cpanel_xmlapi_module => $module,
cpanel_xmlapi_func => $function,
cpanel_xmlapi_apiversion => '1');
if (is_array($args)) {
foreach($args as $key => $data) {
$call['arg-' . $key] = $data;
}
}
return $this->xmlapi_query('cpanel', $call);
}
Usage:PHP Code:// This API1 function adds a pop-account for a specific user.
// The addpop call can only create a mailbox for the main domain,
// Parked domains are replaced by the main domain
public function addpop($username, $args) {
if (!isset($username) || !isset($args)) {
error_log("addpop requires that a username and args are passed to it");
return false;
}
return $this->api1_query($username, 'Email', 'addpop', $args);
}
Note 1:PHP Code:$data = $xmlapi->addpop($username, array('email_username', 'somepass', 'domain.tld'));
echo '<pre>';
print_r($data);
echo '</pre>';
This function replaces the current 'api1_query'-function and makes the 'build_api1_call()'-function unnessecary.
See fast mode for more information about fast mode for API1 en AP2 calls.
Note 2:
API1 en API2 functions always need the username of a hosting-account since these are cPanel-functions. This means that for some functions (f.e. park or unpark) a feature must be enabled for that account to be able to work. XML_API and JSON_API are WHM-functions (root).
Last edited by XenomediaBV; 09-03-2009 at 02:56 PM.
Attachment removed.
Last edited by XenomediaBV; 09-12-2009 at 01:59 PM. Reason: Attachment removed
Hey XenomediaBV,
Thanks for your contributions they are quite useful!
Just a little fix of sorts, my php installations were complaining of undefined constants in your api2_query function. Simple fix, just quote the array keys as so:
I also added some ugly error catching code to the xmlapi_query function, I should probably fix this up and make it more robust but it's working for me now:PHP Code:public function api2_query($username, $module, $function, $args = null) {
$call = array( 'user' => $username,
'cpanel_xmlapi_module' => $module,
'cpanel_xmlapi_func' => $function,
'cpanel_xmlapi_apiversion' => '2');
if (is_array($args)) {
foreach($args as $tag => $data) {
$call[$tag] = $data;
}
}
return $this->xmlapi_query('cpanel', $call);
}
PHP Code:if(stristr($result, '<html>') == TRUE) {
if(stristr($result, 'Login Attempt Failed') == TRUE) {
return 'Login Failed';
}
if(stristr($result, 'action="/login/"') == TRUE) {
return 'Authentication Error';
}
return 'Error';
}
Thank you crshman. I noticed I forgot those quotes. It has been fixed in the new package (see previous post) along with the logon detection you provided.
If any of you use CakePHP I made a nice little component that makes extensive use of Miquel's work
You can grab a copy here:
rnavarro's CakePHP-cPanel-API-Component at master - GitHub
Thanks indeed xenomediabv, I'm glad that releasing this under an open source license is starting to pay off!
Hopefully we'll be moving to a source code management base of some type or another fairly soon here.
Matt Dees
First of all, thanks so much for all this great work. Much appreciated!
The park function isn't working for me either. I'm testing it on a HostGator reseller account accessed from another server. I can get it to listparkeddomains just fine. I've double checked that the specific account I'm targeting does have the parked domain feature turned on and that there are unused parked domains available. I can manually park and unpark on that account all day using WHM or cPanel.
But when I try to park a new domain using the API example, I get the following live output:
I've noted two interesting features of this message. One, it correctly lists another domain (domainalreadyparked.com) that is actually already parked on the test domain. Two, it seems to be repeating the target domain twice in the string mytestdomain.com.mytestdomain.com. That repetition seems wrong.Array
(
[apiversion] => 2
[data] => Array
(
[reason] => Error from park wrapper: The following domain is not configured for this account: mytestdomain.com.mytestdomain.com: @Cpanel:: DOMAINS=(mytestdomain.com,domainalreadyparked.com)
[result] => 0
)
[func] => park
[module] => Park
)
I traced the array being passed to the xmlapi_query function by the api2_query function in this instance. Here it is:
That looks right to me. I don't know where to take this next. Has anyone gotten Park to work correctly?Array ( [user] => username [cpanel_xmlapi_module] => Park [cpanel_xmlapi_func] => park [cpanel_xmlapi_apiversion] => 2 [domain] => newdomaintopark.com [topdomain] => mytestdomain.com )
Thanks!
Steve
Follow up on previous post. When I change the park function wrapper in xmlapi.php to use api1 instead of api2, the example code works just fine. In other words, I modified one character in the next-to-last line of this code block:
Now it reads:PHP Code:public function park($username, $newdomain, $topdomain) {
$args = array();
if ( (!isset($username)) && (!isset($newdomain)) && (!isset($topdomain)) ) {
error_log("park requires that a username, new domain and topdomain are passed to it");
return false;
}
$args['domain'] = $newdomain;
$args['topdomain'] = $topdomain;
return $this->api2_query($username, 'Park', 'park', $args);
}
FWIW, my WHM version information reads:PHP Code:public function park($username, $newdomain, $topdomain) {
$args = array();
if ( (!isset($username)) && (!isset($newdomain)) && (!isset($topdomain)) ) {
error_log("park requires that a username, new domain and topdomain are passed to it");
return false;
}
$args['domain'] = $newdomain;
$args['topdomain'] = $topdomain;
return $this->api1_query($username, 'Park', 'park', $args);
}
I hope this helps someone. I believe I saw earlier posts indicating that it didn't work with api2 or api1, but I decided to try it anyway.Code:cPanel 11.24.5-R38506 - WHM 11.24.2 - X 3.9
Steve
Steve,
Thank you. I ran some tests using API1 in the "park" function on 2 accounts, one with and one without the park feature enabled. Indeed now it's working, but only on the "park"-feature enabled account. I already have a reply for the feature request to enable park and unpark globally for root/resellers no matter the account settings.
I also tested the "unpark" function and again this also works using API1 instead of 2.
----
New version attached. Note the doc. menu for API1 & API2 contains all the functions I could find in the documentation, however most of them are not yet implemented. To be continued...
Last edited by XenomediaBV; 09-12-2009 at 02:11 PM.
Hello there. When I use the above code, it gives me an unexpected variable parse error on print "Creation Failed:" $result->result->statusmsg; line. When I remove the $result->result->statusmsg; part from that line, it works. However, regardless of whether the email account creation was successful, it always prints "Creation Failed". I look in the cPanel email account list and the accounts appear.
Using $xmlapi->set_debug(1); sort of returns what I want. On a successful account creation, it returns something like this:
RAW API1 CALL: Emailaddpop1<username><password><domain> RAW XML: <insert a bunch of other string and function stuff that looks completely ugly.
However, if the account exists, it will show something along the lines of:
RAW API1 CALL: Emailaddpop1<username><password><domain> RAW XML: Emailaddpopeventinternal1Sorry the account <username>@<domain> already exists! object(SimpleXMLElement)#2 (6) { ["module"]=> string(5) "Email" ["func"]=> string(6) "addpop" ["type"]=> string(5) "event" ["source"]=> string(8) "internal" ["apiversion"]=> string(1) "1" ["data"]=> object(SimpleXMLElement)#3 (1) { ["result"]=> string(58) "Sorry the account <username>@<domain> already exists! " } } SIMPLEXML OBJ:
While that does give insight on whether or not the account was created, I would prefer it to leave out all that unnecessary junk and just say "Email account <username>@<domain> created successfully", or "Sorry the account <username>@<domain> already exists! or whatever it's reason for rejecting the email account creation was. I basically just want to strip everything but the result that anyone could understand (either it was created or it wasn't and why it wasn't). How can I go about doing this?
Next one is... How can I add a quota in to the email account? Using this, it creates the email account with an unlimited quota.
Last edited by sleisysoft; 09-16-2009 at 04:20 PM.