HEX
Server: Apache/2.4.41
System: Linux mainweb 5.4.0-182-generic #202-Ubuntu SMP Fri Apr 26 12:29:36 UTC 2024 x86_64
User: nationalmedicaregrp (1119)
PHP: 8.3.7
Disabled: exec,passthru,shell_exec,system,popen,proc_open,pcntl_exec
Upload Files
File: /home/sreyam/public_html/backup/form-data/php_wrappers/CMBase.php
<?php
/**
* LICENSE
* -------------------
* Copyright (c) 2007-2009, Kaiser Shahid <[email protected]> and
* Campaign Monitor <[email protected]>
* All rights reserved.
*
* This software is licensed under the BSD License:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Kaiser Shahid or Campaign Monitor nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Kaiser Shahid "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Kaiser Shahid BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @package CampaignMonitorLib
*/

/**
* This is an all-inclusive package for interfacing with Campaign Monitor's services. It
* supports SOAP, GET, and POST seamlessly (just set the $method property to 'soap', 'get', 
* or 'post' before making a call) and always returns the same view of data regardless of
* the method used to call the service.
*
* See README for more information on usage and details.
*
* CHANGES: 2008-04-28
* -------------------
* - Now compatible with PHP4. Biggest changes include removing reliance on
*   enhanced OOP syntax, and using XML Parser functions instead of SimpleXML.
* - Base class (CMBase) branches into CampaignMonitor and MailBuild
* - CMBase contains all the shared API calls (and extended functionality 
*   related to those) between both classes.
*
* @package CampaignMonitorLib
* @subpackage CMBase
* @author Kaiser Shahid <[email protected]> (www.qaiser.net)
* @copyright 2007-2009
* @see http://www.campaignmonitor.com/api/
*/

define('PHPVER', phpversion());
define('CM_PHP_WRAPPER_VERSION', '1.4.9');

// WARNING: this is needed to keep the socket from apparently hanging (even when it should be done reading)
// NOTE: using a timeout (SOCKET_TIMEOUT) that's passed when calling fsockopen. safer thing to do.
//ini_set( 'default_socket_timeout', 1 );
define( 'SOCKET_TIMEOUT', 1 );

class CMBase
{
	var /*@ protected */
		$api = ''
		, $campaign_id = 0
		, $client_id = 0
		, $list_id = 0
	;
	
	var /*@ public */
		$method = 'get'
		, $url = ''
		, $soapAction = ''
		, $curl = true
		, $curlExists = true
	;
	
	// debugging options
	var /*@ public */
		$debug_level = 0
		, $debug_request = ''
		, $debug_response = ''
		, $debug_url = ''
		, $debug_info = array()
		, $show_response_headers = 0
	;
	
	/**
	* @param string $api Your API key.
	* @param string $client The default ClientId you're going to work with.
	* @param string $campaign The default CampaignId you're going to work with.
	* @param string $list The default ListId you're going to work with.
	* @param string $method Determines request type. Values are either get, post, or soap.
	*/
	function CMBase( $api = null, $client = null, $campaign = null, $list = null, $method = 'get' )
	{
		$this->api = $api;
		$this->client_id = $client;
		$this->campaign_id = $campaign;
		$this->list_id = $list;
		$this->method = $method;
		$this->curlExists = function_exists( 'curl_init' ) && function_exists( 'curl_setopt' );
	}

