· 3 years ago · Dec 20, 2021, 09:50 PM
1<?php
2/**
3 * cpanel - /usr/local/cpanel/php/cpanel.php Copyright(c) 2020 cPanel, L.L.C.
4 * All rights reserved.
5 * copyright@cpanel.net http://cpanel.net
6 */
7if (version_compare(PHP_VERSION, '5.2.0', '<')) {
8 trigger_error(
9 "cPanel's Live PHP class must be executed with PHP >= 5.2",
10 E_USER_ERROR
11 );
12 exit;
13}
14/** cPanel LiveAPI PHP Class
15 *
16 * This class allows for cPanel frontend pages to be developed in PHP using an
17 * object for accessing the APIs.
18 * For the full documentation, see https://go.cpanel.net/livephp.
19 *
20 * You are free to include this module in your program as long as it is for use
21 * with cPanel. This module is only licensed for use with the version of cPanel
22 * it is distributed with.
23 *
24 * The backend APIs are subject to change. If you ignore this message you will
25 * find that this module will not work in future versions. This class will be
26 * updated if the backend APIs change. We will make all efforts to provide
27 * backwards compatibility, but if you do use this class with any version
28 * of cPanel other than the one it is distributed with the results could be
29 * disasterous.
30 *
31 * FOR THE AVOIDANCE OF DOUBT: MAKE SURE YOU ONLY USE THIS MODULE WITH THE
32 * VERSION OF CPANEL THAT IT CAME WITH
33 *
34 * For debugging purposes you can set the following two constants to enable
35 * debug mode:
36 * - LIVEPHP_DEBUG_LEVEL - 0 or 1 - enable or disable debugging
37 * - LIVEPHP_DEBUG_LOG - path - The absolute path and filename for logging.
38 *
39 * This class also provides a set_debug() method for enabling/disabling debug
40 * mode.
41 *
42 * Changes:
43 * Version 2.1
44 * - Corrected various documentation
45 * - Altered code and documentation for better adherence to PEAR PHP coding
46 * standards without breaking BC
47 * - Fixed bug (constructor should explicitly return "$this")
48 * - Altered methods cpanelif() and cpanelfeature() to enforce a boolean
49 * return.
50 * - Implemented the use pre-defined SPL Exception classes instead of generic
51 * Exception base class
52 * - Use Exceptions wherever possible instead of simple log via error_log()
53 * - Use trigger_error() instead of error_log()
54 * - Use error_log() only when logging cPanel related information (i.e., what
55 * normally might be E_NOTICE or E_DEPRECATED, but specific only to cPanel
56 * technicians and developers)
57 * - Added 'deprecated' PHP DocBlock to cpanellangprint(). (future versions
58 * will likely throw E_USER_DEPRECATED; instead use
59 * API1's Locale::maketext() (## no extract maketext)
60 *
61 * Version 2.0
62 * - Changed the backend serialization format to JSON
63 * - Added debug logger
64 * - Added Exceptions
65 *
66 * @category Cpanel
67 * @package CPANEL
68 * @author cPanel, Inc. <copyright@cpanel.net>
69 * @copyright 1997-2020 cPanel, L.L.C.
70 * @license http://cpanel.net
71 * @version Release: 2.1
72 * @link https://go.cpanel.net/livephp
73 */
74class CPANEL
75{
76 /**
77 * Socket resource for communicating with cPanel LiveAPI parser
78 * @var resource Local socket
79 */
80 private $_cpanelfh;
81 /**
82 * State tracker for socket resource
83 * @var boolean State of private resource
84 */
85 public $connected = 0;
86 /**
87 * Absolute path and filename of debug log
88 *
89 * NOTE: If LIVEPHP_DEBUG_LOG environment variable is not set, this variable
90 * will be populated with a random log file (if debugging is enabled):
91 * ~/.cpanel/livephp.log.$randomstring.
92 *
93 * @var string Log file
94 */
95 private $_debug_log;
96 /**
97 * File handle for debug log
98 * @var resource File handle for debug log
99 */
100 private $_debug_fh;
101 /**
102 * Debug logging level
103 *
104 * Value values are:
105 * 0 - Debugging disabled
106 * 1 - Log all socket communication to debug log file
107 *
108 * @var integer Debug logging level
109 */
110 private $_debug_level = 0;
111 /**
112 * Storage location for last server response
113 * @var array Array data structure of the last server response
114 */
115 private $_result;
116
117 /**
118 * Storage location for a stringified DOM as used by the header() and footer() methods
119 * note: modern themes only
120 */
121 private $_dom = 0;
122 /**
123 * Instantiate the LiveAPI PHP Object
124 *
125 * This will create the "CPANEL" object; open the communication socket.
126 *
127 * @return CPANEL A LiveAPI object
128 * @throws RuntimeException if CPANEL_PHPCONNECT_SOCKET environment variable
129 * is not set
130 * @throws RuntimeException if file socket cannot be established
131 * @throws RuntimeException if stream blocking cannot be set for file socket
132 */
133 public function __construct()
134 {
135 $this->connected = 1;
136 // Attempt to set debugging based on defined PHP constants
137 if (defined('LIVEPHP_DEBUG_LOG')) {
138 $this->_debug_log = LIVEPHP_DEBUG_LOG;
139 }
140 if (defined('LIVEPHP_DEBUG_LEVEL')) {
141 $this->set_debug(LIVEPHP_DEBUG_LEVEL);
142 }
143 // prepare socket to communicate with cPanel API parser
144 $socketfile = getenv('CPANEL_PHPCONNECT_SOCKET');
145 if (!$socketfile) {
146 throw new RuntimeException(
147 'There was a problem fetching the env variable'
148 . 'containing the path to the socket'
149 );
150 }
151 $this->_cpanelfh = fsockopen("unix://" . $socketfile);
152 if (!$this->_cpanelfh) {
153 $this->connected = 0;
154 throw new RuntimeException(
155 'There was a problem connecting back to the cPanel engine.'
156 .' Make sure your script ends with .live.php or .livephp'
157 );
158 }
159 stream_set_blocking($this->_cpanelfh, 1) || $this->connected = 0;
160 if (!$this->connected) {
161 throw new RuntimeException(
162 'There was a problem connecting back to the cPanel engine.'
163 .' Make sure your script ends with .live.php or .livephp'
164 );
165 }
166 // enable enbedded json in the protocol
167 $this->exec('<cpaneljson enable="1">');
168 return $this;
169 }
170 /**
171 * Enable debugging mode
172 *
173 * Passing this a non-zero value will enable socket logging.
174 *
175 * NOTE: This should only be used when attempting to debug the transactions
176 * that happen over the socket. ALL data will be log!
177 *
178 * The valid logging level are as follows:
179 * 0 - Disable logging (default)
180 * 1 - Write socket transactions to the log.
181 *
182 * @param int $debug_level The debug level
183 *
184 * @return void
185 * @throws UnexpectedValueException if $debug_level is not numeric
186 */
187 public function set_debug($debug_level)
188 {
189 if (is_numeric($debug_level)) {
190 // Open the debug log if it isn't already
191 if ($debug_level > 0 && !is_resource($this->_debug_fh)) {
192 // Set the debug log
193 if (!isset($this->_debug_log)) {
194 $user_pwnam = posix_getpwuid(posix_getuid());
195 $this->_debug_log = $user_pwnam['dir']
196 . '/.cpanel/livephp.log.' . mt_rand(10000000, 99999999);
197 }
198 $this->_debug_fh = fopen($this->_debug_log, 'a');
199 } elseif (is_resource($this->_debug_fh) && $debug_level == 0) {
200 // Close debug_log if debug logging is being disabled
201 fclose($this->_debug_fh);
202 }
203 $this->_debug_level = $debug_level;
204 } else {
205 $this->set_debug(0);
206 throw new UnexpectedValueException(
207 'CPANEL::set_debug given non-integer value.'
208 );
209 }
210 }
211 /**
212 * Write to the debug log
213 *
214 * Write a message to the debug log
215 *
216 * @param int $level The desired logging level for $log_msg to appear.
217 * @param string $log_msg The message you wish to have logged
218 *
219 * @return void
220 * @throws RuntimeException if log filehandle does not exist
221 */
222 private function debug_log($level, $log_msg)
223 {
224 if ($level > 0 && $level <= $this->_debug_level) {
225 if (is_resource($this->_debug_fh)) {
226 fwrite($this->_debug_fh, date("[d-M-Y H:i:s] ") . $log_msg . "\n");
227 } else {
228 throw new RuntimeException(
229 'Attempted to execute debugging statement on closed filehandle'
230 );
231 }
232 }
233 }
234
235 /**
236 * Parse and log a JSON formatted string.
237 *
238 * @param string $str JSON formatted string to decode and log
239 *
240 * @return void
241 */
242 private function debug_log_json($str)
243 {
244 $parsed = json_decode($str, true);
245 if ($parsed !== null) {
246 ob_start();
247 var_dump($parsed);
248 $log_msg = ob_get_clean();
249 } elseif (function_exists('json_last_error') && json_last_error()) {
250 // json_last_error is only PHP>=5.3
251 switch (json_last_error()) {
252 case JSON_ERROR_DEPTH:
253 $log_msg = 'Maximum stack depth exceeded';
254 break;
255 case JSON_ERROR_CTRL_CHAR:
256 $log_msg = 'Unexpected control character found';
257 break;
258 case JSON_ERROR_SYNTAX:
259 $log_msg = 'Syntax error, malformed JSON';
260 break;
261 case JSON_ERROR_NONE:
262 //do nothing;
263 break;
264 }
265 } else {
266 $log_msg = "Error decoding JSON string";
267 }
268 $this->debug_log(1, 'JSON_decode: ' . $log_msg);
269 }
270 /**
271 * Get the filename of the debug log currently in use.
272 *
273 * @return string Current log file
274 */
275 public function get_debug_log()
276 {
277 return $this->_debug_log;
278 }
279 /**
280 * Return the currently set debug level.
281 *
282 * @return int Current debug level
283 */
284 public function get_debug_level()
285 {
286 return $this->_debug_level;
287 }
288 /**
289 * Return the value of a cPvar
290 *
291 * @param string $var The cPvar to fetch (e.g. $CPDATA{'DNS'} )
292 *
293 * @return array Response containing cPvar value
294 */
295 public function fetch($var)
296 {
297 if (!$this->connected) {
298 return;
299 }
300 return $this->exec('<cpanel print="' . $var . '">');
301 }
302 /**
303 * Execute an API1 call
304 *
305 * @param string $module An API1 module name.
306 * @param string $func An API1 function.
307 * @param array $args An ordinal array containing arguments for the API1 function
308 *
309 * @return array Returned response from the API1 function
310 * @throws RuntimeException if LiveAPI socket is not available
311 */
312 public function api1($module, $func, $args = array())
313 {
314 if (!$this->connected) {
315 throw new RuntimeException(
316 'The LiveAPI PHP socket has closed, unable to continue.'
317 );
318 }
319 return $this->api('exec', "1", $module, $func, $args);
320 }
321 /**
322 * Execute an API2 call
323 *
324 * @param string $module An API2 module name.
325 * @param string $func An API2 function.
326 * @param array $args An associative array containing arguments for the API2 function
327 *
328 * @return array Returned response from the API2 function
329 * @throws RuntimeException if LiveAPI socket is not available
330 */
331 public function api2($module, $func, $args = array())
332 {
333 if (!$this->connected) {
334 throw new RuntimeException(
335 'The LiveAPI PHP socket has closed, unable to continue.'
336 );
337 }
338 return $this->api('exec', "2", $module, $func, $args);
339 }
340 /**
341 * Execute an API3 call, an alias for a UAPI call
342 *
343 * @param string $module A UAPI module name.
344 * @param string $func A UAPI function.
345 * @param array $args An associative array containing arguments for the UAPIfunction
346 *
347 * @return array Returned response from the UAPI function
348 * @throws RuntimeException if LiveAPI socket is not available
349 */
350 public function api3($module, $func, $args = array())
351 {
352 if (!$this->connected) {
353 throw new RuntimeException(
354 'The LiveAPI PHP socket has closed, unable to continue.'
355 );
356 }
357 return $this->api('exec', "3", $module, $func, $args);
358 }
359 /**
360 * Execute an UAPI call
361 *
362 * @param string $module A UAPI module name.
363 * @param string $func A UAPI function.
364 * @param array $args An associative array containing arguments for the UAPIfunction
365 *
366 * @return array Returned response from the UAPI function
367 * @throws RuntimeException if LiveAPI socket is not available
368 */
369 public function uapi($module, $func, $args = array())
370 {
371 if (!$this->connected) {
372 throw new RuntimeException(
373 'The LiveAPI PHP socket has closed, unable to continue.'
374 );
375 }
376 return $this->api('exec', "uapi", $module, $func, $args);
377 }
378 /**
379 * Evaluate a cpanelif statement
380 *
381 * This method will return a boolean value based on the evaluation of the
382 * code expression
383 *
384 * @param string $code A cPvar or logical test condition
385 *
386 * @link https://go.cpanel.net/PluginVars ExpVar Reference Chart
387 * @return boolean Whether the $code expression evaluates as true or false
388 * @throws RuntimeException if LiveAPI socket is not available
389 */
390 public function cpanelif($code)
391 {
392 if (!$this->connected) {
393 throw new RuntimeException(
394 'The LiveAPI PHP socket has closed, unable to continue.'
395 );
396 }
397 $value = (simple_result($this->api('if', '1', 'if', 'if', $code)))? 1 : 0;
398 return $value;
399 }
400 /**
401 * Determine if the current cPanel account has access to a specific feature
402 *
403 * @param string $feature A feature name
404 *
405 * @return boolean Whether the current cPanel account has access to queried
406 * feature.
407 * @throws RuntimeException if LiveAPI socket is not available
408 */
409 public function cpanelfeature($feature)
410 {
411 if (!$this->connected) {
412 throw new RuntimeException(
413 'The LiveAPI PHP socket has closed, unable to continue.'
414 );
415 }
416 $value = (simple_result($this->api('feature', '1', 'feature', 'feature', $feature)))? 1 : 0;
417 return $value;
418 }
419 /**
420 * Return the value of a cPvar
421 *
422 * This method will return the value of a cPvar. This differs from fetch()
423 * which returns the complete response as an array. The method will only
424 * return the cPvar value as a string.
425 *
426 * @param string $var The cPvar to retrieve (e.g. $CPDATA{'DNS'} )
427 *
428 * @return string The value of the queried cPvar
429 * @throws RuntimeException if LiveAPI socket is not available
430 */
431 public function cpanelprint($var)
432 {
433 if (!$this->connected) {
434 throw new RuntimeException(
435 'The LiveAPI PHP socket has closed, unable to continue.'
436 );
437 }
438 return simple_result($this->api1('print', '', $var));
439 }
440 /**
441 * Process a language key for the cPanel account's current language
442 *
443 * @param string $key A language key
444 *
445 * @deprecated The cpanellongprint tag is no longer supported. Use API1
446 * Locale::maketext ## no extract maketext
447 * @see https://go.cpanel.net/maketext ## no extract maketext
448 *
449 * @return string Translated version of the requested language key
450 * @throws RuntimeException if LiveAPI socket is not available
451 */
452 public function cpanellangprint($key)
453 {
454 if (!$this->connected) {
455 throw new RuntimeException(
456 'The LiveAPI PHP socket has closed, unable to continue.'
457 );
458 }
459 return simple_result($this->api1('langprint', '', $key));
460 }
461 /**
462 * Execute a cpanel tag
463 *
464 * In most cases there is no need to call this method directly. Instead one
465 * should use the api1(), api2() or cpanel*() methods (which all call this
466 * method internally).
467 *
468 * @param string $code A cPanel tag to execute.
469 * @param boolean $skip_return (optional) If set to true, this function will
470 * not return anything.
471 *
472 * @return array Returned response in an array data structure
473 * @throws RuntimeException if LiveAPI socket is not available
474 */
475 public function exec($code, $skip_return = 0)
476 {
477 if (!$this->connected) {
478 throw new RuntimeException(
479 'The LiveAPI PHP socket has closed, unable to continue.'
480 );
481 }
482 // SEND CODE
483 $buffer = '';
484 $result = '';
485 if ($this->_debug_level) {
486 $this->debug_log(1, '(exec) SEND:' . $code);
487 }
488 fwrite($this->_cpanelfh, strlen($code) . "\n" . $code);
489 //RECV CODE
490 while ($buffer = fgets($this->_cpanelfh)) {
491 $result = $result . $buffer;
492 if (strstr($buffer, '</cpanelresult>') !== false) {
493 break;
494 }
495 }
496 if ($this->_debug_level) {
497 $this->debug_log(1, '(exec) RECV:' . $result);
498 }
499 if ($skip_return) {
500 $this->_result = null;
501 return;
502 }
503 // Parse out return code, build livePHP result
504 $json_start_pos = strpos($result, "<cpanelresult>{");
505 if ($json_start_pos !== false) {
506 $json_start_pos+= 14;
507 if ($this->_debug_level) {
508 $this->debug_log_json(
509 substr(
510 trim($result),
511 $json_start_pos,
512 strpos(
513 $result,
514 "</cpanelresult>"
515 ) - $json_start_pos
516 )
517 );
518 }
519 $parsed = json_decode(
520 substr(
521 trim($result),
522 $json_start_pos,
523 strpos(
524 $result,
525 "</cpanelresult>"
526 ) - $json_start_pos
527 ),
528 true
529 );
530 if (strpos($result, '<cpanelresult>{"cpanelresult"') === false
531 && $parsed !== null
532 ) {
533 /**
534 * needed for compat: API2 tags will end up with both due to
535 * the internals
536 */
537 $this->_result = array('cpanelresult' => $parsed);
538 } else {
539 $this->_result = $parsed;
540 }
541 } elseif (strpos($result, "<cpanelresult></cpanelresult>") !== false) {
542 /* This is a hybird api1/api2/api3 response to ensure that
543 the developer using api gets the error field in the position
544 they are looking for */
545 $this->_result = array('cpanelresult' => array('error' => 'Error cannot be propagated to liveapi, please check the cPanel error_log.', 'result' => array('errors' => array('Error cannot be propagated to liveapi, please check the cPanel error_log.'))));
546 } elseif (strpos($result, "<cpanelresult>") !== false) {
547 /**
548 * This logic flow is provide for BC in the unlikely event that the
549 * cPanel engine doesn't not handle JSON.
550 * - log this directly to the PHP error log in hopes that it gets
551 * reported
552 */
553
554 if ($this->_debug_level) {
555 $this->debug_log(1, 'XML_unserialize:' . $result);
556 }
557 error_log(
558 'cPanel LiveAPI parser returned XML, which is deprecated. '
559 .'Please file a bug report at https://tickets.cpanel.net/submit/'
560 );
561 include_once '/usr/local/cpanel/php/xml.php';
562 # XML_unserialize takes a reference, and PHP doesn't like it if we
563 # pass a non-variable by reference.
564 $temp = trim($result);
565 $this->_result = XML_unserialize($temp);
566 }
567 return $this->_result;
568 }
569 /**
570 * Execute an API call
571 *
572 * In most cases there is no need to call this method directly. Instead one
573 * should use the api1(), api2() or cpanel*() methods (which all call this
574 * method, or exec(), internally).
575 *
576 * @param string $reqtype The type of request used by the cPanel API parser;
577 * valid values are 'exec', 'feature' or 'if'
578 * @param int $version The version of the API; valid values are either
579 * '1' or '2'
580 * @param string $module An API module name
581 * @param string $func An API function name
582 * @param mixed $args Associate array for API2, ordered array for API1,
583 * string for non exec $reqtypes
584 *
585 * @see api1()
586 * @see api2()
587 * @return array Returned response in an array data structure
588 * @throws RuntimeException if LiveAPI socket is not available
589 */
590 public function api($reqtype, $version, $module, $func, $args = array())
591 {
592 if (!$this->connected) {
593 throw new RuntimeException(
594 'The LiveAPI PHP socket has closed, unable to continue.'
595 );
596 }
597 $input = array(
598 "module" => $module,
599 "reqtype" => $reqtype,
600 "func" => $func,
601 "apiversion" => $version
602 );
603
604 // Args may actually be a string instead of an array.
605 // yay for php 4.x-isms which would automagically turn 'string' into array('string') when accessed as array
606 // As such, just check that it isn't empty instead of doing count()
607 if (!empty($args)) {
608 $input['args'] = $args;
609 }
610 /**
611 * cPanel engine can process the JSON much much faster than XML
612 */
613 if (function_exists('json_encode')) {
614 $code = "<cpanelaction>\n" . json_encode($input) . "\n</cpanelaction>";
615 } else {
616 /**
617 * This logic flow is provide for BC in the unlikely event that the
618 * cPanel engine doesn't not handle JSON.
619 * - log this directly to the PHP error log in hopes that it gets
620 * reported
621 */
622 error_log(
623 'cPanel LiveAPI parser returned XML, which is deprecated. '
624 .'Please file a bug report at https://tickets.cpanel.net/submit/'
625 );
626 include_once '/usr/local/cpanel/php/xml.php';
627 $temp = array("cpanelaction" => array($input));
628 $code = XML_serialize($temp);
629 }
630 return $this->exec($code);
631 }
632 /**
633 * Get the data result node of the last call
634 *
635 * This method will return the ['cpanelresult']['data']['result'] node from
636 * the last call that was made.
637 *
638 * @return mixed A string if the last call was API1, an array or array of
639 * associative arrays if the last call was API2
640 * @throws UnexpectedValueException if no data is available from a previous
641 * call
642 * @throws OutOfBoundsException if previous data response does not contain
643 * proper hierarchy
644 *
645 */
646 public function get_result()
647 {
648 if ( !$this->_result ) {
649 throw new UnexpectedValueException('No previous result exists');
650 }
651 if (!is_array($this->_result)
652 || !is_array($this->_result['cpanelresult'])
653 || !is_array($this->_result['cpanelresult']['data'])
654 ) {
655 throw new OutOfBoundsException(
656 'cpanelresult->data associative array key does not exist or '
657 .'previous call did not return array'
658 );
659 }
660 if (array_key_exists('result', $this->_result['cpanelresult']['data'])) {
661 return $this->_result['cpanelresult']['data']['result'];
662 } else {
663 return $this->_result['cpanelresult']['data'];
664 }
665 }
666
667 /**
668 * Get the string containing all of the output up until the header
669 *
670 * This method will return everything up until just past the body-content div
671 * this intended as a method of writing a liveAPI application that matches cpanel's
672 * presentation.
673 *
674 * @return string A string containing all output before the body-content div
675 * @throws UnexpectedValueException if no header value is detected
676 */
677 public function header( $title = '', $app_key = '' ) {
678 if ( !$this->_dom ) {
679 $result = $this->uapi( 'Chrome', 'get_dom', array( 'page_title' => $title, 'app_key' => $app_key ) );
680 $this->_dom = $result['cpanelresult']['result']['data'];
681 }
682 if ( !array_key_exists( 'header', $this->_dom ) ) {
683 throw new UnexpectedValueException('No header in DOM response!');
684 }
685 return $this->_dom['header'];
686 }
687
688 /**
689 * Get the string containing all of the output after the body
690 *
691 * This method will return everything past the body-content div
692 * this intended as a method of writing a liveAPI application that matches cpanel's
693 * presentation.
694 *
695 * @return string A string containing all output after the body-content div
696 * @throws UnexpectedValueException if no footer value is detected
697 */
698 public function footer( $title = '' ) {
699 if ( !$this->_dom ) {
700 $result = $this->uapi( 'Chrome', 'get_dom', array( 'title' => $title ) );
701 $this->_dom = $result['cpanelresult']['result']['data'];
702 }
703 if ( !array_key_exists( 'footer', $this->_dom ) ) {
704 throw new UnexpectedValueException('No footer in DOM response!');
705 }
706 return $this->_dom['footer'];
707 }
708
709
710
711 /**
712 * Close the connection and destroy the object
713 *
714 * Calling this method should not be required since all logic actually
715 * resides in the class deconstructor. This is provided for BC.
716 *
717 * @return void
718 * @throws RuntimeException if LiveAPI socket is not available
719 */
720 public function end()
721 {
722 if (!$this->connected) {
723 throw new RuntimeException(
724 'The LiveAPI PHP socket has closed, unable to continue.'
725 );
726 }
727 $this->__destruct();
728 }
729 /**
730 * Deconstructor is responsible for closing communication with the cPanel
731 * engine
732 *
733 * @return void
734 * @throws RuntimeException if LiveAPI socket is not available
735 */
736 public function __destruct()
737 {
738 if (!$this->connected) {
739 throw new RuntimeException(
740 'The LiveAPI PHP socket has closed, unable to continue.'
741 );
742 }
743 if (is_resource($this->_cpanelfh)) {
744 $this->exec('<cpanelxml shutdown="1" />', 1);
745 while (!feof($this->_cpanelfh)) {
746 fgets($this->_cpanelfh);
747 }
748 fclose($this->_cpanelfh);
749 if ($this->_debug_level) {
750 $this->debug_log(1, 'MAX_MEM: ' . memory_get_peak_usage());
751 if (is_resource($this->_debug_fh)) {
752 fclose($this->_debug_fh);
753 }
754 }
755 }
756 }
757}
758/**
759 * Retrieve the contents of the 'result' node within a return response data
760 * structure
761 *
762 * This function is only valid for responses which have only a single response
763 * in their data structure, i.e., special cpanel tags. In most cases, one
764 * should consider using CPANEL::get_result() immediately following an API or
765 * cPanel tag query. This function may be deprecated in future versions of the
766 * LiveAPI PHP client code.
767 *
768 * @param array $result_data Returned response in array format
769 *
770 * @return string Contents of the "result" node in the provided data structure
771 */
772function simple_result($result_data)
773{
774 return $result_data['cpanelresult']['data']['result'];
775}
776?>