	/**
	* The direct way to make an API call. This allows developers to include new API
	* methods that might not yet have a wrapper method as part of the package.
	*
	* @param string $action The API call.
	* @param array $options An associative array of values to send as part of the request.
	* @return array The parsed XML of the request.
	*/
	function makeCall( $action = '', $options = array() )
	{
		// NEW [2008-06-24]: switch to soap automatically for these calls
		$old_method = $this->method;
		if ( $action == 'Subscriber.AddWithCustomFields' || $action == 'Subscriber.AddAndResubscribeWithCustomFields' || $action == 'Campaign.Create')
			$this->method = 'soap';
		
		if ( !$action ) return null;
		$url = $this->url;
		
		// DONE: like facebook's client, allow for get/post through the file wrappers
		// if curl isn't available. (or maybe have curl-emulating functions defined 
		// at the bottom of this script.)
		
		//$ch = curl_init();
		if ( !isset( $options['header'] ) )
			$options['header'] = array();
		
		$options['header'][] = 'User-Agent: CMBase URL Handler ' . CM_PHP_WRAPPER_VERSION;
		
		$postdata = '';
		$method = 'GET';
		
		if ( $this->method == 'soap' )
		{
			$options['header'][] = 'Content-Type: text/xml; charset=utf-8';
			$options['header'][] = 'SOAPAction: "' . $this->soapAction . $action . '"';
			
			$postdata = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
			$postdata .= "<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
			$postdata .= " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"";
			$postdata .= " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n";
			$postdata .= "<soap:Body>\n";
			$postdata .= "	<{$action} xmlns=\"{$this->soapAction}\">\n";
			$postdata .= "		<ApiKey>{$this->api}</ApiKey>\n";
			
			if ( isset( $options['params'] ) )
				$postdata .= $this->array2xml( $options['params'], "\t\t" );
			
			$postdata .= "	</{$action}>\n";
			$postdata .= "</soap:Body>\n";
			$postdata .= "</soap:Envelope>";
			
			$method = 'POST';
			
			//curl_setopt( $ch, CURLOPT_POST, 1 );
			//curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
		}
		else
		{
			$postdata = "ApiKey={$this->api}";
			$url .= "/{$action}";
			
			// NOTE: since this is GET, the assumption is that params is a set of simple key-value pairs.
			if ( isset( $options['params'] ) )
			{
				foreach ( $options['params'] as $k => $v )
					$postdata .= '&' . $k . '=' .rawurlencode($this->fixEncoding($v));
			}
			
			if ( $this->method == 'get' )
			{
				$url .= '?' . $postdata;
				$postdata = '';
			}
			else
			{
 				$options['header'][] = 'Content-Type: application/x-www-form-urlencoded';
				$method = 'POST';
				//curl_setopt( $ch, CURLOPT_POST, 1 );
				//curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
			}
		}
					
		$res = '';
		
		// WARNING: using fopen() does not recognize stream contexts in PHP 4.x, so
		// my guess is using fopen() in PHP 4.x implies that POST is not supported
		// (otherwise, how do you tell fopen() to use POST?). tried fsockopen(), but
		// response time was terrible. if someone has more experience with working
		// directly with streams, please troubleshoot that.
		// NOTE: fsockopen() needs a small timeout to force the socket to close.
		// it's defined in SOCKET_TIMEOUT. 
		
		// preferred method is curl, only if it exists and $this->curl is true.
		if ( $this->curl && $this->curlExists )
		{
			$ch = curl_init();
			if ( $this->method != 'get' )
			{
				curl_setopt( $ch, CURLOPT_POST, 1 );
				curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
			}
			
			curl_setopt( $ch, CURLOPT_URL, $url );
			curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
			curl_setopt( $ch, CURLOPT_HTTPHEADER, $options['header'] );
			curl_setopt( $ch, CURLOPT_HEADER, $this->show_response_headers );
			
			// except for the response, all other information will be stored when debugging is on.
			$res = curl_exec( $ch );
			if ( $this->debug_level )
			{
				$this->debug_url = $url;
				$this->debug_request = $postdata;
				$this->debug_info = curl_getinfo( $ch );
				$this->debug_info['headers_sent'] = $options['header'];
			}
			$this->debug_response = $res;
			curl_close( $ch );
		}
		else
		{
			// 'header' is actually the entire HTTP payload. as such, you need
			// Content-Length header, otherwise you'll get errors returned/emitted.
			
			$postLen = strlen( $postdata );
			$ctx = array(
				'method' => $method
				, 'header' => implode( "\n", $options['header'] ) 
					. "\nContent-Length: " . $postLen
					. "\n\n" . $postdata
			);
			
			if ( $this->debug_level )
			{
				$this->debug_url = $url;
				$this->debug_request = $postdata;
				$this->debug_info['overview'] = 'Used stream_context_create()/fopen() to make request. Content length=' . $postLen;
				$this->debug_info['headers_sent'] = $options['header'];
				//$this->debug_info['complete_content'] = $ctx;
			}
			
			$pv = PHPVER;
			
			// the preferred non-cURL way if user is using PHP 5.x
			if ( $pv{0} == '5' )
			{
				$context = stream_context_create( array( 'http' => $ctx ) );
				$fp = fopen( $url, 'r', false, $context );
				ob_start();
				fpassthru( $fp );
				fclose( $fp );
				$res = ob_get_clean();
			}
			else
			{
				// this should work with PHP 4, but it seems to take forever to get data back this way
				// NOTE: setting the default_socket_timeout seems to alleviate this issue [finally].
				list( $protocol, $url ) = explode( '//', $url, 2 );
				list( $domain, $path ) = explode( '/', $url, 2 );
				$fp = fsockopen( $domain, 80, $tvar, $tvar2, SOCKET_TIMEOUT );
			
				if ( $fp )
				{
					$payload = "$method /$path HTTP/1.1\n"
					 	. "Host: $domain\n"
						. $ctx['header']
					;
					fwrite( $fp, $payload );
				
					// even with the socket timeout set, using fgets() isn't playing nice, but
					// fpassthru() seems to be doing the right thing.
				
					ob_start();
					fpassthru( $fp );
					list( $headers, $res ) = explode( "\r\n\r\n", ob_get_clean(), 2 );
				
					if ( $this->debug_level )
						$this->debug_info['headers_received'] = $headers;
				
					fclose( $fp );
				}
				elseif ( $this->debug_level )
					$this->debug_info['overview'] .= "\nOpening $domain/$path failed!";
			}
		}
		
		if ( $res )
		{
			if ( $this->method == 'soap' )
			{
				$tmp = $this->xml2array( $res, '/soap:Envelope/soap:Body' );
				if ( !is_array( $tmp ) )
					return $tmp;
				else
					return $tmp[$action.'Response'][$action.'Result'];
			}
			else
				return $this->xml2array($res);
		}
		else
			return null;
	}

	/**
	 * Encodes a string to UTF-8 only if needed 
	 * @param $in_str String to potentially encode
	 * @return UTF-8 encoded string
	 */
	function fixEncoding($in_str) { 
		$cur_encoding = mb_detect_encoding($in_str);
		if($cur_encoding == "UTF-8" && mb_check_encoding($in_str,"UTF-8"))
			return $in_str; 
		else 
			return utf8_encode($in_str);
	}

	/**
	 * Convert the given XML $contents into a PHP array. Based on code from:
	 * http://www.bin-co.com/php/scripts/xml2array/
	 * @param $contents The XML to be converted.
	 * @param $root The path of the root element within the XML at which 
	 * conversion should occur.
	 * @param $charset The character set to use.
	 * @param $get_attributes 0 or 1. If this is 1 the function will get the 
	 * attributes as well as the tag values - this results in a different array 
	 * structure in the return value.
	 * @param $priority Can be 'tag' or 'attribute'. This will change the structure
	 * of the resulting array. For 'tag', the tags are given more importance.
	 * @return A PHP array representing the XML $contents passed in
	 */
	function xml2array(
		$contents, 
		$root = '/',
		$charset = 'utf-8',
		$get_attributes = 0, 
		$priority = 'tag') {
	
		if(!$contents)
			return array();
	
	    if(!function_exists('xml_parser_create'))
	        return array();
	
	    // Get the PHP XML parser
	    $parser = xml_parser_create($charset);
	
	    // Attempt to find the last tag in the $root path and use this as the 
	    // start/end tag for the process of extracting the xml
		// Example input: '/soap:Envelope/soap:Body'
	
	    // Toggles whether the extraction of xml into the array actually occurs
	    $extract_on = TRUE;
	    $start_and_end_element_name = '';
		$root_elements = explode('/', $root);
		if ($root_elements != FALSE && 
			!empty($root_elements)) {
			$start_and_end_element_name = trim(end($root_elements));
			if (!empty($start_and_end_element_name))
				$extract_on = FALSE;
		}
	
	    xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); # http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
	    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
	    xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
	    xml_parse_into_struct($parser, trim($contents), $xml_values);
	    xml_parser_free($parser);
	
	    if(!$xml_values) 
	    	return;
	
	    $xml_array = array();
	    $parents = array();
	    $opened_tags = array();
	    $arr = array();
	
	    $current = &$xml_array; // Reference
	
	    // Go through the tags.
	    $repeated_tag_index = array(); // Multiple tags with same name will be turned into an array
	    foreach($xml_values as $data) {
	        unset($attributes,$value); // Remove existing values, or there will be trouble
	
	        // This command will extract these variables into the foreach scope
	        // tag(string), type(string), level(int), attributes(array).
	        extract($data);
	
	        if (!empty($start_and_end_element_name) && 
	        	$tag == $start_and_end_element_name) {
	        	// Start at the next element (if looking at the opening tag), 
	        	// or don't process any more elements (if looking at the closing tag)...
	        	$extract_on = !$extract_on;
	        	continue;
	        }
	
	        if (!$extract_on)
	        	continue;
	        
	        $result = array();
	        $attributes_data = array();
	        
	        if(isset($value)) {
	            if($priority == 'tag') $result = $value;
	            else $result['value'] = $value; //Put the value in a assoc array if we are in the 'Attribute' mode
	        }
	
	        // Set the attributes too.
	        if(isset($attributes) and $get_attributes) {
	            foreach($attributes as $attr => $val) {
	                if($priority == 'tag') $attributes_data[$attr] = $val;
	                else $result['attr'][$attr] = $val; // Set all the attributes in a array called 'attr'
	            }
	        }
	
	        // See tag status and do the needed.
	        if($type == "open") {// The starting of the tag '<tag>'
	            $parent[$level-1] = &$current;
	            if(!is_array($current) or (!in_array($tag, array_keys($current)))) { //Insert New tag
	                $current[$tag] = $result;
	                if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
	                $repeated_tag_index[$tag.'_'.$level] = 1;
	                $current = &$current[$tag];
	            } else { // There was another element with the same tag name
	                if(isset($current[$tag][0])) { // If there is a 0th element it is already an array
	                    $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
	                    $repeated_tag_index[$tag.'_'.$level]++;
	                } else { // This section will make the value an array if multiple tags with the same name appear together
	                    $current[$tag] = array($current[$tag],$result); // This will combine the existing item and the new item together to make an array
	                    $repeated_tag_index[$tag.'_'.$level] = 2;
	                    
	                    if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
	                        $current[$tag]['0_attr'] = $current[$tag.'_attr'];
	                        unset($current[$tag.'_attr']);
	                    }
	                }
	                $last_item_index = $repeated_tag_index[$tag.'_'.$level]-1;
	                $current = &$current[$tag][$last_item_index];
	            }
	        } elseif($type == "complete") { // Tags that ends in 1 line '<tag />'
	            // See if the key is already taken.
	            if(!isset($current[$tag])) { //New Key
	            	// Don't insert an empty array - we don't want it!
	                if (!(is_array($result) && empty($result)))
	                	$current[$tag] = $result;
	                $repeated_tag_index[$tag.'_'.$level] = 1;
	                if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;
	
	            } else { // If taken, put all things inside a list(array)
	                if(isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
	
	                    // ...push the new element into that array.
	                    $current[$tag][$repeated_tag_index[$tag.'_'.$level]] = $result;
	                    
	                    if($priority == 'tag' and $get_attributes and $attributes_data) {
	                        $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
	                    }
	                    $repeated_tag_index[$tag.'_'.$level]++;
	
	                } else { // If it is not an array...
	                    $current[$tag] = array($current[$tag],$result); // ...Make it an array using using the existing value and the new value
	                    $repeated_tag_index[$tag.'_'.$level] = 1;
	                    if($priority == 'tag' and $get_attributes) {
	                        if(isset($current[$tag.'_attr'])) { // The attribute of the last(0th) tag must be moved as well
	                            
	                            $current[$tag]['0_attr'] = $current[$tag.'_attr'];
	                            unset($current[$tag.'_attr']);
	                        }
	                        
	                        if($attributes_data) {
	                            $current[$tag][$repeated_tag_index[$tag.'_'.$level] . '_attr'] = $attributes_data;
	                        }
	                    }
	                    $repeated_tag_index[$tag.'_'.$level]++; // 0 and 1 index is already taken
	                }
	            }
	        } elseif($type == 'close') { // End of tag '</tag>'
	            $current = &$parent[$level-1];
	        }
	    }
	    return($xml_array);
	}  

	/**
	* Converts an array to XML. This is the inverse to xml2array(). Values
	* are automatically escaped with htmlentities(), so you don't need to escape 
	* values ahead of time. If you have, just set the third parameter to false.
	* This is an all-or-nothing deal.
	*
	* @param mixed $arr The associative to convert to an XML fragment
	* @param string $indent (Optional) Starting identation of each element
	* @param string $escape (Optional) Determines whether or not to escape a text node.
	* @return string An XML fragment.
	*/
	function array2xml( $arr, $indent = '', $escape = true )
	{
		$buff = '';
		
		foreach ( $arr as $k => $v )
		{
			// Dreamweaver has a bug which causes "/" to be stripped out of "</$k>"
			// when editing PHP files, so variable parsing is not longer being used
			// where a forward slash is required before the variable
			if ( !is_array( $v ) )
				$buff .= "$indent<$k>" . ($escape ? $this->fixEncoding(htmlspecialchars($v)) : $v ) . "</" . $k . ">\n";
			else
			{
				/*
				Encountered a list. The primary difference between the two branches is that
				in the 'if' branch, a $k element is generated for each item in $v, whereas
				in the 'else' branch, a single $k element encapsulates $v.
				*/
				
				if ( isset( $v[0] ) )
				{
					foreach ( $v as $_k => $_v )
					{
						if ( is_array( $_v ) )
					 		$buff .= "$indent<$k>\n" . $this->array2xml( $_v, $indent . "\t", $escape ) . "$indent</" . $k . ">\n";
						else
							$buff .= "$indent<$k>" . ($escape ? $this->fixEncoding(htmlspecialchars($_v)) : $_v ) . "</" . $k . ">\n";
					}
				}
				else
					$buff .= "$indent<$k>\n" . $this->array2xml( $v, $indent . "\t", $escape ) . "$indent</" . $k .">\n";
			}
		}
		
		return $buff;
	}
}	

/**
* The new CampaignMonitor class that now extends from CMBase. This should be 
* backwards compatible with the original (PHP5) version.
*
* @package CampaignMonitorLib
* @subpackage CampaignMonitor
* @author Kaiser Shahid <[email protected]> (www.qaiser.net) and 
* Campaign Monitor <[email protected]> 
* @copyright 2007-2009
* @see http://www.campaignmonitor.com/api/
*/
class CampaignMonitor extends CMBase
{
	var /*@ protected */
		$url = 'http://api.createsend.com/api/api.asmx',
		$soapAction = 'http://api.createsend.com/api/';
	
	/**
	* @param string $api Your API key.
	* @param string $client The default ClientId you're going to work with.
	* @param string $campaign The default CampaignId you're going to work with.
	* @param string $list The default ListId you're going to work with.
	* @param string $method Determines request type. Values are either get, post, or soap.
	*/
	
	function CampaignMonitor( $api = null, $client = null, $campaign = null, $list = null, $method = 'get' )
	{
		CMBase::CMBase( $api, $client, $campaign, $list, $method );
	}

	/**
	* Wrapper for Subscribers.GetActive. This method triples as Subscribers.GetUnsubscribed 
	* and Subscribers.GetBounced when the very last parameter is overridden.
	*
	* @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @param string $action (Optional) Set the actual API method to call. Defaults to Subscribers.GeActive if no other valid value is given.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Subscribers.GetActive.aspx
	*/
	function subscribersGetActive( $date  = 0, $list_id = null, $action = 'Subscribers.GetActive' )
	{
		if ( !$list_id )
			$list_id = $this->list_id;
		
		if ( is_numeric( $date ) )
			$date = date( 'Y-m-d H:i:s', $date );
		
		$valid_actions = array( 'Subscribers.GetActive' => '', 'Subscribers.GetUnsubscribed' => '', 'Subscribers.GetBounced' => '' );
		if ( !isset( $valid_actions[$action] ) )
			$action = 'Subscribers.GetActive';
		
		return $this->makeCall( $action
			, array( 
				'params' => array( 
					'ListID' => $list_id 
					, 'Date' => $date
				)
			)
		);
	}
	
	/**
	* @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @see http://www.campaignmonitor.com/api/Subscribers.GetUnsubscribed.aspx
	*/
	function subscribersGetUnsubscribed( $date  = 0, $list_id = null )
	{
		return $this->subscribersGetActive( $date, $list_id, 'Subscribers.GetUnsubscribed' );
	}
	
	/**
	* @param mixed $date If a string, should be in the date() format of 'Y-m-d H:i:s', otherwise, a Unix timestamp.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @see http://www.campaignmonitor.com/api/Subscribers.GetBounced.aspx
	*/
	function subscribersGetBounced( $date  = 0, $list_id = null )
	{
		return $this->subscribersGetActive( $date, $list_id, 'Subscribers.GetBounced' );
	}
	
	/**
	* subscriberAdd()
	* @param string $email Email address.
	* @param string $name User's name.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
	* @see http://www.campaignmonitor.com/api/Subscriber.Add.aspx
	*/
	function subscriberAdd( $email, $name, $list_id = null, $resubscribe = false )
	{
		if ( !$list_id )
			$list_id = $this->list_id;
		
		$action = 'Subscriber.Add';
		if ( $resubscribe ) $action = 'Subscriber.AddAndResubscribe';
		
		return $this->makeCall( $action
			, array(
				'params' => array(
					'ListID' => $list_id
					, 'Email' => $email
					, 'Name' => $name
				)
			)
		);
	}
	
	/**
	* This encapsulates the check of whether this particular user unsubscribed once.
	* @param string $email Email address.
	* @param string $name User's name.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	*/
	function subscriberAddRedundant( $email, $name, $list_id = null )
	{
		$added = $this->subscriberAdd( $email, $name, $list_id );        
	        
		if ( $added && $added['Result']['Code'] == '204' )
		{
			$subscribed = $this->subscribersGetIsSubscribed( $email, $list_id );    
	    
			// Must have unsubscribed, so resubscribe
			if ( $subscribed['anyType'] == 'False' )
			{
				// since we're internal, we'll just call the method with full parameters rather
				// than go through a secondary wrapper function.
				$added = $this->subscriberAdd( $email, $name, $list_id, true );
				return $added;
			}
		}
		
		return $added;
	}
	
	/**
	* @param string $email Email address.
	* @param string $name User's name.
	* @param mixed $fields Should be a $key => $value mapping. If there are more than one items for $key, let
	*        $value be a list of scalar values. Example: array( 'Interests' => array( 'xbox', 'wii' ) )
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Subscriber.AddWithCustomFields.aspx
	*/
	function subscriberAddWithCustomFields( $email, $name, $fields, $list_id = null, $resubscribe = false )
	{
		if ( !$list_id )
			$list_id = $this->list_id;
		
		$action = 'Subscriber.AddWithCustomFields';
		if ( $resubscribe ) $action = 'Subscriber.AddAndResubscribeWithCustomFields';
		
		if ( !is_array( $fields ) )
			$fields = array();
		
		$_fields = array( 'SubscriberCustomField' => array() );
		foreach ( $fields as $k => $v )
		{
			if ( is_array( $v ) )
			{
				foreach ( $v as $nv )
					$_fields['SubscriberCustomField'][] = array( 'Key' => $k, 'Value' => $nv );
			}
			else
				$_fields['SubscriberCustomField'][] = array( 'Key' => $k, 'Value' => $v );
		}
		return $this->makeCall( $action
			, array(
				'params' => array(
					'ListID' => $list_id
					, 'Email' => $email
					, 'Name' => $name
					, 'CustomFields' => $_fields
				)
			)
		);
	}
	
	/**
	* Same as subscriberAddRedundant() except with CustomFields.
	*
	* @param string $email Email address.
	* @param string $name User's name.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	*/
	function subscriberAddWithCustomFieldsRedundant( $email, $name, $fields, $list_id = null )
	{
		$added = $this->subscriberAddWithCustomFields( $email, $name, $fields, $list_id );
		if ( $added && $added['Code'] == '0' )
		{
			$subscribed = $this->subscribersGetIsSubscribed( $email );
			if ( $subscribed == 'False' )
			{
				$added = $this->subscriberAddWithCustomFields( $email, $name, $fields, $list_id, true );
				return $added;
			}
		}
		
		return $added;
	}
	
	/**
	* @param string $email Email address.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @param boolean $check_subscribed If true, does the Subscribers.GetIsSubscribed API method instead.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Subscriber.Unsubscribe.aspx
	*/
	function subscriberUnsubscribe( $email, $list_id = null, $check_subscribed = false )
	{
		if ( !$list_id )
			$list_id = $this->list_id;
		
		$action = 'Subscriber.Unsubscribe';
		if ( $check_subscribed ) $action = 'Subscribers.GetIsSubscribed';
		
		return $this->makeCall( $action
			, array(
				'params' => array(
					'ListID' => $list_id
					, 'Email' => $email
				)
			)
		);
	}
	
	/**
	* @return string A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Subscribers.GetIsSubscribed.aspx
	*/
	function subscribersGetIsSubscribed( $email, $list_id = null )
	{
		return $this->subscriberUnsubscribe( $email, $list_id, true );
	}
	
	/**
	* Given an array of lists, indicate whether the $email is subscribed to each of those lists.
	*
	* @param string $email User's email
	* @param mixed $lists An associative array of lists to check against. Each key should be a List ID
	* @param boolean $no_assoc If true, only returns an array where each value indicates that the user is subscribed
	*        to that particular list. Otherwise, returns a fully associative array of $list_id => true | false.
	* @return mixed An array corresponding to $lists where true means the user is subscribed to that particular list.
	*/
	function checkSubscriptions( $email, $lists, $no_assoc = true )
	{
		$nlist = array();
		foreach ( $lists as $lid => $misc )
		{
			$val = $this->subscribersGetIsSubscribed( $email, $lid );
			$val = $val != 'False';
			if ( $no_assoc && $val ) $nlist[] = $lid;
			elseif ( !$no_assoc ) $nlist[$lid] = $val;
		}
		
		return $nlist;
	}
	
	/**
	* @param string $email Email address.
	* @param string $name User's name.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @see http://www.campaignmonitor.com/api/Subscriber.AddAndResubscribe.aspx
	*/
	
	function subscriberAddAndResubscribe( $email, $name, $list_id = null )
	{
		return $this->subscriberAdd( $email, $name, $list_id, true );
	}
	
	/**
	* @param string $email Email address.
	* @param string $name User's name.
	* @param mixed $fields Should only be a single-dimension array of key-value pairs.
	* @param int $list_id (Optional) A valid List ID to check against. If not given, the default class property is used.
	* @param boolean $resubscribe If true, does an equivalent 'AndResubscribe' API method.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Subscriber.AddAndResubscribeWithCustomFields.aspx
	*/
	
	function subscriberAddAndResubscribeWithCustomFields( $email, $name, $fields, $list_id = null )
	{
		return $this->subscriberAddWithCustomFields( $email, $name, $fields, $list_id, true );
	}

	/**
	 * Returns the details of a particular subscriber.
	 * @param $list_id The ID of the list to which the subscriber belongs
	 * @param $email The subscriber's email address
	 * @return mixed A parsed response from the server, or null if something failed
	 * @see http://www.campaignmonitor.com/api/method/subscribers-get-single-subscriber/
	 */
	function subscriberGetSingleSubscriber($list_id = null, $email)
    {
        if (!$list_id != null)
            $list_id = $this->list_id;

        return $this->makeCall( 
        	'Subscribers.GetSingleSubscriber',
            array(
                'params' => array(
                    'ListID' => $list_id,
                    'EmailAddress' => $email
                )
            )
        );
    }	

    /*
	* A generic wrapper to feed Client.* calls.
	*
	* @param string $method The API method to call.
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	*/
	
	function clientGeneric( $method, $client_id = null )
	{
		if ( !$client_id )
			$client_id = $this->client_id;
		
		return $this->makeCall( 'Client.' . $method
			, array(
				'params' => array(
					'ClientID' => $client_id
				)
			)
		);
	}
	
	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Client.GetLists.aspx
	*/
	
	function clientGetLists( $client_id = null )
	{
		return $this->clientGeneric( 'GetLists', $client_id );
	}
	
	/**
	* Creates an associative array with list_id => List_label pairings.
	*
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	*/
	
	function clientGetListsDropdown( $client_id = null )
	{
		$lists = $this->clientGetLists( $client_id );
		if ( !isset( $lists['List'] ) )
			return null;
		else
			$lists = $lists['List'];
		
		$_lists = array();
		
		if ( isset( $lists[0] ) )
		{
			foreach ( $lists as $list )
				$_lists[$list['ListID']] = $list['Name'];
		}
		else
			$_lists[$lists['ListID']] = $lists['Name'];
		
		return $_lists;
	}
	
	/**
	* Creates an associative array with list_id:List_Label => (list_id) List_label pairings.
	* Remember that you'll need to split the key on ':' only once to get the appropriate ListID
	* and Segment Name.
	*
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	*/
	
	function clientGetSegmentsDropdown( $client_id = null )
	{
		$lists = $this->clientGetSegments( $client_id );
		if ( !isset( $lists['List'] ) )
			return null;
		else
			$lists = $lists['List'];
		
		$_lists = array();
		
		if ( isset( $lists[0] ) )
		{
			foreach ( $lists as $list )
				$_lists[$list['ListID'].':'.$list['Name']] = '(' . $list['ListID'] . ') ' . $list['Name'];
		}
		else
			$_lists[$lists['ListID'].':'.$lists['Name']] = '(' . $lists['ListID'] . ') ' . $lists['Name'];
		
		return $_lists;
	}
	
	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Client.GetCampaigns.aspx
	*/
	
	function clientGetCampaigns( $client_id = null )
	{
		return $this->clientGeneric( 'GetCampaigns', $client_id );
	}
	
	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Client.GetSegments.aspx
	*/
	
	function clientGetSegments( $client_id = null )
	{
		return $this->clientGeneric( 'GetSegments', $client_id );
	}
	
	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-getsuppressionlist/
	*/
	function clientGetSuppressionList( $client_id = null )
	{
		return $this->clientGeneric( 'GetSuppressionList', $client_id );
	}

	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-gettemplates/
	*/
	function clientGetTemplates( $client_id = null )
	{
		return $this->clientGeneric( 'GetTemplates', $client_id );
	}
	
	/**
	* @param int $client_id (Optional) A valid Client ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-getdetail/
	*/
	function clientGetDetail( $client_id = null )
	{
		return $this->clientGeneric( 'GetDetail', $client_id );
	}
	
	/**
	* @param string $companyName (CompanyName) Company name of the client to be added
	* @param string $contactName (ContactName) Contact name of the client to be added
	* @param string $emailAddress (EmailAddress) Email Address of the client to be added
	* @param string $country (Country) Country of the client to be added
	* @param string $timezone (Timezone) Timezone of the client to be added
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-create/
	*/
	function clientCreate( $companyName, $contactName, $emailAddress, $country, $timezone )
	{
		return $this->makeCall( 'Client.Create'
			, array(
				'params' => array(
					'CompanyName' => $companyName
					, 'ContactName' => $contactName
					, 'EmailAddress' => $emailAddress
					, 'Country' => $country
					, 'Timezone' => $timezone
				)
			)
		);
	}
	
	/**
	* @param int $client_id (ClientID) ID of the client to be updated
	* @param string $companyName (CompanyName) Company name of the client to be updated
	* @param string $contactName (ContactName) Contact name of the client to be updated
	* @param string $emailAddress (EmailAddress) Email Address of the client to be updated
	* @param string $country (Country) Country of the client to be updated
	* @param string $timezone (Timezone) Timezone of the client to be updated
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-create/
	*/
	function clientUpdateBasics( $client_id, $companyName, $contactName, $emailAddress, $country, $timezone )
	{
		return $this->makeCall( 'Client.UpdateBasics'
			, array(
				'params' => array(
					'ClientID' => $client_id
					, 'CompanyName' => $companyName
					, 'ContactName' => $contactName
					, 'EmailAddress' => $emailAddress
					, 'Country' => $country
					, 'Timezone' => $timezone
				)
			)
		);
	}
	
	/**
	* @param int $client_id (ClientID) ID of the client to be updated
	* @param string $accessLevel (AccessLevel) AccessLevel of the client
	* @param string $username (Username) Clients username
	* @param string $password (Password) Password of the client
	* @param string $billingType (BillingType) BillingType that the client will be set as
	* @param string $currency (Currency) Currency that the client will pay in
	* @param string $deliveryFee (DeliveryFee) Per campaign deliivery fee for the campaign
	* @param string $costPerRecipient (CostPerRecipient) Per email fee for the client
	* @param string $designAndSpamTestFee (DesignAndSpamTestFee) Amount the client will
	*				be charged if they have access to send design/spam tests
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/client-updateaccessandbilling/
	*/
	function clientUpdateAccessAndBilling( $client_id, $accessLevel, $username, $password, $billingType, $currency, $deliveryFee, $costPerRecipient, $designAndSpamTestFee )
	{
		return $this->makeCall( 'Client.UpdateAccessAndBilling'
			, array(
				'params' => array(
					'ClientID' => $client_id
					, 'AccessLevel' => $accessLevel
					, 'Username' => $username
					, 'Password' => $password
					, 'BillingType' => $billingType
					, 'Currency' => $currency
					, 'DeliveryFee' => $deliveryFee
					, 'CostPerRecipient' => $costPerRecipient
					, 'DesignAndSpamTestFee' => $designAndSpamTestFee
				)
			)
		);
	}
	
	
	
	/**
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/User.GetClients.aspx
	*/
	
	function userGetClients()
	{
		return $this->makeCall( 'User.GetClients' );
	}
	
	/**
	* @return string A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/User.GetSystemDate.aspx
	*/
	
	function userGetSystemDate()
	{
		return $this->makeCall( 'User.GetSystemDate' );
	}
	
	/**
	* @return string A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/user-gettimezones/
	*/
	
	function userGetTimezones()
	{
		return $this->makeCall( 'User.GetTimezones' );
	}
	
	/**
	* @return string A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/user-getcountries/
	*/
	
	function userGetCountries()
	{
		return $this->makeCall( 'User.GetCountries' );
	}
	
	/**
	 * Gets the API key for a Campaign Monitor user, given site URL, username, 
	 * password. If the user has not already had their API key generated at 
	 * the time this method is called, the userís API key will be generated 
	 * and returned by this method.
	 * 
	 * @param $site_url The base URL of the site you use to login to 
	 * Campaign Monitor. e.g. http://example.createsend.com/
	 * @param $username The username you use to login to Campaign Monitor.
	 * @param $password The password you use to login to Campaign Monitor.
	 * @return mixed A parsed response from the server, or null if something 
	 * failed.
	 * @see http://www.campaignmonitor.com/api/method/user-getapikey/
	 */
	function userGetApiKey($site_url, $username, $password)
	{
		return $this->makeCall(
			'User.GetApiKey', 
			array(
				'params' => array(
					'SiteUrl' => $site_url,
					'Username' => $username,
					'Password' => $password,
				)
			)
		);
	}
	
	/**
	* A generic wrapper to feed Campaign.* calls.
	*
	* @param string $method The API method to call.
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	*/
	
	function campaignGeneric( $method, $campaign_id = null )
	{
		if ( !$campaign_id )
			$campaign_id = $this->campaign_id;
		
		return $this->makeCall( 'Campaign.' . $method
			, array(
				'params' => array(
					'CampaignID' => $campaign_id
				)
			)
		);
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetSummary.aspx
	*/
	
	function campaignGetSummary( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetSummary', $campaign_id );
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetOpens.aspx
	*/
	
	function campaignGetOpens( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetOpens', $campaign_id );
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetBounces.aspx
	*/
	
	function campaignGetBounces( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetBounces', $campaign_id );
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetSubscriberClicks.aspx
	*/
	
	function campaignGetSubscriberClicks( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetSubscriberClicks', $campaign_id );
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetUnsubscribes.aspx
	*/
	
	function campaignGetUnsubscribes( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetUnsubscribes', $campaign_id );
	}
	
	/**
	* @param int $campaign_id (Optional) A valid Campaign ID to check against. If not given, the default class property is used.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.GetLists.aspx
	*/
	
	function campaignGetLists( $campaign_id = null )
	{
		return $this->campaignGeneric( 'GetLists', $campaign_id );
	}
	
	/**
	* @param int $client_id The ClientID you wish to use; set it to null to use the default class property.
	* @param string $name (CampaignName) Name of campaign
	* @param string $subject (CampaignSubject) Subject of campaign mailing
	* @param string $fromName (FromName) The From name of the sender
	* @param string $fromEmail (FromEmail) The email of the sender
	* @param string $replyTo (ReplyTo) An alternate email to send replies to
	* @param string $htmlUrl (HtmlUrl) Location of HTML body of email
	* @param string $textUrl (TextUrl) Location of plaintext body of email
	* @param array $subscriberListIds (SubscriberListIDs) An array of ListIDs. This will automatically be converted to the right format
	* @param array $listSegments (ListSegments) An array of segment names and their corresponding ListIDs. Each element needs to
	*        be an associative array with keys ListID and Name.
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/Campaign.Create.aspx
	*/
	
	function campaignCreate( $client_id, $name, $subject, $fromName, $fromEmail, $replyTo, $htmlUrl, $textUrl, $subscriberListIds, $listSegments )
	{
		if ($client_id == null)
			$client_id = $this->client_id;

		$_subListIds = '';
		if ($subscriberListIds != "") {
			$_subListIds = array( 'string' => array() );
			if ( is_array( $subscriberListIds ) ) {
				foreach ( $subscriberListIds as $lid ) {
					$_subListIds['string'][] = $lid;
				}
			}
		}

		$_seg = '';
		if ($listSegments != "")
		{
			$_seg = array();
			if (is_array($listSegments)) {
				for($i=0; $i < count($listSegments); $i++) {
					foreach ($listSegments[$i] as $k => $v) {
						$_seg['List'][$i][$k] = $v;
					}
				}
			}
		}

		return $this->makeCall( 'Campaign.Create', array(
			'params' => array(
				'ClientID' => $client_id
				, 'CampaignName' => $name
				, 'CampaignSubject' => $subject
				, 'FromName' => $fromName
				, 'FromEmail' => $fromEmail
				, 'ReplyTo' => $replyTo
				, 'HtmlUrl' => $htmlUrl
				, 'TextUrl' => $textUrl
				, 'SubscriberListIDs' => $_subListIds
				, 'ListSegments' => $_seg
				)
			)
		);
	}
	
	/**
	* @param int $client_id The CampaignID you wish to use; set it to null to use the default class property
	* @param string $confirmEmail (ConfirmationEmail) Email address to send confirmation of campaign send to
	* @param string $sendDate (SendDate) The timestamp to send the campaign. It must be formatted as YYY-MM-DD HH:MM:SS 
	*               and should correspond to user's timezone.
	*/
	
	function campaignSend( $campaign_id, $confirmEmail, $sendDate )
	{
		if ( $campaign_id == null )
			$campaign_id = $this->campaign_id;
		
		return $this->makeCall( 'Campaign.Send', array(
			'params' => array(
				'CampaignID' => $campaign_id
				, 'ConfirmationEmail' => $confirmEmail
				, 'SendDate' => $sendDate
				)
			)
		);
	}

	/**
	 * Delete a campaign.
	 * @param $campaign_id The ID of the campaign to delete.
	 * @return A Status code indicating success or failure.
	 * @see http://www.campaignmonitor.com/api/method/campaign-delete/
	 */
	function campaignDelete($campaign_id)
	{
		return $this->campaignGeneric('Delete', $campaign_id);
	}

	/**
	* @param int $client_id (ClientID) ID of the client the list will be created for
	* @param string $title (Title) Name of the new list
	* @param string $unsubscribePage (UnsubscribePage) URL of the page users will be 
	*				directed to when they unsubscribe from this list.
	* @param string $confirmOptIn (ConfirmOptIn) If true, the user will be sent a confirmation
	*				email before they are added to the list. If they click the link to confirm
	*				their subscription they will be added to the list. If false, they will be
	*				added automatically.
	* @param string $confirmationSuccessPage (ConfirmationSuccessPage) URL of the page that
	*				users will be sent to if they confirm their subscription. Only required when
					$confirmOptIn is true.
	* @see http://www.campaignmonitor.com/api/method/list-create/
	*/
	function listCreate( $client_id, $title, $unsubscribePage, $confirmOptIn, $confirmationSuccessPage )
	{
		if ( $confirmOptIn == 'false' )
			$confirmationSuccessPage = '';
			
		return $this->makeCall( 'List.Create', array(
			'params' => array(
				'ClientID' => $client_id
				, 'Title' => $title
				, 'UnsubscribePage' => $unsubscribePage
				, 'ConfirmOptIn' => $confirmOptIn
				, 'ConfirmationSuccessPage' => $confirmationSuccessPage
				)
			)
		);
	}
	
	/**
	* @param int $list_id (List) ID of the list to be updated
	* @param string $title (Title) Name of the new list
	* @param string $unsubscribePage (UnsubscribePage) URL of the page users will be 
	*				directed to when they unsubscribe from this list.
	* @param string $confirmOptIn (ConfirmOptIn) If true, the user will be sent a confirmation
	*				email before they are added to the list. If they click the link to confirm
	*				their subscription they will be added to the list. If false, they will be
	*				added automatically.
	* @param string $confirmationSuccessPage (ConfirmationSuccessPage) URL of the page that
	*				users will be sent to if they confirm their subscription. Only required when
					$confirmOptIn is true.
	* @see http://www.campaignmonitor.com/api/method/list-update/
	*/
	function listUpdate( $list_id, $title, $unsubscribePage, $confirmOptIn, $confirmationSuccessPage )
	{
		if ( $confirmOptIn == 'false' )
			$confirmationSuccessPage = '';
			
		return $this->makeCall( 'List.Update', array(
			'params' => array(
				'ListID' => $list_id
				, 'Title' => $title
				, 'UnsubscribePage' => $unsubscribePage
				, 'ConfirmOptIn' => $confirmOptIn
				, 'ConfirmationSuccessPage' => $confirmationSuccessPage
				)
			)
		);
	}
	
	/**
	* @param int $list_id (List) ID of the list to be deleted
	* @see http://www.campaignmonitor.com/api/method/list-delete/
	*/
	function listDelete( $list_id )
	{			
		return $this->makeCall( 'List.Delete', array(
			'params' => array(
				'ListID' => $list_id
				)
			)
		);
	}
	
	/**
	* @param int $list_id (List) ID of the list to be deleted
	* @see http://www.campaignmonitor.com/api/method/list-getdetail/
	*/
	function listGetDetail( $list_id )
	{			
		return $this->makeCall( 'List.GetDetail', array(
			'params' => array(
				'ListID' => $list_id
				)
			)
		);
	}

	/**
	 * Gets statistics for a subscriber list
	 * @param $list_id The ID of the list whose statistics will be returned.
	 * @return mixed A parsed response from the server, or null if something 
	 * @see http://www.campaignmonitor.com/api/method/list-getstats/
	 */
	function listGetStats($list_id)
	{
		return $this->makeCall(
			'List.GetStats',
			array(
				'params' => array(
					'ListID' => $list_id
				)
			)
		);
	}
	
	/**
	* @param int $list_id (ListID) A valid list ID to check against. 
	* @param string $fieldName (FieldName) Name of the new custom field
	* @param string $dataType (DataType) Data type of the field. Options are Text, Number, 
	*				MultiSelectOne, or MultiSelectMany
	* @param string $Options (Options) The available options for a multi-valued custom field. 
	*				Options should be separated by a double pipe ì||î. This field must be null 
	*				for Text and Number custom fields
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/list-createcustomfield/
	*/
	
	function listCreateCustomField( $list_id, $fieldName, $dataType, $options )
	{
		if ( $dataType == 'Text' || $dataType == 'Number' )
			$options = null;
			
		return $this->makeCall( 'List.CreateCustomField', array(
			'params' => array(
				'ListID' => $list_id
				, 'FieldName' => $fieldName
				, 'DataType' => $dataType
				, 'Options' => $options
				)
			)
		);
	}
	
	/**
	* @param int $list_id (ListID) A valid list ID to check against. 
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/list-getcustomfields/
	*/
	
	function listGetCustomFields( $list_id )
	{			
		return $this->makeCall( 'List.GetCustomFields', array(
			'params' => array(
				'ListID' => $list_id
				)
			)
		);
	}
	
	/**
	* @param int $list_id (ListID) A valid list ID to check against. 
	* @param int $key (Key) The Key of the field we want to delete. 
	* @return mixed A parsed response from the server, or null if something failed.
	* @see http://www.campaignmonitor.com/api/method/list-deletecustomfield/
	*/
	
	function listDeleteCustomField( $list_id, $key )
	{		
		return $this->makeCall( 'List.DeleteCustomField', array(
			'params' => array(
				'ListID' => $list_id
				, 'Key' => $key
				)
			)
		);
	}
	
	/**
	 * @param int $client_id (ClientID) ID of the client the template will be created for
	 * @param string $template_name (TemplateName) Name of the new template
	 * @param string $html_url (HTMLPageURL) URL of the HTML page you have created for the template
	 * @param string $zip_url (ZipFileURL) URL of a zip file containing any other files required by the template
	 * @param string $screenshot_url (ScreenshotURL) URL of a screenshot of the template
	 * @see http://www.campaignmonitor.com/api/method/template-create/
	 */
	function templateCreate($client_id, $template_name, $html_url, $zip_url, $screenshot_url)
	{
		return $this->makeCall('Template.Create', array(
			'params' => array(
				'ClientID' => $client_id,
				'TemplateName' => $template_name,
				'HTMLPageURL' => $html_url,
				'ZipFileURL' => $zip_url,
				'ScreenshotURL' => $screenshot_url
			))
		);
	}
	
	/**
	 * @param string $template_id (TemplateID) ID of the template whose details are being requested
	 * @see http://www.campaignmonitor.com/api/method/template-getdetail/
	 */
	function templateGetDetail($template_id)
	{
		return $this->makeCall('Template.GetDetail', array(
			'params' => array(
				'TemplateID' => $template_id
			))
		);
	}

	/**
	 * @param string $template_id (TemplateID) ID of the template to be updated
	 * @param string $template_name (TemplateName) Name of the template
	 * @param string $html_url (HTMLPageURL) URL of the HTML page you have created for the template
	 * @param string $zip_url (ZipFileURL) URL of a zip file containing any other files required by the template
	 * @param string $screenshot_url (ScreenshotURL) URL of a screenshot of the template
	 * @see http://www.campaignmonitor.com/api/method/template-update/
	 */
	function templateUpdate($template_id, $template_name, $html_url, $zip_url, $screenshot_url)
	{
		return $this->makeCall('Template.Update', array(
			'params' => array(
				'TemplateID' => $template_id,
				'TemplateName' => $template_name,
				'HTMLPageURL' => $html_url,
				'ZIPFileURL' => $zip_url,
				'ScreenshotURL' => $screenshot_url
			))
		);
	}

	/**
	 * @param string $template_id (TemplateID) ID of the template to be deleted
	 * @see http://www.campaignmonitor.com/api/method/template-delete/
	 */
	function templateDelete($template_id)
	{
		return $this->makeCall('Template.Delete', array(
			'params' => array(
				'TemplateID' => $template_id
			))
		);
	}
}