diff --git a/AdminMondialRelay.php b/AdminMondialRelay.php index 8005fcd..37ac44c 100644 --- a/AdminMondialRelay.php +++ b/AdminMondialRelay.php @@ -30,141 +30,141 @@ class AdminMondialRelay extends AdminTab { - private $mondialrelay = null; - - public $post_errors = array(); - - public function __construct() - { - $this->mondialrelay = new MondialRelay(); - - $this->table = 'mr_selected'; - - parent::__construct(); - $this->context = Context::getContext(); - $datas = array( - 'display_header' => true, - 'display_header_javascript' => true, - 'display_footer' => true, - ); - - $this->context->smarty->assign($datas); - } - - private function displayOrdersTable() - { - $order_state = new OrderState((int)(Configuration::get('MONDIAL_RELAY_ORDER_STATE')), $this->context->language->id); - $orders = MondialRelay::getOrders(array(), MondialRelay::NO_FILTER, $this->mondialrelay->account_shop['MR_WEIGHT_COEFFICIENT']); - - // Simulate a ticket generation - $MRCreateTicket = new MRCreateTickets(array( - 'orderIdList' => null, - 'totalOrder' => null, - 'weightList' => null - ), - $this->mondialrelay - ); - if (is_array($orders) && count($orders)) - foreach ($orders as &$order) - { - $order['display_total_price'] = Tools::displayPrice($order['total'], new Currency($order['id_currency'])); - $order['display_shipping_price'] = Tools::displayPrice($order['shipping'], new Currency($order['id_currency'])); - if (version_compare(_PS_VERSION_, '1.5.5', '<')) - $order['display_date'] = Tools::displayDate($order['date'], $this->context->language->id); - else - $order['display_date'] = Tools::displayDate($order['date']); - $order['weight'] = (!empty($order['mr_weight']) && $order['mr_weight'] > 0) ? $order['mr_weight'] : $order['order_weight']; - } - - $controller = (_PS_VERSION_ < '1.5') ? 'AdminContact' : 'AdminStores'; - - $this->context->smarty->assign(array( - 'MR_token_admin_module' => Tools::getAdminToken('AdminModules'.(int)(Tab::getIdFromClassName('AdminModules')).(int)$this->context->employee->id), - 'MR_token_admin_contact' => array( - 'controller_name' => $controller, - 'token' => Tools::getAdminToken($controller.(int)(Tab::getIdFromClassName($controller)).(int)$this->context->employee->id)), - 'MR_token_admin_orders' => Tools::getAdminToken('AdminOrders'.(int)(Tab::getIdFromClassName('AdminOrders')).(int)$this->context->employee->id), - 'MR_order_state_name' => $order_state->name, - 'MR_orders' => $orders, - 'MR_PS_IMG_DIR_' => _PS_IMG_DIR_, - 'MR_errors_type' => $MRCreateTicket->checkPreValidation()) - ); - - unset($order_state); - echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'generate_tickets'); - } - - public function displayhistoriqueForm() - { - $query = 'SELECT * FROM `'._DB_PREFIX_.'mr_history` ORDER BY `id` DESC ;'; - $history = Db::getInstance()->executeS($query); - - foreach ($history as &$item) - $item['url_10x15'] = str_replace('format=A4', 'format=10x15', $item['url_a4']); - - $this->context->smarty->assign(array( - 'MR_histories' => $history) - ); - echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'history'); - } - - public function displaySettings($post_action) - { - $curr_order_state = new OrderState((int)$this->mondialrelay->account_shop['MR_ORDER_STATE']); - $order_state = array( - 'id_order_state' => $this->mondialrelay->account_shop['MR_ORDER_STATE'], - 'name' => $curr_order_state->name[$this->context->language->id] - ); - - $this->context->smarty->assign(array( - 'MR_token_admin_mondialrelay' => Tools::getAdminToken('AdminMondialRelay'.(int)(Tab::getIdFromClassName('AdminMondialRelay')).(int)$this->context->employee->id), - 'MR_account_set' => MondialRelay::isAccountSet(), - 'MR_order_state' => $order_state, - 'MR_orders_states_list' => OrderState::getOrderStates($this->context->language->id), - 'MR_form_action' => $post_action, - 'MR_error_list' => $this->post_errors - )); - - echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'settings'); - } - - public function postProcess() - { - $post_action = array( - 'type' => Tools::getValue('MR_action_name'), - 'message_success' => $this->l('Action Succeed'), - 'had_errors' => false - ); - - parent::postProcess(); - - if (Tools::isSubmit('submit_order_state')) - if (($order_state = (int)Tools::getValue('id_order_state'))) - { - $this->mondialrelay->account_shop['MR_ORDER_STATE'] = $order_state; - - if ($this->mondialrelay->updateAccountShop()) - $post_action['message_success'] = $this->l('Order State has been updated'); - else - $this->post_errors[] = $this->l('Cannot Update the account shop'); - } - - if (count($this->post_errors)) - $post_action['had_errors'] = true; - - return $post_action; - } - - public function display() - { - - $post_action = count($_POST) ? $this->postProcess() : null; - - $this->displaySettings($post_action); - if (MondialRelay::isAccountSet() && (int)$this->mondialrelay->account_shop['MR_ORDER_STATE']) - { - $this->displayOrdersTable(); - $this->displayhistoriqueForm(); - } - } + private $mondialrelay = null; + + public $post_errors = array(); + + public function __construct() + { + $this->mondialrelay = new MondialRelay(); + + $this->table = 'mr_selected'; + + parent::__construct(); + $this->context = Context::getContext(); + $datas = array( + 'display_header' => true, + 'display_header_javascript' => true, + 'display_footer' => true, + ); + + $this->context->smarty->assign($datas); + } + + private function displayOrdersTable() + { + $order_state = new OrderState((int)(Configuration::get('MONDIAL_RELAY_ORDER_STATE')), $this->context->language->id); + $orders = MondialRelay::getOrders(array(), MondialRelay::NO_FILTER, $this->mondialrelay->account_shop['MR_WEIGHT_COEFFICIENT']); + + // Simulate a ticket generation + $MRCreateTicket = new MRCreateTickets(array( + 'orderIdList' => null, + 'totalOrder' => null, + 'weightList' => null + ), + $this->mondialrelay + ); + if (is_array($orders) && count($orders)) + foreach ($orders as &$order) + { + $order['display_total_price'] = Tools::displayPrice($order['total'], new Currency($order['id_currency'])); + $order['display_shipping_price'] = Tools::displayPrice($order['shipping'], new Currency($order['id_currency'])); + if (version_compare(_PS_VERSION_, '1.5.5', '<')) + $order['display_date'] = Tools::displayDate($order['date'], $this->context->language->id); + else + $order['display_date'] = Tools::displayDate($order['date']); + $order['weight'] = (!empty($order['mr_weight']) && $order['mr_weight'] > 0) ? $order['mr_weight'] : $order['order_weight']; + } + + $controller = (_PS_VERSION_ < '1.5') ? 'AdminContact' : 'AdminStores'; + + $this->context->smarty->assign(array( + 'MR_token_admin_module' => Tools::getAdminToken('AdminModules'.(int)(Tab::getIdFromClassName('AdminModules')).(int)$this->context->employee->id), + 'MR_token_admin_contact' => array( + 'controller_name' => $controller, + 'token' => Tools::getAdminToken($controller.(int)(Tab::getIdFromClassName($controller)).(int)$this->context->employee->id)), + 'MR_token_admin_orders' => Tools::getAdminToken('AdminOrders'.(int)(Tab::getIdFromClassName('AdminOrders')).(int)$this->context->employee->id), + 'MR_order_state_name' => $order_state->name, + 'MR_orders' => $orders, + 'MR_PS_IMG_DIR_' => _PS_IMG_DIR_, + 'MR_errors_type' => $MRCreateTicket->checkPreValidation()) + ); + + unset($order_state); + echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'generate_tickets'); + } + + public function displayhistoriqueForm() + { + $query = 'SELECT * FROM `'._DB_PREFIX_.'mr_history` ORDER BY `id` DESC ;'; + $history = Db::getInstance()->executeS($query); + + foreach ($history as &$item) + $item['url_10x15'] = str_replace('format=A4', 'format=10x15', $item['url_a4']); + + $this->context->smarty->assign(array( + 'MR_histories' => $history) + ); + echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'history'); + } + + public function displaySettings($post_action) + { + $curr_order_state = new OrderState((int)$this->mondialrelay->account_shop['MR_ORDER_STATE']); + $order_state = array( + 'id_order_state' => $this->mondialrelay->account_shop['MR_ORDER_STATE'], + 'name' => $curr_order_state->name[$this->context->language->id] + ); + + $this->context->smarty->assign(array( + 'MR_token_admin_mondialrelay' => Tools::getAdminToken('AdminMondialRelay'.(int)(Tab::getIdFromClassName('AdminMondialRelay')).(int)$this->context->employee->id), + 'MR_account_set' => MondialRelay::isAccountSet(), + 'MR_order_state' => $order_state, + 'MR_orders_states_list' => OrderState::getOrderStates($this->context->language->id), + 'MR_form_action' => $post_action, + 'MR_error_list' => $this->post_errors + )); + + echo $this->mondialrelay->fetchTemplate('/views/templates/admin/', 'settings'); + } + + public function postProcess() + { + $post_action = array( + 'type' => Tools::getValue('MR_action_name'), + 'message_success' => $this->l('Action Succeed'), + 'had_errors' => false + ); + + parent::postProcess(); + + if (Tools::isSubmit('submit_order_state')) + if (($order_state = (int)Tools::getValue('id_order_state'))) + { + $this->mondialrelay->account_shop['MR_ORDER_STATE'] = $order_state; + + if ($this->mondialrelay->updateAccountShop()) + $post_action['message_success'] = $this->l('Order State has been updated'); + else + $this->post_errors[] = $this->l('Cannot Update the account shop'); + } + + if (count($this->post_errors)) + $post_action['had_errors'] = true; + + return $post_action; + } + + public function display() + { + + $post_action = count($_POST) ? $this->postProcess() : null; + + $this->displaySettings($post_action); + if (MondialRelay::isAccountSet() && (int)$this->mondialrelay->account_shop['MR_ORDER_STATE']) + { + $this->displayOrdersTable(); + $this->displayhistoriqueForm(); + } + } } diff --git a/ajax.php b/ajax.php index 753a007..4c0d9f4 100644 --- a/ajax.php +++ b/ajax.php @@ -32,19 +32,20 @@ */ /* -* Clean displayed content for Admin ajax query +* Clean displayed content for Admin ajax query */ -@ob_clean(); +if ( ob_get_level() && ob_get_length() > 0) + @ob_clean(); /* -* Front Ajax query, need the front cookie and MR class -* When it's back query, the PS core made the work +* Front Ajax query, need the front cookie and MR class +* When it's back query, the PS core made the work */ if (!defined('_PS_ADMIN_DIR_')) { - require_once(realpath(dirname(__FILE__).'/../../config/config.inc.php')); - require_once(realpath(dirname(__FILE__).'/../../init.php')); - require_once(dirname(__FILE__).'/mondialrelay.php'); + require_once(realpath(dirname(__FILE__).'/../../config/config.inc.php')); + require_once(realpath(dirname(__FILE__).'/../../init.php')); + require_once(dirname(__FILE__).'/mondialrelay.php'); } $mondialrelay = isset($this) ? $this : new Mondialrelay(); @@ -62,18 +63,18 @@ /* Access page List liable to the generated token*/ $accessPageList = array( - MondialRelay::getToken('front') => array( - 'MRGetRelayPoint', - 'addSelectedCarrierToDB' - ), - MondialRelay::getToken('back') => array( - 'MRGetTickets', - 'MRCreateTickets', - 'MRDeleteHistory', - 'uninstallDetail', - 'DeleteHistory', - 'MRDownloadPDF' - ) + MondialRelay::getToken('front') => array( + 'MRGetRelayPoint', + 'addSelectedCarrierToDB' + ), + MondialRelay::getToken('back') => array( + 'MRGetTickets', + 'MRCreateTickets', + 'MRDeleteHistory', + 'uninstallDetail', + 'DeleteHistory', + 'MRDownloadPDF' + ) ); $params = array(); @@ -82,73 +83,74 @@ /* If the method name associated to the token received doesn't match with*/ /* the list, then we kill the request*/ if (!isset($accessPageList[$token]) || !in_array($method, $accessPageList[$token])) - exit(); + exit(); /* Method name allow to instanciate his object to properly call the*/ /* implemented interface method and do his job*/ switch ($method) { - case 'MRCreateTickets': - $params['orderIdList'] = Tools::getValue('order_id_list'); - $params['totalOrder'] = Tools::getValue('numSelected'); - $params['weightList'] = Tools::getValue('weight_list'); - $params['insuranceList'] = Tools::getValue('insurance_list'); - break; - case 'MRGetTickets': - $params['detailedExpeditionList'] = Tools::getValue('detailedExpeditionList'); - break; - case 'MRDownloadPDF': - $params['Expeditions'] = Tools::getValue('detailedExpeditionList'); - break; - case 'DeleteHistory': - $params['historyIdList'] = Tools::getValue('history_id_list'); - break; - case 'uninstallDetail': - $params['action'] = Tools::getValue('action'); - break; - case 'MRGetRelayPoint': - $params['id_carrier'] = Tools::getValue('id_carrier'); - $params['weight'] = Context::getContext()->cart->getTotalWeight(); - $params['id_address_delivery'] = Context::getContext()->cart->id_address_delivery; - break; - case 'addSelectedCarrierToDB': - $params['id_carrier'] = Tools::getValue('id_carrier'); - $params['id_cart'] = Context::getContext()->cart->id; - $params['id_customer'] = Context::getContext()->customer->id; - $params['id_mr_method'] = Tools::getValue('id_mr_method'); - $params['relayPointInfo'] = Tools::getValue('relayPointInfo'); - break; - default: + case 'MRCreateTickets': + $params['orderIdList'] = Tools::getValue('order_id_list'); + $params['totalOrder'] = Tools::getValue('numSelected'); + $params['weightList'] = Tools::getValue('weight_list'); + $params['insuranceList'] = Tools::getValue('insurance_list'); + break; + case 'MRGetTickets': + $params['detailedExpeditionList'] = Tools::getValue('detailedExpeditionList'); + break; + case 'MRDownloadPDF': + $params['Expeditions'] = Tools::getValue('detailedExpeditionList'); + break; + case 'DeleteHistory': + $params['historyIdList'] = Tools::getValue('history_id_list'); + break; + case 'uninstallDetail': + $params['action'] = Tools::getValue('action'); + break; + case 'MRGetRelayPoint': + $params['id_carrier'] = Tools::getValue('id_carrier'); + $params['mode_liv'] = Tools::getValue('mode_liv'); + $params['weight'] = Context::getContext()->cart->getTotalWeight(); + $params['id_address_delivery'] = Context::getContext()->cart->id_address_delivery; + break; + case 'addSelectedCarrierToDB': + $params['id_carrier'] = Tools::getValue('id_carrier'); + $params['id_cart'] = Context::getContext()->cart->id; + $params['id_customer'] = Context::getContext()->customer->id; + $params['id_mr_method'] = Tools::getValue('id_mr_method'); + $params['relayPointInfo'] = Tools::getValue('relayPointInfo'); + break; + default: } - + /* Try to instanciate the method object name and call the necessaries method*/ try { - if (class_exists($method, false)) - { - /* $this is the current mondialrelay object loaded when use in BO. Use for perf*/ - $obj = new $method($params, $mondialrelay); - - /* Verify that the class implement correctly the interface*/ - /* Else use a Management class to do some ajax stuff*/ - if (($obj instanceof IMondialRelayWSMethod)) - { - $obj->init(); - $obj->send(); - $result = $obj->getResult(); - } - unset($obj); - } - else if (($management = new MRManagement($params)) && method_exists($management, $method)) - $result = $management->{$method}(); - else - throw new Exception('Method Class : '.$method.' can\'t be found'); - unset($management); + if (class_exists($method, false)) + { + /* $this is the current mondialrelay object loaded when use in BO. Use for perf*/ + $obj = new $method($params, $mondialrelay); + + /* Verify that the class implement correctly the interface*/ + /* Else use a Management class to do some ajax stuff*/ + if (($obj instanceof IMondialRelayWSMethod)) + { + $obj->init(); + $obj->send(); + $result = $obj->getResult(); + } + unset($obj); + } + else if (($management = new MRManagement($params)) && method_exists($management, $method)) + $result = $management->{$method}(); + else + throw new Exception('Method Class : '.$method.' can\'t be found'); + unset($management); } catch(Exception $e) { - echo MRTools::jsonEncode(array('other' => array('error' => array($e->getMessage())))); - exit(-1); + echo MRTools::jsonEncode(array('other' => array('error' => array($e->getMessage())))); + exit(-1); } echo MRTools::jsonEncode($result); exit(0); diff --git a/classes/IMondialRelayWSMethod.php b/classes/IMondialRelayWSMethod.php index 0b0e82e..30d19da 100644 --- a/classes/IMondialRelayWSMethod.php +++ b/classes/IMondialRelayWSMethod.php @@ -33,25 +33,25 @@ */ interface IMondialRelayWSMethod { - /* - * Initiate the data needed to be send properly - * Can manage a list of data for multiple request - */ - public function init(); - - /* - * Send one or multiple request to the webservice - */ - public function send(); - - /* - * Get the values with associated fields name - */ - public function getFieldsList(); - - /* - * Get the result of one or multiple send request - */ - public function getResult(); + /* + * Initiate the data needed to be send properly + * Can manage a list of data for multiple request + */ + public function init(); + + /* + * Send one or multiple request to the webservice + */ + public function send(); + + /* + * Get the values with associated fields name + */ + public function getFieldsList(); + + /* + * Get the result of one or multiple send request + */ + public function getResult(); } ?> \ No newline at end of file diff --git a/classes/MRCreateTickets.php b/classes/MRCreateTickets.php index ba9e257..08d7715 100644 --- a/classes/MRCreateTickets.php +++ b/classes/MRCreateTickets.php @@ -35,597 +35,597 @@ */ class MRCreateTickets implements IMondialRelayWSMethod { - public $class_name = __CLASS__; - - private $_fields = array( - 'id_mr_selected' => 0, - 'list' => array( - 'Enseigne' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'ModeCol' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^(CCC|CDR|CDS|REL)$#'), - 'ModeLiv' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^(LCC|LD1|LDS|24R|ESP|DRI|HOM)$#'), - 'NDossier' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9A-Z_ -]{0,15})$#'), - 'NClient' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9A-Z]{0,9})$#'), - 'Expe_Langage' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Expe_Ad1' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), - 'Expe_Ad2' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), - 'Expe_Ad3' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), - 'Expe_Ad4' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'Expe_Ville' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,26}$#'), - 'Expe_CP' => array( - 'required' => true, - 'value' => '', - 'params' => array(), - 'methodValidation' => 'checkZipcodeByCountry'), - 'Expe_Pays' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Expe_Tel1' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), - 'Expe_Tel2' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), - 'Expe_Mail' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[\w\-\.\@_]{0,70}$#'), - 'Dest_Langage' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Dest_Ad1' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), - 'Dest_Ad2' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), - 'Dest_Ad3' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), - 'Dest_Ad4' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), - 'Dest_Ville' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,26}$#'), - 'Dest_CP' => array( - 'required' => true, - 'value' => '', - 'params' => array(), - 'methodValidation' => 'checkZipcodeByCountry'), - 'Dest_Pays' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Dest_Tel1' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), - 'Dest_Tel2' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), - 'Dest_Mail' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[\w\-\.\@_]{0,70}$#'), - 'Poids' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{3,7}$#'), - 'Longueur' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9]{0,3}$#'), - 'Taille' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(XS|S|M|L|XL|XXL|3XL)$#'), - 'NbColis' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{1,2}$#'), - 'CRT_Valeur' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{1,7}$#'), - 'CRT_Devise' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|EUR)$#'), - 'Exp_Valeur' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9]{0,7}$#'), - 'Exp_Devise' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|EUR)$#'), - 'COL_Rel_Pays' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'COL_Rel' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9]{6})$#'), - 'LIV_Rel_Pays' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'LIV_Rel' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9]{6})$#'), - 'TAvisage' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|O|N)$#'), - 'TReprise' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|O|N)$#'), - 'Montage' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9]{1,3})$#'), - 'TRDV' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|O|N)$#'), - 'Assurance' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(|[0-9A-Z]{1})$#'), - 'Instructions' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,31}#'), - 'Security' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{32}$#'), - 'Texte' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^([^<>&\']{3,30})(\(cr\)[^<>&\']{0,30}){0,9}$#'))); - - private $_orderListId = null; - private $_totalOrder = 0; - private $_weightList = null; - private $_insuranceList = null; - private $_mondialrelay = null; - private $_fieldsList = array(); - private $_webServiceKey = ''; - private $_markCode = ''; - - private $_resultList = array( - 'error' => array(), - 'success' => array()); - - private $_webserviceURL = ''; - - public function __construct($params, $object) - { - $this->_orderListId = $params['orderIdList']; - $this->_totalOrder = $params['totalOrder']; - $this->_weightList = $params['weightList']; - $this->_insuranceList = isset($params['insuranceList']) ? $params['insuranceList'] : ''; - $this->_mondialrelay = $object; - $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; - $this->_markCode = $this->_mondialrelay->account_shop['MR_CODE_MARQUE']; - $this->class_name = Tools::strtolower($this->class_name); - - $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - } - - public function __destruct() - { - unset($this->_mondialrelay); - } - - /* - * Build a correct weight format (NNNNN) - */ - private function _weightFormat($weight) - { - return sprintf("%05s", $weight); - } - - /* - * Set the default value to the order paramaters - */ - private function _setRequestDefaultValue() - { - $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; - $this->_fields['list']['Expe_Langage']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; - $this->_fields['list']['Expe_Ad1']['value'] = Configuration::get('PS_SHOP_NAME'); - $this->_fields['list']['Expe_Ad3']['value'] = Configuration::get('PS_SHOP_ADDR1'); - // Deleted, cause to many failed for the process - // $this->_fields['list']['Expe_Ad4']['value'] = Configuration::get('PS_SHOP_ADDR2'); - $this->_fields['list']['Expe_Ville']['value'] = Configuration::get('PS_SHOP_CITY'); - $this->_fields['list']['Expe_CP']['value'] = Configuration::get('PS_SHOP_CODE'); - $this->_fields['list']['Expe_CP']['params']['id_country'] = Configuration::get('PS_COUNTRY_DEFAULT'); - - if (version_compare(_PS_VERSION_, '1.4', '>=')) - { - $this->_fields['list']['Expe_Pays']['value'] = Country::getIsoById(Configuration::get('PS_SHOP_COUNTRY_ID')); - $this->_fields['list']['Expe_CP']['params']['id_country'] = Configuration::get('PS_SHOP_COUNTRY_ID'); - } - else - $this->_fields['list']['Expe_Pays']['value'] = Tools::substr(Configuration::get('PS_SHOP_COUNTRY'), 0, 2); - $this->_fields['list']['Expe_Tel1']['value'] = MRTools::getFormatedPhone(Configuration::get('PS_SHOP_PHONE')); - $this->_fields['list']['Expe_Mail']['value'] = Configuration::get('PS_SHOP_EMAIL'); - $this->_fields['list']['NbColis']['value'] = 1; - $this->_fields['list']['CRT_Valeur']['value'] = 0; - $this->_fields['list']['CRT_Devise']['value'] = 'EUR'; - } - - /* - * Initiate the data needed to be send properly - * Can manage a list of data for multiple request - */ - public function init() - { - if ($this->_totalOrder == 0) - throw new Exception($this->_mondialrelay->l('Please select at least one order', $this->class_name)); - - $this->_setRequestDefaultValue(); - if (count($orderListDetails = $this->_mondialrelay->getOrders($this->_orderListId))) - { - foreach ($orderListDetails as $orderDetail) - { - // Storage temporary - $base = $this->_fields; - $tmp = &$base['list']; - - $deliveriesAddress = new Address($orderDetail['id_address_delivery']); - $customer = new Customer($orderDetail['id_customer']); - - // Store the weight order set by the user - - foreach ($this->_weightList as $orderWeightInfos) - { - $detail = explode('-', $orderWeightInfos); - if (count($detail) == 2 && $detail[1] == $orderDetail['id_order']) - $tmp['Poids']['value'] = (float)$this->_weightFormat($detail[0]); - } - foreach ($this->_insuranceList as $insurance) - { - $detail = explode('-', $insurance); - if (count($detail) == 2 && $detail[1] == $orderDetail['id_order']) - $tmp['Assurance']['value'] = (int)($detail[0]); - else - $tmp['Assurance']['value'] = $orderDetail['mr_ModeAss']; - } - $dest_tel = (!empty($deliveriesAddress->phone)) ? MRTools::getFormatedPhone($deliveriesAddress->phone) : ''; - $dest_tel2 = (!empty($deliveriesAddress->phone_mobile)) ? MRTools::getFormatedPhone($deliveriesAddress->phone_mobile) : ''; - $destIsoCode = Country::getIsoById($deliveriesAddress->id_country); - $tmp['ModeCol']['value'] = $orderDetail['mr_ModeCol']; - $tmp['ModeLiv']['value'] = $orderDetail['mr_ModeLiv']; - $tmp['NDossier']['value'] = $orderDetail['id_order']; - $tmp['NClient']['value'] = $orderDetail['id_customer']; - $tmp['Dest_Langage']['value'] = 'FR'; //Language::getIsoById($orderDetail['id_lang']); - $tmp['Dest_Ad1']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->firstname.' '.$deliveriesAddress->lastname), 0, 32)); - $tmp['Dest_Ad2']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->address2), 0, 32)); - $tmp['Dest_Ad3']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->address1), 0, 32)); - $tmp['Dest_Ville']['value'] = $deliveriesAddress->city; - $tmp['Dest_CP']['value'] = $deliveriesAddress->postcode; - $tmp['Dest_CP']['params']['id_country'] = $deliveriesAddress->id_country; - $tmp['Dest_Pays']['value'] = $destIsoCode; - $tmp['Dest_Tel1']['value'] = $dest_tel; - $tmp['Dest_Tel2']['value'] = $dest_tel2; - $tmp['Dest_Mail']['value'] = $customer->email; - if ($orderDetail['mr_ModeLiv'] != 'LD1' && $orderDetail['mr_ModeLiv'] != 'LDS' && $orderDetail['mr_ModeLiv'] != 'HOM') - { - $tmp['LIV_Rel_Pays']['value'] = $orderDetail['MR_Selected_Pays']; - $tmp['LIV_Rel']['value'] = $orderDetail['MR_Selected_Num']; - } - - // Store the necessary information to the root case table - $base['id_mr_selected'] = $orderDetail['id_mr_selected']; - - // Add the temporary values to a field list for multiple request - $this->_fieldsList[] = $base; - unset($deliveriesAddress); - unset($customer); - } - $this->_generateMD5SecurityKey(); - } - } - - /* - * Generate the MD5 key for each param list - */ - private function _generateMD5SecurityKey() - { - // RootCase is the array case where the main information are stored - // it's an array containing id_mr_selected and an array with the necessary fields - foreach ($this->_fieldsList as &$rootCase) - { - $concatenationValue = ''; - foreach ($rootCase['list'] as $paramName => &$valueDetailed) - if ($paramName != 'Texte' && $paramName != 'Security') - { - // Mac server make an empty string instead of a cleaned string - // TODO : test on windows and linux server - $cleanedString = MRTools::removeAccents($valueDetailed['value']); - $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); - - // Call a pointer function if exist to do different test - if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::$valueDetailed['methodValidation']($valueDetailed['value'], $valueDetailed['params'])) - $concatenationValue .= $valueDetailed['value']; - // Use simple Regex test given by MondialRelay - else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) - $concatenationValue .= $valueDetailed['value']; - // If the key is required, we set an error, else it's skipped - else if ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) - { - if (empty($valueDetailed['value'])) - $error = $this->_mondialrelay->l('This key', $this->class_name).' ['.$paramName.'] '.$this->_mondialrelay->l('is empty and need to be filled', $this->class_name); - else - $error = 'This key ['.$paramName.'] hasn not a valid value format : '.$valueDetailed['value']; - $this->_resultList['error'][$rootCase['list']['NDossier']['value']][] = $error; - } - } - $concatenationValue .= $this->_webServiceKey; - $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); - } - } - - /* - * Update the tables used and send mail with the order history - */ - private function _updateTable($params, $expeditionNum, $ticketURL, $trackingURL, $id_mr_selected) - { - $sql = ' - UPDATE `'._DB_PREFIX_.'mr_selected` - SET `MR_poids` = \''.pSQL($params['Poids']).'\', - `MR_insurance` = \''.pSQL($params['Assurance']).'\', - `exp_number` = \''.pSQL($expeditionNum).'\', - `url_etiquette` = \''.pSQL($ticketURL).'\', - `url_suivi` = \''.pSQL($trackingURL).'\' - WHERE id_mr_selected = '.(int)$id_mr_selected; - Db::getInstance()->execute($sql); - - // NDossier contains the id_order - $order = new Order($params['NDossier']); - - // Update the database for order and orderHistory - $order->shipping_number = $expeditionNum; - $order->update(); - - if (version_compare(_PS_VERSION_, '1.5', '>=')) - { - //Retrieve Order Carrier - $sql = 'SELECT `id_order_carrier` - FROM `'._DB_PREFIX_.'order_carrier` - WHERE `id_order` = '.(int)$order->id; - - $id_order_carrier = Db::getInstance()->getValue($sql); - - if($id_order_carrier) - { - $order_carrier = new OrderCarrier((int)$id_order_carrier); - if(Validate::isLoadedObject($order_carrier)) - { - $order_carrier->tracking_number = pSQL($expeditionNum); - $order_carrier->update(); - } - } - } - - $templateVars = array('{followup}' => $trackingURL); - $orderState = (Configuration::get('PS_OS_SHIPPING')) ? - Configuration::get('PS_OS_SHIPPING') : - _PS_OS_SHIPPING_; - - $history = new OrderHistory(); - $history->id_order = (int)$params['NDossier']; - if (version_compare(_PS_VERSION_, '1.5.2', '>=')) - $history->changeIdOrderState((int)$orderState, $order); - else - $history->changeIdOrderState((int)$orderState, (int)($params['NDossier'])); - $history->id_employee = (isset(Context::getContext()->employee->id) ? (int)Context::getContext()->employee->id : ''); - $history->addWithemail(true, $templateVars); - unset($order); - unset($order_carrier); - unset($history); - } - - /* - * Manage the return value of the webservice, handle the errors or build the - * succeed message - */ - private function _parseResult($client, $result, $params, $id_mr_selected) - { - $errors = &$this->_resultList['error'][$params['NDossier']]; - $success = &$this->_resultList['success'][$params['NDossier']]; - $result = $result->WSI2_CreationEtiquetteResult; - if (($errorNumber = $result->STAT) != 0) - { - $errors[] = $this->_mondialrelay->l('There is an error number : ', $this->class_name).$errorNumber; - $errors[] = $this->_mondialrelay->l('Details : ', $this->class_name). - $this->_mondialrelay->getErrorCodeDetail($errorNumber); - } - else - { - $baseURL = MondialRelay::MR_URL; - $expedition = $result->ExpeditionNum; - $securityKey = Tools::strtoupper(md5('<'.$params['Enseigne'].$this->_markCode. - '>'.$expedition.'<'.$this->_webServiceKey.'>')); - $ticketURL = $baseURL.$result->URL_Etiquette; - $trackingURL = $baseURL. - 'public/permanent/tracking.aspx?ens='.$params['Enseigne'].$this->_markCode.'&exp='.$expedition.'&language='.Configuration::get('PS_LANG_DEFAULT').'&crc='.$securityKey; - - $success['displayExpedition'] = $this->_mondialrelay->l('Expedition Number : ', $this->class_name).$expedition; - $success['displayTicketURL'] = $this->_mondialrelay->l('Ticket URL : ', $this->class_name).$ticketURL; - $success['displayTrackingURL'] = $this->_mondialrelay->l('Tracking URL: ', $this->class_name).$trackingURL; - $success['expeditionNumber'] = $expedition; - - $this->_updateTable($params, $expedition, $ticketURL, $trackingURL, $id_mr_selected); - } - } - - /* - * Send one or multiple request to the webservice - */ - public function send() - { - if ($client = new SoapClient($this->_webserviceURL)) - { - $client->soap_defencoding = 'UTF-8'; - $client->decode_utf8 = false; - - foreach ($this->_fieldsList as $rootCase) - { - $params = $this->_getSimpleParamArray($rootCase['list']); - $result = $client->WSI2_CreationEtiquette($params); - $this->_parseResult($client, $result, $params, $rootCase['id_mr_selected']); - } - unset($client); - Configuration::updateValue('MONDIALRELAY_CONFIGURATION_OK', true); - } - else - throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice is not currently reliable', $this->class_name)); - } - - /* - ** Check if the shop parameter are currently well configured - */ - public function checkPreValidation() - { - $errorList = array('error' => array(), 'warn' => array()); - - $list = array( - 'Expe_Langage' => array( - 'value' => $this->_mondialrelay->account_shop['MR_LANGUAGE'], - 'error' => $this->_mondialrelay->l('Please check your language configuration', $this->class_name)), - 'Expe_Ad1' => array( - 'value' => Configuration::get('PS_SHOP_NAME'), - 'error' => $this->_mondialrelay->l('Please check your shop name configuration', $this->class_name)), - 'Expe_Ad3' => array( - 'value' => Configuration::get('PS_SHOP_ADDR1'), - 'error' => $this->_mondialrelay->l('Please check your address 1 configuration', $this->class_name)), - 'Expe_Ville' => array( - 'value' => Configuration::get('PS_SHOP_CITY'), - 'error' => $this->_mondialrelay->l('Please check your city configuration', $this->class_name)), - 'Expe_CP' => array( - 'value' => Configuration::get('PS_SHOP_CODE'), - 'error' => $this->_mondialrelay->l('Please check your zipcode configuration', $this->class_name)), - 'Expe_Pays' => array( - 'value' => ((version_compare(_PS_VERSION_, '1.4', '>=')) ? - Country::getIsoById(Configuration::get('PS_SHOP_COUNTRY_ID')) : - Tools::substr(Configuration::get('PS_SHOP_COUNTRY'), 0, 2)), - 'error' => $this->_mondialrelay->l('Please check your country configuration', $this->class_name)), - 'Expe_Tel1' => array( - 'value' => MRTools::getFormatedPhone(Configuration::get('PS_SHOP_PHONE')), - 'error' => $this->_mondialrelay->l('Please check your Phone configuration', $this->class_name)), - 'Expe_Mail' => array( - 'value' => Configuration::get('PS_SHOP_EMAIL'), - 'error' => $this->_mondialrelay->l('Please check your mail configuration', $this->class_name))); - - foreach ($list as $name => $tab) - { - // Mac server make an empty string instead of a cleaned string - // TODO : test on windows and linux server - $cleanedString = MRTools::removeAccents($tab['value']); - $tab['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($tab['value']); - - if ($name == 'Expe_CP') - { - if (version_compare(_PS_VERSION_, '1.4', '>=')) - { - if (!(MRTools::checkZipcodeByCountry($tab['value'], array( - 'id_country' => Configuration::get('PS_SHOP_COUNTRY_ID'))))) - $errorList['error'][$name] = $tab['error']; - } - else - $errorList['warn'][$name] = $this->_mondialrelay->l('Post code cannot be validated with PrestaShop versions older than 1.4', $this->class_name); - } - else if (isset($this->_fields['list'][$name]['regexValidation']) && - (!preg_match($this->_fields['list'][$name]['regexValidation'], $tab['value'], $matches))) - $errorList['error'][$name] = $tab['error']; - } - return $errorList; - } - - /* - * Get the values with associated fields name - * @fields : array containing multiple values information - */ - private function _getSimpleParamArray($fields) - { - $params = array(); - - foreach ($fields as $keyName => $valueDetailed) - $params[$keyName] = $valueDetailed['value']; - return $params; - } - - /* - * Return the fields list - */ - public function getFieldsList() - { - return $this->_fieldsList['list']; - } - - /* - * Return the result of one or multiple sent requests - */ - public function getResult() - { - return $this->_resultList; - } - - /* - * Return which number order of the list is currently managed - */ - public static function getCurrentRequestUnderTraitment() - { - // TODO: Build a SQL Query to know how many request have been executed - } + public $class_name = __CLASS__; + + private $_fields = array( + 'id_mr_selected' => 0, + 'list' => array( + 'Enseigne' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'ModeCol' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^(CCC|CDR|CDS|REL)$#'), + 'ModeLiv' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^(LCC|LD1|LDS|24R|ESP|DRI|HOM)$#'), + 'NDossier' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9A-Z_ -]{0,15})$#'), + 'NClient' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9A-Z]{0,9})$#'), + 'Expe_Langage' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Expe_Ad1' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), + 'Expe_Ad2' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), + 'Expe_Ad3' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), + 'Expe_Ad4' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'Expe_Ville' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,26}$#'), + 'Expe_CP' => array( + 'required' => true, + 'value' => '', + 'params' => array(), + 'methodValidation' => 'checkZipcodeByCountry'), + 'Expe_Pays' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Expe_Tel1' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), + 'Expe_Tel2' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), + 'Expe_Mail' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[\w\-\.\@_]{0,70}$#'), + 'Dest_Langage' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Dest_Ad1' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), + 'Dest_Ad2' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), + 'Dest_Ad3' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{2,32}$#'), + 'Dest_Ad4' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,32}$#'), + 'Dest_Ville' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,26}$#'), + 'Dest_CP' => array( + 'required' => true, + 'value' => '', + 'params' => array(), + 'methodValidation' => 'checkZipcodeByCountry'), + 'Dest_Pays' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Dest_Tel1' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), + 'Dest_Tel2' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^((00|\+)[1-9]{2}|0)[0-9][0-9]{7,8}$#'), + 'Dest_Mail' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[\w\-\.\@_]{0,70}$#'), + 'Poids' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{3,7}$#'), + 'Longueur' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9]{0,3}$#'), + 'Taille' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(XS|S|M|L|XL|XXL|3XL)$#'), + 'NbColis' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{1,2}$#'), + 'CRT_Valeur' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{1,7}$#'), + 'CRT_Devise' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|EUR)$#'), + 'Exp_Valeur' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9]{0,7}$#'), + 'Exp_Devise' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|EUR)$#'), + 'COL_Rel_Pays' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'COL_Rel' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9]{6})$#'), + 'LIV_Rel_Pays' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'LIV_Rel' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9]{6})$#'), + 'TAvisage' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|O|N)$#'), + 'TReprise' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|O|N)$#'), + 'Montage' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9]{1,3})$#'), + 'TRDV' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|O|N)$#'), + 'Assurance' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(|[0-9A-Z]{1})$#'), + 'Instructions' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z_\-\'., /]{0,31}#'), + 'Security' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{32}$#'), + 'Texte' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^([^<>&\']{3,30})(\(cr\)[^<>&\']{0,30}){0,9}$#'))); + + private $_orderListId = null; + private $_totalOrder = 0; + private $_weightList = null; + private $_insuranceList = null; + private $_mondialrelay = null; + private $_fieldsList = array(); + private $_webServiceKey = ''; + private $_markCode = ''; + + private $_resultList = array( + 'error' => array(), + 'success' => array()); + + private $_webserviceURL = ''; + + public function __construct($params, $object) + { + $this->_orderListId = $params['orderIdList']; + $this->_totalOrder = $params['totalOrder']; + $this->_weightList = $params['weightList']; + $this->_insuranceList = isset($params['insuranceList']) ? $params['insuranceList'] : ''; + $this->_mondialrelay = $object; + $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; + $this->_markCode = $this->_mondialrelay->account_shop['MR_CODE_MARQUE']; + $this->class_name = Tools::strtolower($this->class_name); + + $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + } + + public function __destruct() + { + unset($this->_mondialrelay); + } + + /* + * Build a correct weight format (NNNNN) + */ + private function _weightFormat($weight) + { + return sprintf("%05s", $weight); + } + + /* + * Set the default value to the order paramaters + */ + private function _setRequestDefaultValue() + { + $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; + $this->_fields['list']['Expe_Langage']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; + $this->_fields['list']['Expe_Ad1']['value'] = Configuration::get('PS_SHOP_NAME'); + $this->_fields['list']['Expe_Ad3']['value'] = Configuration::get('PS_SHOP_ADDR1'); + // Deleted, cause to many failed for the process + // $this->_fields['list']['Expe_Ad4']['value'] = Configuration::get('PS_SHOP_ADDR2'); + $this->_fields['list']['Expe_Ville']['value'] = Configuration::get('PS_SHOP_CITY'); + $this->_fields['list']['Expe_CP']['value'] = Configuration::get('PS_SHOP_CODE'); + $this->_fields['list']['Expe_CP']['params']['id_country'] = Configuration::get('PS_COUNTRY_DEFAULT'); + + if (version_compare(_PS_VERSION_, '1.4', '>=')) + { + $this->_fields['list']['Expe_Pays']['value'] = Country::getIsoById(Configuration::get('PS_SHOP_COUNTRY_ID')); + $this->_fields['list']['Expe_CP']['params']['id_country'] = Configuration::get('PS_SHOP_COUNTRY_ID'); + } + else + $this->_fields['list']['Expe_Pays']['value'] = Tools::substr(Configuration::get('PS_SHOP_COUNTRY'), 0, 2); + $this->_fields['list']['Expe_Tel1']['value'] = MRTools::getFormatedPhone(Configuration::get('PS_SHOP_PHONE')); + $this->_fields['list']['Expe_Mail']['value'] = Configuration::get('PS_SHOP_EMAIL'); + $this->_fields['list']['NbColis']['value'] = 1; + $this->_fields['list']['CRT_Valeur']['value'] = 0; + $this->_fields['list']['CRT_Devise']['value'] = 'EUR'; + } + + /* + * Initiate the data needed to be send properly + * Can manage a list of data for multiple request + */ + public function init() + { + if ($this->_totalOrder == 0) + throw new Exception($this->_mondialrelay->l('Please select at least one order', $this->class_name)); + + $this->_setRequestDefaultValue(); + if (count($orderListDetails = $this->_mondialrelay->getOrders($this->_orderListId))) + { + foreach ($orderListDetails as $orderDetail) + { + // Storage temporary + $base = $this->_fields; + $tmp = &$base['list']; + + $deliveriesAddress = new Address($orderDetail['id_address_delivery']); + $customer = new Customer($orderDetail['id_customer']); + + // Store the weight order set by the user + + foreach ($this->_weightList as $orderWeightInfos) + { + $detail = explode('-', $orderWeightInfos); + if (count($detail) == 2 && $detail[1] == $orderDetail['id_order']) + $tmp['Poids']['value'] = (float)$this->_weightFormat($detail[0]); + } + foreach ($this->_insuranceList as $insurance) + { + $detail = explode('-', $insurance); + if ($detail[1] == $orderDetail['id_order']) { + $tmp['Assurance']['value'] = (count($detail) == 2) ? (int)($detail[0]) : $orderDetail['mr_ModeAss']; + } + + } + $dest_tel = (!empty($deliveriesAddress->phone)) ? MRTools::getFormatedPhone($deliveriesAddress->phone) : ''; + $dest_tel2 = (!empty($deliveriesAddress->phone_mobile)) ? MRTools::getFormatedPhone($deliveriesAddress->phone_mobile) : ''; + $destIsoCode = Country::getIsoById($deliveriesAddress->id_country); + $tmp['ModeCol']['value'] = $orderDetail['mr_ModeCol']; + $tmp['ModeLiv']['value'] = $orderDetail['mr_ModeLiv']; + $tmp['NDossier']['value'] = $orderDetail['id_order']; + $tmp['NClient']['value'] = $orderDetail['id_customer']; + $tmp['Dest_Langage']['value'] = 'FR'; //Language::getIsoById($orderDetail['id_lang']); + $tmp['Dest_Ad1']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->firstname.' '.$deliveriesAddress->lastname), 0, 32)); + $tmp['Dest_Ad2']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->address2), 0, 32)); + $tmp['Dest_Ad3']['value'] = preg_replace(MRTools::REGEX_CLEAN_ADDR, '', Tools::substr(MRTools::removeAccents($deliveriesAddress->address1), 0, 32)); + $tmp['Dest_Ville']['value'] = $deliveriesAddress->city; + $tmp['Dest_CP']['value'] = $deliveriesAddress->postcode; + $tmp['Dest_CP']['params']['id_country'] = $deliveriesAddress->id_country; + $tmp['Dest_Pays']['value'] = $destIsoCode; + $tmp['Dest_Tel1']['value'] = $dest_tel; + $tmp['Dest_Tel2']['value'] = $dest_tel2; + $tmp['Dest_Mail']['value'] = $customer->email; + if ($orderDetail['mr_ModeLiv'] != 'LD1' && $orderDetail['mr_ModeLiv'] != 'LDS' && $orderDetail['mr_ModeLiv'] != 'HOM') + { + $tmp['LIV_Rel_Pays']['value'] = $orderDetail['MR_Selected_Pays']; + $tmp['LIV_Rel']['value'] = $orderDetail['MR_Selected_Num']; + } + + // Store the necessary information to the root case table + $base['id_mr_selected'] = $orderDetail['id_mr_selected']; + + // Add the temporary values to a field list for multiple request + $this->_fieldsList[] = $base; + unset($deliveriesAddress); + unset($customer); + } + $this->_generateMD5SecurityKey(); + } + } + + /* + * Generate the MD5 key for each param list + */ + private function _generateMD5SecurityKey() + { + // RootCase is the array case where the main information are stored + // it's an array containing id_mr_selected and an array with the necessary fields + foreach ($this->_fieldsList as &$rootCase) + { + $concatenationValue = ''; + foreach ($rootCase['list'] as $paramName => &$valueDetailed) + if ($paramName != 'Texte' && $paramName != 'Security') + { + // Mac server make an empty string instead of a cleaned string + // TODO : test on windows and linux server + $cleanedString = MRTools::removeAccents($valueDetailed['value']); + $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); + + // Call a pointer function if exist to do different test + if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::{$valueDetailed['methodValidation']}($valueDetailed['value'], $valueDetailed['params'])) + $concatenationValue .= $valueDetailed['value']; + // Use simple Regex test given by MondialRelay + else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) + $concatenationValue .= $valueDetailed['value']; + // If the key is required, we set an error, else it's skipped + else if ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) + { + if (empty($valueDetailed['value'])) + $error = $this->_mondialrelay->l('This key', $this->class_name).' ['.$paramName.'] '.$this->_mondialrelay->l('is empty and need to be filled', $this->class_name); + else + $error = 'This key ['.$paramName.'] hasn not a valid value format : '.$valueDetailed['value']; + $this->_resultList['error'][$rootCase['list']['NDossier']['value']][] = $error; + } + } + $concatenationValue .= $this->_webServiceKey; + $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); + } + } + + /* + * Update the tables used and send mail with the order history + */ + private function _updateTable($params, $expeditionNum, $ticketURL, $trackingURL, $id_mr_selected) + { + $sql = ' + UPDATE `'._DB_PREFIX_.'mr_selected` + SET `MR_poids` = \''.pSQL($params['Poids']).'\', + `MR_insurance` = \''.pSQL($params['Assurance']).'\', + `exp_number` = \''.pSQL($expeditionNum).'\', + `url_etiquette` = \''.pSQL($ticketURL).'\', + `url_suivi` = \''.pSQL($trackingURL).'\' + WHERE id_mr_selected = '.(int)$id_mr_selected; + Db::getInstance()->execute($sql); + + // NDossier contains the id_order + $order = new Order($params['NDossier']); + + // Update the database for order and orderHistory + $order->shipping_number = $expeditionNum; + $order->update(); + + if (version_compare(_PS_VERSION_, '1.5', '>=')) + { + //Retrieve Order Carrier + $sql = 'SELECT `id_order_carrier` + FROM `'._DB_PREFIX_.'order_carrier` + WHERE `id_order` = '.(int)$order->id; + + $id_order_carrier = Db::getInstance()->getValue($sql); + + if($id_order_carrier) + { + $order_carrier = new OrderCarrier((int)$id_order_carrier); + if(Validate::isLoadedObject($order_carrier)) + { + $order_carrier->tracking_number = pSQL($expeditionNum); + $order_carrier->update(); + } + } + } + + $templateVars = array('{followup}' => $trackingURL); + $orderState = (Configuration::get('PS_OS_SHIPPING')) ? + Configuration::get('PS_OS_SHIPPING') : + _PS_OS_SHIPPING_; + + $history = new OrderHistory(); + $history->id_order = (int)$params['NDossier']; + if (version_compare(_PS_VERSION_, '1.5.2', '>=')) + $history->changeIdOrderState((int)$orderState, $order); + else + $history->changeIdOrderState((int)$orderState, (int)($params['NDossier'])); + $history->id_employee = (isset(Context::getContext()->employee->id) ? (int)Context::getContext()->employee->id : ''); + $history->addWithemail(true, $templateVars); + unset($order); + unset($order_carrier); + unset($history); + } + + /* + * Manage the return value of the webservice, handle the errors or build the + * succeed message + */ + private function _parseResult($client, $result, $params, $id_mr_selected) + { + $errors = &$this->_resultList['error'][$params['NDossier']]; + $success = &$this->_resultList['success'][$params['NDossier']]; + $result = $result->WSI2_CreationEtiquetteResult; + if (($errorNumber = $result->STAT) != 0) + { + $errors[] = $this->_mondialrelay->l('There is an error number : ', $this->class_name).$errorNumber; + $errors[] = $this->_mondialrelay->l('Details : ', $this->class_name). + $this->_mondialrelay->getErrorCodeDetail($errorNumber); + } + else + { + $baseURL = MondialRelay::MR_URL; + $expedition = $result->ExpeditionNum; + $securityKey = Tools::strtoupper(md5('<'.$params['Enseigne'].$this->_markCode. + '>'.$expedition.'<'.$this->_webServiceKey.'>')); + $ticketURL = $baseURL.$result->URL_Etiquette; + $trackingURL = $baseURL. + 'public/permanent/tracking.aspx?ens='.$params['Enseigne'].$this->_markCode.'&exp='.$expedition.'&language='.Configuration::get('PS_LANG_DEFAULT').'&crc='.$securityKey; + + $success['displayExpedition'] = $this->_mondialrelay->l('Expedition Number : ', $this->class_name).$expedition; + $success['displayTicketURL'] = $this->_mondialrelay->l('Ticket URL : ', $this->class_name).$ticketURL; + $success['displayTrackingURL'] = $this->_mondialrelay->l('Tracking URL: ', $this->class_name).$trackingURL; + $success['expeditionNumber'] = $expedition; + + $this->_updateTable($params, $expedition, $ticketURL, $trackingURL, $id_mr_selected); + } + } + + /* + * Send one or multiple request to the webservice + */ + public function send() + { + if ($client = new SoapClient($this->_webserviceURL)) + { + $client->soap_defencoding = 'UTF-8'; + $client->decode_utf8 = false; + + foreach ($this->_fieldsList as $rootCase) + { + $params = $this->_getSimpleParamArray($rootCase['list']); + $result = $client->WSI2_CreationEtiquette($params); + $this->_parseResult($client, $result, $params, $rootCase['id_mr_selected']); + } + unset($client); + Configuration::updateValue('MONDIALRELAY_CONFIGURATION_OK', true); + } + else + throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice is not currently reliable', $this->class_name)); + } + + /* + ** Check if the shop parameter are currently well configured + */ + public function checkPreValidation() + { + $errorList = array('error' => array(), 'warn' => array()); + + $list = array( + 'Expe_Langage' => array( + 'value' => $this->_mondialrelay->account_shop['MR_LANGUAGE'], + 'error' => $this->_mondialrelay->l('Please check your language configuration', $this->class_name)), + 'Expe_Ad1' => array( + 'value' => Configuration::get('PS_SHOP_NAME'), + 'error' => $this->_mondialrelay->l('Please check your shop name configuration', $this->class_name)), + 'Expe_Ad3' => array( + 'value' => Configuration::get('PS_SHOP_ADDR1'), + 'error' => $this->_mondialrelay->l('Please check your address 1 configuration', $this->class_name)), + 'Expe_Ville' => array( + 'value' => Configuration::get('PS_SHOP_CITY'), + 'error' => $this->_mondialrelay->l('Please check your city configuration', $this->class_name)), + 'Expe_CP' => array( + 'value' => Configuration::get('PS_SHOP_CODE'), + 'error' => $this->_mondialrelay->l('Please check your zipcode configuration', $this->class_name)), + 'Expe_Pays' => array( + 'value' => ((version_compare(_PS_VERSION_, '1.4', '>=')) ? + Country::getIsoById(Configuration::get('PS_SHOP_COUNTRY_ID')) : + Tools::substr(Configuration::get('PS_SHOP_COUNTRY'), 0, 2)), + 'error' => $this->_mondialrelay->l('Please check your country configuration', $this->class_name)), + 'Expe_Tel1' => array( + 'value' => MRTools::getFormatedPhone(Configuration::get('PS_SHOP_PHONE')), + 'error' => $this->_mondialrelay->l('Please check your Phone configuration', $this->class_name)), + 'Expe_Mail' => array( + 'value' => Configuration::get('PS_SHOP_EMAIL'), + 'error' => $this->_mondialrelay->l('Please check your mail configuration', $this->class_name))); + + foreach ($list as $name => $tab) + { + // Mac server make an empty string instead of a cleaned string + // TODO : test on windows and linux server + $cleanedString = MRTools::removeAccents($tab['value']); + $tab['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($tab['value']); + + if ($name == 'Expe_CP') + { + if (version_compare(_PS_VERSION_, '1.4', '>=')) + { + if (!(MRTools::checkZipcodeByCountry($tab['value'], array( + 'id_country' => Configuration::get('PS_SHOP_COUNTRY_ID'))))) + $errorList['error'][$name] = $tab['error']; + } + else + $errorList['warn'][$name] = $this->_mondialrelay->l('Post code cannot be validated with PrestaShop versions older than 1.4', $this->class_name); + } + else if (isset($this->_fields['list'][$name]['regexValidation']) && + (!preg_match($this->_fields['list'][$name]['regexValidation'], $tab['value'], $matches))) + $errorList['error'][$name] = $tab['error']; + } + return $errorList; + } + + /* + * Get the values with associated fields name + * @fields : array containing multiple values information + */ + private function _getSimpleParamArray($fields) + { + $params = array(); + + foreach ($fields as $keyName => $valueDetailed) + $params[$keyName] = $valueDetailed['value']; + return $params; + } + + /* + * Return the fields list + */ + public function getFieldsList() + { + return $this->_fieldsList['list']; + } + + /* + * Return the result of one or multiple sent requests + */ + public function getResult() + { + return $this->_resultList; + } + + /* + * Return which number order of the list is currently managed + */ + public static function getCurrentRequestUnderTraitment() + { + // TODO: Build a SQL Query to know how many request have been executed + } } ?> diff --git a/classes/MRDownloadPDF.php b/classes/MRDownloadPDF.php index ebd3bd3..9e3a147 100644 --- a/classes/MRDownloadPDF.php +++ b/classes/MRDownloadPDF.php @@ -32,160 +32,160 @@ */ class MRDownloadPDF implements IMondialRelayWSMethod { - public $class_name = __CLASS__; + public $class_name = __CLASS__; - private $_fields = array( - 'list' => array( - 'Enseigne' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'Expeditions' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{8}(;[0-9]{8})*$#'), - 'Langue' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Security' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{32}$#'))); + private $_fields = array( + 'list' => array( + 'Enseigne' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'Expeditions' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{8}(;[0-9]{8})*$#'), + 'Langue' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Security' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{32}$#'))); - private $_mondialrelay = null; - private $_fieldsList = array(); - private $_webServiceKey = ''; - private $_markCode = ''; - private $Expeditions = ''; + private $_mondialrelay = null; + private $_fieldsList = array(); + private $_webServiceKey = ''; + private $_markCode = ''; + private $Expeditions = ''; - private $_resultList = array( - 'error' => array(), - 'success' => array()); + private $_resultList = array( + 'error' => array(), + 'success' => array()); - private $_webserviceURL = ''; + private $_webserviceURL = ''; - public function __construct($params, $object) - { - $this->Expeditions = $params['Expeditions']; - $this->_mondialrelay = $object; - $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; - $this->_markCode = $this->_mondialrelay->account_shop['MR_CODE_MARQUE']; - $this->class_name = Tools::strtolower($this->class_name); - $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - } + public function __construct($params, $object) + { + $this->Expeditions = $params['Expeditions']; + $this->_mondialrelay = $object; + $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; + $this->_markCode = $this->_mondialrelay->account_shop['MR_CODE_MARQUE']; + $this->class_name = Tools::strtolower($this->class_name); + $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + } - public function __destruct() - { - unset($this->_mondialrelay); - } + public function __destruct() + { + unset($this->_mondialrelay); + } - /* - * Initiate the data needed to be send properly - * Can manage a list of data for multiple request - */ - public function init() - { - $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; - $this->_fields['list']['Expeditions']['value'] = $this->Expeditions; - $this->_fields['list']['Langue']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; - $this->_fieldsList[] = $this->_fields; - $this->_generateMD5SecurityKey(); - } + /* + * Initiate the data needed to be send properly + * Can manage a list of data for multiple request + */ + public function init() + { + $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; + $this->_fields['list']['Expeditions']['value'] = $this->Expeditions; + $this->_fields['list']['Langue']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; + $this->_fieldsList[] = $this->_fields; + $this->_generateMD5SecurityKey(); + } - /* - * Generate the MD5 key for each param list - */ - private function _generateMD5SecurityKey() - { - foreach ($this->_fieldsList as &$rootCase) - { - $concatenationValue = ''; - foreach ($rootCase['list'] as $paramName => &$valueDetailed) - if ($paramName != 'Texte' && $paramName != 'Security') - { - $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); - if (preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) - $concatenationValue .= $valueDetailed['value']; - elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) - { - $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; - $this->_resultList['error'][] = $error; - } - } - $concatenationValue .= $this->_webServiceKey; - $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); - } - } + /* + * Generate the MD5 key for each param list + */ + private function _generateMD5SecurityKey() + { + foreach ($this->_fieldsList as &$rootCase) + { + $concatenationValue = ''; + foreach ($rootCase['list'] as $paramName => &$valueDetailed) + if ($paramName != 'Texte' && $paramName != 'Security') + { + $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); + if (preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) + $concatenationValue .= $valueDetailed['value']; + elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) + { + $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; + $this->_resultList['error'][] = $error; + } + } + $concatenationValue .= $this->_webServiceKey; + $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); + } + } - /* - * Manage the return value of the webservice, handle the errors or build the - * succeed message - */ - private function _parseResult($client, $result, $params) - { - $errors = &$this->_resultList['error'][]; - $success = &$this->_resultList['success'][]; - $result = $result->WSI2_GetEtiquettesResult; - if (($errorNumber = $result->STAT) != 0) - { - $errors[] = $this->_mondialrelay->l('There is an error number : ', $this->class_name).$errorNumber; - $errors[] = $this->_mondialrelay->l('Details : ', $this->class_name). - $this->_mondialrelay->getErrorCodeDetail($errorNumber); - } - else - { - $baseURL = 'http://www.mondialrelay.fr'; - $success['URL_PDF_A4'] = $baseURL.$result->URL_PDF_A4; - $success['URL_PDF_A5'] = $baseURL.$result->URL_PDF_A5; - $success['URL_PDF_10x15'] = $baseURL.str_replace('format=A4', 'format=10x15', $result->URL_PDF_A4); - } - } + /* + * Manage the return value of the webservice, handle the errors or build the + * succeed message + */ + private function _parseResult($client, $result, $params) + { + $errors = &$this->_resultList['error'][]; + $success = &$this->_resultList['success'][]; + $result = $result->WSI2_GetEtiquettesResult; + if (($errorNumber = $result->STAT) != 0) + { + $errors[] = $this->_mondialrelay->l('There is an error number : ', $this->class_name).$errorNumber; + $errors[] = $this->_mondialrelay->l('Details : ', $this->class_name). + $this->_mondialrelay->getErrorCodeDetail($errorNumber); + } + else + { + $baseURL = 'http://www.mondialrelay.fr'; + $success['URL_PDF_A4'] = $baseURL.$result->URL_PDF_A4; + $success['URL_PDF_A5'] = $baseURL.$result->URL_PDF_A5; + $success['URL_PDF_10x15'] = $baseURL.str_replace('format=A4', 'format=10x15', $result->URL_PDF_A4); + } + } - /* - * Send one or multiple request to the webservice - */ - public function send() - { - if ($client = new SoapClient($this->_webserviceURL)) - { - $client->soap_defencoding = 'UTF-8'; - $client->decode_utf8 = false; - foreach ($this->_fieldsList as $rootCase) - { - $params = $this->_getSimpleParamArray($rootCase['list']); - $result = $client->WSI2_GetEtiquettes($params); - $this->_parseResult($client, $result, $params); - } - unset($client); - } - else - throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); - } - - /* - * Return the fields list - */ - public function getFieldsList() - { - return $this->_fieldsList['list']; - } + /* + * Send one or multiple request to the webservice + */ + public function send() + { + if ($client = new SoapClient($this->_webserviceURL)) + { + $client->soap_defencoding = 'UTF-8'; + $client->decode_utf8 = false; + foreach ($this->_fieldsList as $rootCase) + { + $params = $this->_getSimpleParamArray($rootCase['list']); + $result = $client->WSI2_GetEtiquettes($params); + $this->_parseResult($client, $result, $params); + } + unset($client); + } + else + throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); + } + + /* + * Return the fields list + */ + public function getFieldsList() + { + return $this->_fieldsList['list']; + } - /* - * Return the result of one or multiple sent requests - */ - public function getResult() - { - return $this->_resultList; - } - - private function _getSimpleParamArray($fields) - { - $params = array(); - - foreach ($fields as $keyName => $valueDetailed) - $params[$keyName] = $valueDetailed['value']; - return $params; - } + /* + * Return the result of one or multiple sent requests + */ + public function getResult() + { + return $this->_resultList; + } + + private function _getSimpleParamArray($fields) + { + $params = array(); + + foreach ($fields as $keyName => $valueDetailed) + $params[$keyName] = $valueDetailed['value']; + return $params; + } } diff --git a/classes/MRGetRelayPoint.php b/classes/MRGetRelayPoint.php index f25a0af..7bbaf87 100644 --- a/classes/MRGetRelayPoint.php +++ b/classes/MRGetRelayPoint.php @@ -35,248 +35,312 @@ */ class MRGetRelayPoint implements IMondialRelayWSMethod { - /*Params is required if you use a pointer function*/ - private $_fields = array( - 'list' => array( - 'Enseigne' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'Pays' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Ville' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,25}$#'), - 'CP' => array( - 'required' => false, - 'value' => '', - 'params' => array(), - 'methodValidation' => 'checkZipcodeByCountry'), - 'Taille' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(XS|S|M|L|XL|XXL|3XL)$#'), - 'Poids' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^[0-9]{3,7}$#'), - 'Action' => array( - 'required' => false, - 'value' => '', - 'regexValidation' => '#^(REL|24R|ESP|DRI)$#'), - 'Security' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{32}$#'))); - - private $_id_address_delivery = 0; - private $_weight = 0; - private $_webServiceKey = ''; - private $_mondialRelay = null; - private $_id_carrier = 0; - private $_id_delivery_country = 0; - - private $_resultList = array( - 'error' => array(), - 'success' => array()); - - private $_webserviceURL; - - public function __construct($params, $object) - { - $this->_mondialRelay = $object; - $this->_id_address_delivery = (int)($params['id_address_delivery']); - $this->_id_carrier = (int)($params['id_carrier']); - $this->_weight = (float)($params['weight']); - $this->_webServiceKey = $this->_mondialRelay->account_shop['MR_KEY_WEBSERVICE']; - $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - } - - public function __destruct() - { - unset($this->_mondialRelay); - } - - public function init() - { - $address = new Address($this->_id_address_delivery); - $weight = $this->_mondialRelay->account_shop['MR_WEIGHT_COEFFICIENT'] * $this->_weight; - - if (!$address) - throw new Exception($this->_mondialrelay->l('Customer address can\'t be found')); - - $this->_fields['list']['Enseigne']['value'] = $this->_mondialRelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; - $this->_fields['list']['Poids']['value'] = ($weight < 100) ? 100 : $weight; - $this->_fields['list']['Pays']['value'] = trim(Country::getIsoById($address->id_country)); - $this->_fields['list']['Ville']['value'] = trim($address->city); - $this->_fields['list']['CP']['value'] = trim($address->postcode); - $this->_fields['list']['CP']['params']['id_country'] = $address->id_country; - - $this->_generateMD5SecurityKey(); - unset($address); - } - - /* - * Generate the MD5 key for each param list - */ - private function _generateMD5SecurityKey() - { - $concatenationValue = ''; - foreach ($this->_fields['list'] as $paramName => &$valueDetailed) - if ($paramName != 'Texte' && $paramName != 'Security') - { - // Mac server make an empty string instead of a cleaned string - // TODO : test on windows and linux server - $cleanedString = MRTools::removeAccents($valueDetailed['value']); - $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); + /*Params is required if you use a pointer function*/ + private $_fields = array( + 'list' => array( + 'Enseigne' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'Pays' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Ville' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[A-Z_\-\' 0-9]{2,25}$#'), + 'CP' => array( + 'required' => false, + 'value' => '', + 'params' => array(), + 'methodValidation' => 'checkZipcodeByCountry'), + 'Taille' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(XS|S|M|L|XL|XXL|3XL)$#'), + 'Poids' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9]{3,7}$#'), + 'Latitude' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^-?[0-9]{2}\.[0-9]{7}$#'), + 'Longitude' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^-?[0-9]{2}\.[0-9]{7}$#'), + 'Action' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^(REL|24R|ESP|DRI)$#'), + 'DelaiEnvoi' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9]{1,2}$#'), + 'RayonRecherche' => array( + 'required' => false, + 'value' => '', + 'regexValidation' => '#^[0-9]{1,4}$#'), + 'Security' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{32}$#'))); + + private $_id_address_delivery = 0; + private $_weight = 0; + private $_mode_liv = ''; + private $_webServiceKey = ''; + private $_mondialRelay = null; + private $_id_carrier = 0; + private $_id_delivery_country = 0; + + private $_resultList = array( + 'error' => array(), + 'success' => array()); + + private $_webserviceURL; + + public function __construct($params, $object) + { + $this->_mondialRelay = $object; + $this->_id_address_delivery = (int)($params['id_address_delivery']); + $this->_id_carrier = (int)($params['id_carrier']); + $this->_weight = (float)($params['weight']); + $this->_mode_liv = $params['mode_liv']; + $this->_webServiceKey = $this->_mondialRelay->account_shop['MR_KEY_WEBSERVICE']; + $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + } + + public function __destruct() + { + unset($this->_mondialRelay); + } + + public function init() + { + $address = new Address($this->_id_address_delivery); + $weight = $this->_mondialRelay->account_shop['MR_WEIGHT_COEFFICIENT'] * $this->_weight; + + if (!$address) + throw new Exception($this->_mondialrelay->l('Customer address can\'t be found')); + + $this->_fields['list']['Enseigne']['value'] = $this->_mondialRelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; + $this->_fields['list']['Poids']['value'] = ($weight < 100) ? 100 : $weight; + $this->_fields['list']['Pays']['value'] = trim(Country::getIsoById($address->id_country)); + $this->_fields['list']['Ville']['value'] = trim($address->city); + $this->_fields['list']['CP']['value'] = trim($address->postcode); + $this->_fields['list']['CP']['params']['id_country'] = $address->id_country; + $this->_fields['list']['Action']['value'] = $this->_mode_liv; - $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); - // Call a pointer function if exist to do different test - if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::$valueDetailed['methodValidation']($valueDetailed['value'], $valueDetailed['params'])) - $concatenationValue .= $valueDetailed['value']; - // Use simple Regex test given by MondialRelay - else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) - $concatenationValue .= $valueDetailed['value']; - // If the key is required, we set an error, else it's skipped - elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) - { - $error = $this->_mondialRelay->l('This key').' ['.$paramName.'] '. - $this->_mondialRelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; - $this->_resultList['error'][] = $error; - } - } - $concatenationValue .= $this->_webServiceKey; - $this->_fields['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); - } + if ($this->_mode_liv == "DRI" || $this->_mode_liv == "24R") { + $this->_fields['list']['Latitude']['value'] = null; + $this->_fields['list']['Longitude']['value'] = null; + $this->_fields['list']['DelaiEnvoi']['value'] = "0"; - /* - * Get the values with associated fields name - * @fields : array containing multiple values information - */ - private function _getSimpleParamArray($fields) - { - $params = array(); - - foreach ($fields as $keyName => $valueDetailed) - $params[$keyName] = $valueDetailed['value']; - return $params; - } - - /* - ** Get detail information for each relay - */ - private function _getRelayPointDetails($relayPointList) - { - $relayPointNumList = array(); - foreach ($relayPointList as $relayPoint) - $relayPointNumList[] = $relayPoint['Num']; - $MRRelayDetail = new MRRelayDetail(array('relayPointNumList' => $relayPointNumList, 'id_address_delivery' => $this->_id_address_delivery)); - $MRRelayDetail->init(); - $MRRelayDetail->send(); - return $MRRelayDetail->getResult(); - } + if ($this->_mode_liv == "DRI") + $this->_fields['list']['RayonRecherche']['value'] = "200"; + else + $this->_fields['list']['RayonRecherche']['value'] = null; + } - /* - ** Generate a perman link to view relay detail on their website - */ - private function _addLinkHoursDetail(&$relayPointList) - { - $relayPointNumList = array(); - foreach ($relayPointList as $relayPoint) - $relayPointNumList[] = $relayPoint->Num; - $permaList = MRRelayDetail::getPermaLink($relayPointNumList, $this->_id_address_delivery); - foreach ($relayPointList as &$relayPoint) - { - $relayPoint->permaLinkDetail = ''; - if (array_key_exists($relayPoint->Num, $permaList)) - $relayPoint->permaLinkDetail = $permaList[$relayPoint->Num]; - } - return $relayPointList; - } - - /* - * Manage the return value of the webservice, handle the errors or build the - * succeed message - */ - private function _parseResult($client, $result, $params) - { - $errors = array(); - $success = array(); - $result = $result->WSI2_RecherchePointRelaisResult; - if (($errorNumber = $result->STAT) != 0) - { - $errors[] = $this->_mondialRelay->l('There is an error number : ').$errorNumber; - $errors[] = $this->_mondialRelay->l('Details : '). - $this->_mondialRelay->getErrorCodeDetail($errorNumber); - } - else - { - unset($result->STAT); - - // Clean Content - foreach ($result as $num => $relayPoint) - { - $totalEmptyFields = 0; - foreach ($relayPoint as &$value) - { - $value = trim($value); - if (empty($value)) - ++$totalEmptyFields; - } - if ($totalEmptyFields == count($relayPoint)) - unset($result[$num]); - } - if (!count($result)) - $errors[] = $this->_mondialRelay->l('MondialRelay can\'t find any relay point near your address. Maybe your address isn\'t properly filled ?'); - else - { - $this->_addLinkHoursDetail($result); + $this->_generateMD5SecurityKey(); + unset($address); + } + + /* + * Generate the MD5 key for each param list + */ + private function _generateMD5SecurityKey() + { + $concatenationValue = ''; + foreach ($this->_fields['list'] as $paramName => &$valueDetailed) { + if ($paramName != 'Texte' && $paramName != 'Security') + { + // Mac server make an empty string instead of a cleaned string + // TODO : test on windows and linux server + $cleanedString = MRTools::removeAccents($valueDetailed['value']); + $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); - // Fetch detail info using webservice (not used anymore) - // $this->_generateLinkHoursDetail($result); - // $result = (count($relayDetail['success'])) ? $relayDetail['success'] : $result; - } - $success = $result; - } - $this->_resultList['error'] = $errors; - $this->_resultList['success'] = $success; - } - - /* - * Send one or multiple request to the webservice - */ - public function send() - { - if ($client = new SoapClient($this->_webserviceURL)) - { - $client->soap_defencoding = 'UTF-8'; - $client->decode_utf8 = false; - - $params = $this->_getSimpleParamArray($this->_fields['list']); - $result = $client->WSI2_RecherchePointRelais($params); - $this->_parseResult($client, $result, $params); - unset($client); - } - else - throw new Exception($this->_mondialRelay->l('The Mondial Relay webservice isn\'t currently reliable')); - } - - /* - * Get the values with associated fields name - */ - public function getFieldsList() - { - return $this->_fieldsList['list']; - } - - /* - * Get the result of one or multiple send request - */ - public function getResult() - { - return $this->_resultList; - } + $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); + // Call a pointer function if exist to do different test + if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::$valueDetailed['methodValidation']($valueDetailed['value'], $valueDetailed['params'])) { + $concatenationValue .= $valueDetailed['value']; + } + // Use simple Regex test given by MondialRelay + else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) { + $concatenationValue .= $valueDetailed['value']; + } + // If the key is required, we set an error, else it's skipped + elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) + { + $error = $this->_mondialRelay->l('This key').' ['.$paramName.'] '. + $this->_mondialRelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; + $this->_resultList['error'][] = $error; + } + } + } + $concatenationValue .= $this->_webServiceKey; + $this->_fields['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); + } + + /* + * Get the values with associated fields name + * @fields : array containing multiple values information + */ + private function _getSimpleParamArray($fields) + { + $params = array(); + + foreach ($fields as $keyName => $valueDetailed) + $params[$keyName] = $valueDetailed['value']; + return $params; + } + + /* + ** Get detail information for each relay + */ + private function _getRelayPointDetails($relayPointList) + { + $relayPointNumList = array(); + foreach ($relayPointList as $relayPoint) + $relayPointNumList[] = $relayPoint['Num']; + $MRRelayDetail = new MRRelayDetail(array('relayPointNumList' => $relayPointNumList, 'id_address_delivery' => $this->_id_address_delivery)); + $MRRelayDetail->init(); + $MRRelayDetail->send(); + return $MRRelayDetail->getResult(); + } + + /* + ** Generate a perman link to view relay detail on their website + */ + private function _addLinkHoursDetail(&$relayPointList) + { + $relayPointNumList = array(); + foreach ($relayPointList as $relayPoint) + $relayPointNumList[] = $relayPoint->Num; + $permaList = MRRelayDetail::getPermaLink($relayPointNumList, $this->_id_address_delivery); + foreach ($relayPointList as &$relayPoint) + { + $relayPoint->permaLinkDetail = ''; + if (array_key_exists($relayPoint->Num, $permaList)) + $relayPoint->permaLinkDetail = $permaList[$relayPoint->Num]; + } + return $relayPointList; + } + + /* + * Manage the return value of the webservice, handle the errors or build the + * succeed message + */ + private function _parseResult($client, $result, $params) + { + $errors = array(); + $success = array(); + + if ($this->_mode_liv == 'DRI' || $this->_mode_liv == "24R") + $result = $result->WSI3_PointRelais_RechercheResult; + else + $result = $result->WSI2_RecherchePointRelaisResult; + + if (($errorNumber = $result->STAT) != 0) + { + $errors[] = $this->_mondialRelay->l('There is an error number : ').$errorNumber; + $errors[] = $this->_mondialRelay->l('Details : '). + $this->_mondialRelay->getErrorCodeDetail($errorNumber); + } + else + { + unset($result->STAT); + + + if ($this->_mode_liv == 'DRI' || $this->_mode_liv == "24R") { + $list = $result->PointsRelais->PointRelais_Details; + if (!is_array($list)) + $list = array($list); + } + else + $list = $result; + + // Clean Content + foreach ($list as $num => &$relayPoint) + { + $totalEmptyFields = 0; + foreach ($relayPoint as &$value) + { + if (is_string($value)) + { + $value = trim($value); + if (empty($value)) + ++$totalEmptyFields; + } + else if (is_array($value) && count($value)) { + $value = array_map('trim', $value); + ++$totalEmptyFields; + } + } + + if ($totalEmptyFields == count($relayPoint)) + unset($list[$num]); + } + + if (!count($result)) + $errors[] = $this->_mondialRelay->l('MondialRelay can\'t find any relay point near your address. Maybe your address isn\'t properly filled ?'); + else + { + $this->_addLinkHoursDetail($list); + + // Fetch detail info using webservice (not used anymore) + // $this->_generateLinkHoursDetail($result); + // $result = (count($relayDetail['success'])) ? $relayDetail['success'] : $result; + } + $success = $list; + } + $this->_resultList['error'] = $errors; + $this->_resultList['success'] = $success; + } + + /* + * Send one or multiple request to the webservice + */ + public function send() + { + if ($client = new SoapClient($this->_webserviceURL, array('trace' => 1))) + { + $client->soap_defencoding = 'UTF-8'; + $client->decode_utf8 = false; + + $params = $this->_getSimpleParamArray($this->_fields['list']); + + if ($this->_mode_liv == 'DRI' || $this->_mode_liv == "24R") + { + $result = $client->WSI3_PointRelais_Recherche($params); + } + else { + $result = $client->WSI2_RecherchePointRelais($params); + } + + $this->_parseResult($client, $result, $params); + unset($client); + } + else + throw new Exception($this->_mondialRelay->l('The Mondial Relay webservice isn\'t currently reliable')); + } + + /* + * Get the values with associated fields name + */ + public function getFieldsList() + { + return $this->_fieldsList['list']; + } + + /* + * Get the result of one or multiple send request + */ + public function getResult() + { + return $this->_resultList; + } } diff --git a/classes/MRGetTickets.php b/classes/MRGetTickets.php index da0f638..5ff43e0 100644 --- a/classes/MRGetTickets.php +++ b/classes/MRGetTickets.php @@ -35,221 +35,221 @@ */ class MRGetTickets implements IMondialRelayWSMethod { - private $_fields = array( - 'list' => array( - 'Enseigne' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'Expeditions' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{8}(;[0-9]{8})*$#'), - 'Langue' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Security' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{32}$#'))); - - private $_detailedExpeditionList = array(); - private $_webServiceKey = ''; - private $_mondialrelay = null; - - private $_resultList = array( - 'error' => array(), - 'success' => array()); - - private $_webserviceURL; - - public function __construct($params, $object) - { - $this->_mondialrelay = $object; - $this->_detailedExpeditionList = $params['detailedExpeditionList']; - $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; - $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - } - - public function __destruct() - { - unset($this->_mondialrelay); - } - - public function init() - { - $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; - $this->_fields['list']['Langue']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; - - foreach ($this->_detailedExpeditionList as $detailedExpedition) - { - // Storage temporary - $base = $this->_fields; - $tmp = &$base['list']; - - $tmp['Expeditions']['value'] = $detailedExpedition['expeditionNumber']; - $this->_fieldsList[] = $base; - } - $this->_generateMD5SecurityKey(); - } - - /* - * Generate the MD5 key for each param list - */ - private function _generateMD5SecurityKey() - { - // RootCase is the array case where the main information are stored - // it's an array containing id_mr_selected and an array with the necessary fields - foreach ($this->_fieldsList as &$rootCase) - { - $concatenationValue = ''; - foreach ($rootCase['list'] as $paramName => &$valueDetailed) - if ($paramName != 'Texte' && $paramName != 'Security') - { - $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); - if (preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) - $concatenationValue .= $valueDetailed['value']; - elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) - { - $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; - $id_order = $this->_getOrderIdWithExpeditionNumber($rootCase['list']['Expeditions']['value']); - $this->_resultList['error'][$id_order][] = $error; - } - } - $concatenationValue .= $this->_webServiceKey; - $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); - } - } + private $_fields = array( + 'list' => array( + 'Enseigne' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'Expeditions' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{8}(;[0-9]{8})*$#'), + 'Langue' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Security' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{32}$#'))); + + private $_detailedExpeditionList = array(); + private $_webServiceKey = ''; + private $_mondialrelay = null; + + private $_resultList = array( + 'error' => array(), + 'success' => array()); + + private $_webserviceURL; + + public function __construct($params, $object) + { + $this->_mondialrelay = $object; + $this->_detailedExpeditionList = $params['detailedExpeditionList']; + $this->_webServiceKey = $this->_mondialrelay->account_shop['MR_KEY_WEBSERVICE']; + $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + } + + public function __destruct() + { + unset($this->_mondialrelay); + } + + public function init() + { + $this->_fields['list']['Enseigne']['value'] = $this->_mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; + $this->_fields['list']['Langue']['value'] = $this->_mondialrelay->account_shop['MR_LANGUAGE']; + + foreach ($this->_detailedExpeditionList as $detailedExpedition) + { + // Storage temporary + $base = $this->_fields; + $tmp = &$base['list']; + + $tmp['Expeditions']['value'] = $detailedExpedition['expeditionNumber']; + $this->_fieldsList[] = $base; + } + $this->_generateMD5SecurityKey(); + } + + /* + * Generate the MD5 key for each param list + */ + private function _generateMD5SecurityKey() + { + // RootCase is the array case where the main information are stored + // it's an array containing id_mr_selected and an array with the necessary fields + foreach ($this->_fieldsList as &$rootCase) + { + $concatenationValue = ''; + foreach ($rootCase['list'] as $paramName => &$valueDetailed) + if ($paramName != 'Texte' && $paramName != 'Security') + { + $valueDetailed['value'] = Tools::strtoupper($valueDetailed['value']); + if (preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) + $concatenationValue .= $valueDetailed['value']; + elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) + { + $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('hasn\'t a valide value format').' : '.$valueDetailed['value']; + $id_order = $this->_getOrderIdWithExpeditionNumber($rootCase['list']['Expeditions']['value']); + $this->_resultList['error'][$id_order][] = $error; + } + } + $concatenationValue .= $this->_webServiceKey; + $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); + } + } - /* - * Get the values with associated fields name - * @fields : array containing multiple values information - */ - private function _getSimpleParamArray($fields) - { - $params = array(); - - foreach ($fields as $keyName => $valueDetailed) - $params[$keyName] = $valueDetailed['value']; - return $params; - } - - /* - * Update the history tables - */ - private function _updateTable($id_order, $expeditionNumber, $URLA4, $URLA5, &$success) - { - $query = ' - SELECT id FROM `'._DB_PREFIX_.'mr_history` - WHERE `order`='.(int)$id_order; - - $row = Db::getInstance()->getRow($query); - if ($row) - { - $query = ' - UPDATE `'._DB_PREFIX_.'mr_history` - SET - `exp` = \''.(int)$expeditionNumber.'\', - `url_a4` = \''.pSQL((string)$URLA4).'\', - `url_a5` = \''.pSQL((string)$URLA5).'\' - WHERE `order` = '.(int)$id_order; - } - else - { - $query = ' - INSERT INTO '._DB_PREFIX_.'mr_history - (`order`, `exp`, `url_a4`, `url_a5`) - VALUES ( - '.(int)$id_order.', - '.(int)$expeditionNumber.', - \''.pSQL((string)$URLA4).'\', - \''.pSQL((string)$URLA5).'\')'; - } - Db::getInstance()->execute($query); - $success['id_mr_history'] = isset($row['id']) ? $row['id'] : Db::getInstance()->Insert_ID(); - } - - /* - * Manage the return value of the webservice, handle the errors or build the - * succeed message - */ - private function _parseResult($client, $result, $params) - { - $errors = array(); - $success = array(); - - $id_order = $this->_getOrderIdWithExpeditionNumber($params['Expeditions']); - $result = $result->WSI2_GetEtiquettesResult; - if (($errorNumber = $result->STAT) != 0) - { - $errors[] = $this->_mondialrelay->l('There is an error number : ').$errorNumber; - $errors[] = $this->_mondialrelay->l('Details : '). - $this->_mondialrelay->getErrorCodeDetail($errorNumber); - } - else - { - $baseURL = 'http://www.mondialrelay.fr'; - $URLPDF_A4 = $baseURL.$result->URL_PDF_A4; - $URLPDF_A5 = $baseURL.$result->URL_PDF_A5; + /* + * Get the values with associated fields name + * @fields : array containing multiple values information + */ + private function _getSimpleParamArray($fields) + { + $params = array(); + + foreach ($fields as $keyName => $valueDetailed) + $params[$keyName] = $valueDetailed['value']; + return $params; + } + + /* + * Update the history tables + */ + private function _updateTable($id_order, $expeditionNumber, $URLA4, $URLA5, &$success) + { + $query = ' + SELECT id FROM `'._DB_PREFIX_.'mr_history` + WHERE `order`='.(int)$id_order; + + $row = Db::getInstance()->getRow($query); + if ($row) + { + $query = ' + UPDATE `'._DB_PREFIX_.'mr_history` + SET + `exp` = \''.(int)$expeditionNumber.'\', + `url_a4` = \''.pSQL((string)$URLA4).'\', + `url_a5` = \''.pSQL((string)$URLA5).'\' + WHERE `order` = '.(int)$id_order; + } + else + { + $query = ' + INSERT INTO '._DB_PREFIX_.'mr_history + (`order`, `exp`, `url_a4`, `url_a5`) + VALUES ( + '.(int)$id_order.', + '.(int)$expeditionNumber.', + \''.pSQL((string)$URLA4).'\', + \''.pSQL((string)$URLA5).'\')'; + } + Db::getInstance()->execute($query); + $success['id_mr_history'] = isset($row['id']) ? $row['id'] : Db::getInstance()->Insert_ID(); + } + + /* + * Manage the return value of the webservice, handle the errors or build the + * succeed message + */ + private function _parseResult($client, $result, $params) + { + $errors = array(); + $success = array(); + + $id_order = $this->_getOrderIdWithExpeditionNumber($params['Expeditions']); + $result = $result->WSI2_GetEtiquettesResult; + if (($errorNumber = $result->STAT) != 0) + { + $errors[] = $this->_mondialrelay->l('There is an error number : ').$errorNumber; + $errors[] = $this->_mondialrelay->l('Details : '). + $this->_mondialrelay->getErrorCodeDetail($errorNumber); + } + else + { + $baseURL = 'http://www.mondialrelay.fr'; + $URLPDF_A4 = $baseURL.$result->URL_PDF_A4; + $URLPDF_A5 = $baseURL.$result->URL_PDF_A5; - $success['id_order'] = $id_order; - $success['expeditionNumber'] = $params['Expeditions']; - $success['URLPDF_A4'] = $URLPDF_A4; - $success['URLPDF_A5'] = $URLPDF_A5; - $success['URLPDF_10x15'] = $baseURL.str_replace('format=A4', 'format=10x15', $result->URL_PDF_A4); - $this->_updateTable($id_order, $params['Expeditions'], $URLPDF_A4, $URLPDF_A5, $success); - } - $this->_resultList['error'][$id_order] = $errors; - $this->_resultList['success'][$id_order] = $success; - } - - /* - * Get the order id using the expedition number - */ - private function _getOrderIdWithExpeditionNumber($expeditionNumber) - { - foreach ($this->_detailedExpeditionList as $detailedExpedition) - if ($detailedExpedition['expeditionNumber'] == $expeditionNumber) - return $detailedExpedition['id_order']; - return 0; - } - - /* - * Send one or multiple request to the webservice - */ - public function send() - { - if ($client = new SoapClient($this->_webserviceURL)) - { - $client->soap_defencoding = 'UTF-8'; - $client->decode_utf8 = false; - - foreach ($this->_fieldsList as $rootCase) - { - $params = $this->_getSimpleParamArray($rootCase['list']); - $result = $client->WSI2_GetEtiquettes($params); - $this->_parseResult($client, $result, $params); - } - unset($client); - } - else - throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); - } - - /* - * Get the values with associated fields name - */ - public function getFieldsList() - { - return $this->_fieldsList['list']; - } - - /* - * Get the result of one or multiple send request - */ - public function getResult() - { - return $this->_resultList; - } + $success['id_order'] = $id_order; + $success['expeditionNumber'] = $params['Expeditions']; + $success['URLPDF_A4'] = $URLPDF_A4; + $success['URLPDF_A5'] = $URLPDF_A5; + $success['URLPDF_10x15'] = $baseURL.str_replace('format=A4', 'format=10x15', $result->URL_PDF_A4); + $this->_updateTable($id_order, $params['Expeditions'], $URLPDF_A4, $URLPDF_A5, $success); + } + $this->_resultList['error'][$id_order] = $errors; + $this->_resultList['success'][$id_order] = $success; + } + + /* + * Get the order id using the expedition number + */ + private function _getOrderIdWithExpeditionNumber($expeditionNumber) + { + foreach ($this->_detailedExpeditionList as $detailedExpedition) + if ($detailedExpedition['expeditionNumber'] == $expeditionNumber) + return $detailedExpedition['id_order']; + return 0; + } + + /* + * Send one or multiple request to the webservice + */ + public function send() + { + if ($client = new SoapClient($this->_webserviceURL)) + { + $client->soap_defencoding = 'UTF-8'; + $client->decode_utf8 = false; + + foreach ($this->_fieldsList as $rootCase) + { + $params = $this->_getSimpleParamArray($rootCase['list']); + $result = $client->WSI2_GetEtiquettes($params); + $this->_parseResult($client, $result, $params); + } + unset($client); + } + else + throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); + } + + /* + * Get the values with associated fields name + */ + public function getFieldsList() + { + return $this->_fieldsList['list']; + } + + /* + * Get the result of one or multiple send request + */ + public function getResult() + { + return $this->_resultList; + } } \ No newline at end of file diff --git a/classes/MRManagement.php b/classes/MRManagement.php index f7e28db..931c098 100644 --- a/classes/MRManagement.php +++ b/classes/MRManagement.php @@ -29,142 +29,172 @@ class MRManagement extends MondialRelay { - private $_params = array(); - - private $_resultList = array( - 'error' => array(), - 'success' => array()); - - public function __construct($params) - { - $this->_params = $params; - - parent::__construct(); - } - - /* - ** This method fill the database with the selected carrier - */ - public function addSelectedCarrierToDB() - { - $db = Db::getInstance(); - // insutance - $sql = 'SELECT insurance FROM '._DB_PREFIX_.'mr_method WHERE id_mr_method = '.(int)$this->_params['id_mr_method']; - $insurance = $db->getValue($sql); - - $query = 'SELECT `id_mr_selected` FROM `'._DB_PREFIX_.'mr_selected` WHERE `id_cart` = '.(int)$this->_params['id_cart'].' '; + private $_params = array(); + + private $_resultList = array( + 'error' => array(), + 'success' => array()); + + public function __construct($params) + { + $this->_params = $params; + + parent::__construct(); + } + + /* + ** This method fill the database with the selected carrier + */ + public function addSelectedCarrierToDB() + { + $db = Db::getInstance(); + // insutance + $sql = 'SELECT insurance FROM '._DB_PREFIX_.'mr_method WHERE id_mr_method = '.(int)$this->_params['id_mr_method']; + $insurance = $db->getValue($sql); + + $query = 'SELECT `id_mr_selected` FROM `'._DB_PREFIX_.'mr_selected` WHERE `id_cart` = '.(int)$this->_params['id_cart'].' '; - // Not exist and needed for database - unset($this->_params['relayPointInfo']['permaLinkDetail']); - - // Update if Exist else add a new entry - if ($db->getRow($query)) - { - $query = 'UPDATE `'._DB_PREFIX_.'mr_selected` - SET `id_method` = '.(int)$this->_params['id_mr_method'].', - `MR_insurance` = '.(int)$insurance.','; - if (is_array($this->_params['relayPointInfo'])) - foreach ($this->_params['relayPointInfo'] as $nameKey => $value) - $query .= '`MR_Selected_'.MRTools::bqSQL($nameKey).'` = "'.pSQL($value).'", '; - else // Clean the existing relay point data - $query .= ' - MR_Selected_Num = NULL, - MR_Selected_LgAdr1 = NULL, - MR_Selected_LgAdr2 = NULL, - MR_Selected_LgAdr3 = NULL, - MR_Selected_LgAdr4 = NULL, - MR_Selected_CP = NULL, - MR_Selected_Pays = NULL, - MR_Selected_Ville = NULL, '; - $query = rtrim($query, ', ').' WHERE `id_cart` = '.(int)$this->_params['id_cart']; - } - else - { - $query = 'INSERT INTO `'._DB_PREFIX_.'mr_selected` - (`id_customer`, `id_method`, `id_cart`, MR_insurance, '; - if (is_array($this->_params['relayPointInfo'])) - foreach ($this->_params['relayPointInfo'] as $nameKey => $value) - $query .= '`MR_Selected_'.MRTools::bqSQL($nameKey).'`, '; - $query = rtrim($query, ', ').') VALUES ( - '.(int)$this->_params['id_customer'].', - '.(int)$this->_params['id_mr_method'].', - '.(int)$this->_params['id_cart'].', - '.(int)$insurance.', '; - if (is_array($this->_params['relayPointInfo'])) - foreach ($this->_params['relayPointInfo'] as $nameKey => $value) - $query .= '"'.pSQL($value).'", '; - $query = rtrim($query, ', ').')'; - } - $db->execute($query); - } + $columns = 'select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME="'._DB_PREFIX_.'mr_selected"'; + $columns_mr = array(); + $column_exist = $db->executeS($columns); + foreach ($column_exist as $key => $value) { + $columns_mr2[] .= $value['COLUMN_NAME']; + } + - public function uninstallDetail() - { - $html = ''; - - switch ($this->_params['action']) - { - case 'showFancy': - $html .= ' -
-

'.$this->l('Uninstalling Mondial Relay').'

-
- '.$this->l('You\'re about to uninstall the module, do you want to remove the database').' ? -

- -
- -
- -
-

-
-
- '; - $this->_resultList['html'] = $html; - break; - case 'backupAndUninstall': - - break; - default: - } - return $this->_resultList; - } + // Not exist and needed for database + unset($this->_params['relayPointInfo']['permaLinkDetail']); + + // Update if Exist else add a new entry + if ($db->getRow($query)) + { + - public function DeleteHistory() - { - $success = array(); - $error = array(); - - if (is_array($this->_params['historyIdList']) && count($this->_params['historyIdList'])) - { - $query = ' - DELETE FROM `'._DB_PREFIX_.'mr_history` - WHERE id IN('; - foreach ($this->_params['historyIdList'] as $id) - $query .= (int)$id.', '; - $query = trim($query, ', ').')'; - - $success['deletedListId'] = $this->_params['historyIdList']; - $totalDeleted = Db::getInstance()->execute($query); - if (count($success['deletedListId']) != $totalDeleted) - { - $error[] = $this->l('Some items can\'t be removed, please try to remove it again'); - foreach ($success['deletedListId'] as $id) - { - $query = ' - SELECT id FROM `'._DB_PREFIX_.'mr_history` - WHERE id='.(int)$id; - if (Db::getInstance()->getRow($query) && ($key = array_search($id, $success['deletedListId'])) !== false) - unset($success['deletedListId'][$key]); - } - } - $this->_resultList['success'] = $success; - $this->_resultList['other']['error'] = $error; - } - else - throw new Exception($this->l('Please select at least one history element')); - return $this->_resultList; - } + $query = 'UPDATE `'._DB_PREFIX_.'mr_selected` + SET `id_method` = '.(int)$this->_params['id_mr_method'].', + `MR_insurance` = '.(int)$insurance.','; + if (is_array($this->_params['relayPointInfo'])) { + foreach ($this->_params['relayPointInfo'] as $nameKey => $value) { + if (in_array('MR_Selected_'.MRTools::bqSQL($nameKey).'', $columns_mr2)) { + $query .= '`MR_Selected_'.MRTools::bqSQL($nameKey).'` = "'.pSQL($value).'", '; + } + } + + + } else { // Clean the existing relay point data + $query .= ' + MR_Selected_Num = NULL, + MR_Selected_LgAdr1 = NULL, + MR_Selected_LgAdr2 = NULL, + MR_Selected_LgAdr3 = NULL, + MR_Selected_LgAdr4 = NULL, + MR_Selected_CP = NULL, + MR_Selected_Pays = NULL, + MR_Selected_Ville = NULL, '; + } + $query = rtrim($query, ', ').' WHERE `id_cart` = '.(int)$this->_params['id_cart']; + } + else + { + + $query = 'INSERT INTO `'._DB_PREFIX_.'mr_selected` + (`id_customer`, `id_method`, `id_cart`, MR_insurance, '; + if (is_array($this->_params['relayPointInfo'])) { + foreach ($this->_params['relayPointInfo'] as $nameKey => $value) { + if (in_array('MR_Selected_'.MRTools::bqSQL($nameKey).'', $columns_mr2)) { + $query .= '`MR_Selected_'.MRTools::bqSQL($nameKey).'`, '; + } + + } + } + $query = rtrim($query, ', ').') VALUES ( + '.(int)$this->_params['id_customer'].', + '.(int)$this->_params['id_mr_method'].', + '.(int)$this->_params['id_cart'].', + '.(int)$insurance.', '; + + if (is_array($this->_params['relayPointInfo'])) { + foreach ($this->_params['relayPointInfo'] as $nameKey => $value) { + + if (in_array('MR_Selected_'.MRTools::bqSQL($nameKey).'', $columns_mr2)) { + $query .= '"'.pSQL($value).'", '; + } + + } + } + $query = rtrim($query, ', ').')'; + } + $db->execute($query); + + } + + public function uninstallDetail() + { + $html = ''; + + switch ($this->_params['action']) + { + case 'showFancy': + $html .= ' +
+

'.$this->l('Uninstalling Mondial Relay').'

+
+ '.$this->l('You\'re about to uninstall the module, do you want to remove the database').' ? +

+ +
+ +
+ +
+

+
+
+ '; + $this->_resultList['html'] = $html; + break; + case 'backupAndUninstall': + + break; + default: + } + return $this->_resultList; + } + + public function DeleteHistory() + { + $success = array(); + $error = array(); + + if (is_array($this->_params['historyIdList']) && count($this->_params['historyIdList'])) + { + $query = ' + DELETE FROM `'._DB_PREFIX_.'mr_history` + WHERE id IN('; + foreach ($this->_params['historyIdList'] as $id) + $query .= (int)$id.', '; + $query = trim($query, ', ').')'; + + $success['deletedListId'] = $this->_params['historyIdList']; + $totalDeleted = Db::getInstance()->execute($query); + if (count($success['deletedListId']) != $totalDeleted) + { + $error[] = $this->l('Some items can\'t be removed, please try to remove it again'); + foreach ($success['deletedListId'] as $id) + { + $query = ' + SELECT id FROM `'._DB_PREFIX_.'mr_history` + WHERE id='.(int)$id; + if (Db::getInstance()->getRow($query) && ($key = array_search($id, $success['deletedListId'])) !== false) + unset($success['deletedListId'][$key]); + } + } + $this->_resultList['success'] = $success; + $this->_resultList['other']['error'] = $error; + } + else + throw new Exception($this->l('Please select at least one history element')); + return $this->_resultList; + } } \ No newline at end of file diff --git a/classes/MRRelayDetail.php b/classes/MRRelayDetail.php index 1d2ab9b..412ce84 100644 --- a/classes/MRRelayDetail.php +++ b/classes/MRRelayDetail.php @@ -35,231 +35,231 @@ */ class MRRelayDetail implements IMondialRelayWSMethod { - private $_fields = array( - 'list' => array( - 'Enseigne' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), - 'Num' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9]{6}$#'), - 'Pays' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[A-Z]{2}$#'), - 'Security' => array( - 'required' => true, - 'value' => '', - 'regexValidation' => '#^[0-9A-Z]{32}$#'))); - - private $_relayPointNumList = array(); - private $_id_address_delivery = 0; - private $_webServiceKey = ''; - private $_mondialrelay = null; - private $_markCode = ''; - - private $_resultList = array( - 'error' => array(), - 'success' => array()); - - private $_webserviceURL = ''; - - public function __construct($params, $object) - { - $this->_mondialrelay = $object; + private $_fields = array( + 'list' => array( + 'Enseigne' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{2}[0-9A-Z ]{6}$#'), + 'Num' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9]{6}$#'), + 'Pays' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[A-Z]{2}$#'), + 'Security' => array( + 'required' => true, + 'value' => '', + 'regexValidation' => '#^[0-9A-Z]{32}$#'))); + + private $_relayPointNumList = array(); + private $_id_address_delivery = 0; + private $_webServiceKey = ''; + private $_mondialrelay = null; + private $_markCode = ''; + + private $_resultList = array( + 'error' => array(), + 'success' => array()); + + private $_webserviceURL = ''; + + public function __construct($params, $object) + { + $this->_mondialrelay = $object; - $this->_relayPointNumList = $params['relayPointNumList']; - $this->_id_address_delivery = (int)($params['id_address_delivery']); - $this->_webServiceKey = $this->_mondialRelay->account_shop['MR_KEY_WEBSERVICE']; - $this->_markCode = $this->_mondialRelay->account_shop['MR_CODE_MARQUE']; - $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - } - - public function __destruct() - { - unset($this->_mondialrelay); - } - - public function init() - { - $address = new Address($this->_id_address_delivery); - - if (!$address) - throw new Exception($this->_mondialrelay->l('Customer address can\'t be found')); - - $this->_fields['list']['Enseigne']['value'] = $this->_mondialRelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; - $this->_fields['list']['Pays']['value'] = Country::getIsoById($address->id_country); - - foreach ($this->_relayPointNumList as $num) - { - // Storage temporary - $base = $this->_fields; - $tmp = &$base['list']; - - $tmp['Num']['value'] = $num; - $this->_fieldsList[] = $base; - } - $this->_generateMD5SecurityKey(); - unset($address); - } - - /* - * Generate the MD5 key for each param list - */ - private function _generateMD5SecurityKey() - { - // RootCase is the array case where the main information are stored - // it's an array containing id_mr_selected and an array with the necessary fields - foreach ($this->_fieldsList as &$rootCase) - { - $concatenationValue = ''; - foreach ($rootCase['list'] as $paramName => &$valueDetailed) - if ($paramName != 'Texte' && $paramName != 'Security') - { - // Mac server make an empty string instead of a cleaned string - // TODO : test on windows and linux server - $cleanedString = MRTools::removeAccents($valueDetailed['value']); - $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); - - // Call a pointer function if exist to do different test - if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::$valueDetailed['methodValidation']($valueDetailed['value'], $valueDetailed['params'])) - $concatenationValue .= $valueDetailed['value']; - // Use simple Regex test given by MondialRelay - else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) - $concatenationValue .= $valueDetailed['value']; - // If the key is required, we set an error, else it's skipped - elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) - { - if (empty($valueDetailed['value'])) - $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('is empty and need to be filled'); - else - $error = 'This key ['.$paramName.'] hasn\'t a valid value format : '.$valueDetailed['value']; - $this->_resultList['error'][$rootCase['list']['Num']['value']] = $error; - } - } - $concatenationValue .= $this->_webServiceKey; - $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); - } - } + $this->_relayPointNumList = $params['relayPointNumList']; + $this->_id_address_delivery = (int)($params['id_address_delivery']); + $this->_webServiceKey = $this->_mondialRelay->account_shop['MR_KEY_WEBSERVICE']; + $this->_markCode = $this->_mondialRelay->account_shop['MR_CODE_MARQUE']; + $this->_webserviceURL = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + } + + public function __destruct() + { + unset($this->_mondialrelay); + } + + public function init() + { + $address = new Address($this->_id_address_delivery); + + if (!$address) + throw new Exception($this->_mondialrelay->l('Customer address can\'t be found')); + + $this->_fields['list']['Enseigne']['value'] = $this->_mondialRelay->account_shop['MR_ENSEIGNE_WEBSERVICE']; + $this->_fields['list']['Pays']['value'] = Country::getIsoById($address->id_country); + + foreach ($this->_relayPointNumList as $num) + { + // Storage temporary + $base = $this->_fields; + $tmp = &$base['list']; + + $tmp['Num']['value'] = $num; + $this->_fieldsList[] = $base; + } + $this->_generateMD5SecurityKey(); + unset($address); + } + + /* + * Generate the MD5 key for each param list + */ + private function _generateMD5SecurityKey() + { + // RootCase is the array case where the main information are stored + // it's an array containing id_mr_selected and an array with the necessary fields + foreach ($this->_fieldsList as &$rootCase) + { + $concatenationValue = ''; + foreach ($rootCase['list'] as $paramName => &$valueDetailed) + if ($paramName != 'Texte' && $paramName != 'Security') + { + // Mac server make an empty string instead of a cleaned string + // TODO : test on windows and linux server + $cleanedString = MRTools::removeAccents($valueDetailed['value']); + $valueDetailed['value'] = !empty($cleanedString) ? Tools::strtoupper($cleanedString) : Tools::strtoupper($valueDetailed['value']); + + // Call a pointer function if exist to do different test + if (isset($valueDetailed['methodValidation']) && method_exists('MRTools', $valueDetailed['methodValidation']) && isset($valueDetailed['params']) && MRTools::$valueDetailed['methodValidation']($valueDetailed['value'], $valueDetailed['params'])) + $concatenationValue .= $valueDetailed['value']; + // Use simple Regex test given by MondialRelay + else if (isset($valueDetailed['regexValidation']) && preg_match($valueDetailed['regexValidation'], $valueDetailed['value'], $matches)) + $concatenationValue .= $valueDetailed['value']; + // If the key is required, we set an error, else it's skipped + elseif ((!Tools::strlen($valueDetailed['value']) && $valueDetailed['required']) || Tools::strlen($valueDetailed['value'])) + { + if (empty($valueDetailed['value'])) + $error = $this->_mondialrelay->l('This key').' ['.$paramName.'] '.$this->_mondialrelay->l('is empty and need to be filled'); + else + $error = 'This key ['.$paramName.'] hasn\'t a valid value format : '.$valueDetailed['value']; + $this->_resultList['error'][$rootCase['list']['Num']['value']] = $error; + } + } + $concatenationValue .= $this->_webServiceKey; + $rootCase['list']['Security']['value'] = Tools::strtoupper(md5($concatenationValue)); + } + } - /* - * Get the values with associated fields name - * @fields : array containing multiple values information - */ - private function _getSimpleParamArray($fields) - { - $params = array(); - - foreach ($fields as $keyName => $valueDetailed) - $params[$keyName] = $valueDetailed['value']; - return $params; - } - - /* - * Manage the return value of the webservice, handle the errors or build the - * succeed message - */ - private function _parseResult($client, $result, $params) - { - $errors = array(); - $result = $result->WSI2_DetailPointRelaisResult; - if (($errorNumber = $result->STAT) != 0) - { - $errors[] = $this->_mondialrelay->l('There is an error number : ').$errorNumber; - $errors[] = $this->_mondialrelay->l('Details : '). - $this->_mondialrelay->getErrorCodeDetail($errorNumber); - } - else - { - $HDayList = array( - 'Horaires_Lundi' => $this->_mondialrelay->l('Monday'), - 'Horaires_Mardi' => $this->_mondialrelay->l('Tuesday'), - 'Horaires_Mercredi' => $this->_mondialrelay->l('Wednesday'), - 'Horaires_Jeudi' => $this->_mondialrelay->l('Thursday'), - 'Horaires_Vendredi' => $this->_mondialrelay->l('Friday'), - 'Horaires_Samedi' => $this->_mondialrelay->l('Saturday'), - 'Horaires_Dimanche' => $this->_mondialrelay->l('Sunday')); - - $orderedDate = array(); - // Format hour properly - $priority = 0; - foreach ($HDayList as $day => $tradDayName) - { - $mr_day = $result->{$day}; - foreach ($mr_day['string'] as &$value) - if ($value == '0000') - $value = ''; - else - $value = implode('h', str_split($value, 2)); - $orderedDate[$priority++] = array('name' => $tradDayName, 'list' => $mr_day); - unset($result[$day]); - } - $result->orderedDate = $orderedDate; - $this->_resultList['success'][$result->Num] = $result; - } - $this->_resultList['error'][] = $errors; - } - - /* - * Send one or multiple request to the webservice - */ - public function send() - { - if ($client = new SoapClient($this->_webserviceURL)) - { - $client->soap_defencoding = 'UTF-8'; - $client->decode_utf8 = false; - - foreach ($this->_fieldsList as $rootCase) - { - $params = $this->_getSimpleParamArray($rootCase['list']); - $result = $client->WSI2_DetailPointRelais($params); - $this->_parseResult($client, $result, $params); - } - unset($client); - } - else - throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); - } - - /* - ** Generate a list of perma link - */ - public static function getPermaLink($relayList, $id_address_delivery) - { - if (!($address = new Address($id_address_delivery))) - return array(); + /* + * Get the values with associated fields name + * @fields : array containing multiple values information + */ + private function _getSimpleParamArray($fields) + { + $params = array(); + + foreach ($fields as $keyName => $valueDetailed) + $params[$keyName] = $valueDetailed['value']; + return $params; + } + + /* + * Manage the return value of the webservice, handle the errors or build the + * succeed message + */ + private function _parseResult($client, $result, $params) + { + $errors = array(); + $result = $result->WSI2_DetailPointRelaisResult; + if (($errorNumber = $result->STAT) != 0) + { + $errors[] = $this->_mondialrelay->l('There is an error number : ').$errorNumber; + $errors[] = $this->_mondialrelay->l('Details : '). + $this->_mondialrelay->getErrorCodeDetail($errorNumber); + } + else + { + $HDayList = array( + 'Horaires_Lundi' => $this->_mondialrelay->l('Monday'), + 'Horaires_Mardi' => $this->_mondialrelay->l('Tuesday'), + 'Horaires_Mercredi' => $this->_mondialrelay->l('Wednesday'), + 'Horaires_Jeudi' => $this->_mondialrelay->l('Thursday'), + 'Horaires_Vendredi' => $this->_mondialrelay->l('Friday'), + 'Horaires_Samedi' => $this->_mondialrelay->l('Saturday'), + 'Horaires_Dimanche' => $this->_mondialrelay->l('Sunday')); + + $orderedDate = array(); + // Format hour properly + $priority = 0; + foreach ($HDayList as $day => $tradDayName) + { + $mr_day = $result->{$day}; + foreach ($mr_day['string'] as &$value) + if ($value == '0000') + $value = ''; + else + $value = implode('h', str_split($value, 2)); + $orderedDate[$priority++] = array('name' => $tradDayName, 'list' => $mr_day); + unset($result[$day]); + } + $result->orderedDate = $orderedDate; + $this->_resultList['success'][$result->Num] = $result; + } + $this->_resultList['error'][] = $errors; + } + + /* + * Send one or multiple request to the webservice + */ + public function send() + { + if ($client = new SoapClient($this->_webserviceURL)) + { + $client->soap_defencoding = 'UTF-8'; + $client->decode_utf8 = false; + + foreach ($this->_fieldsList as $rootCase) + { + $params = $this->_getSimpleParamArray($rootCase['list']); + $result = $client->WSI2_DetailPointRelais($params); + $this->_parseResult($client, $result, $params); + } + unset($client); + } + else + throw new Exception($this->_mondialrelay->l('The Mondial Relay webservice isn\'t currently reliable')); + } + + /* + ** Generate a list of perma link + */ + public static function getPermaLink($relayList, $id_address_delivery) + { + if (!($address = new Address($id_address_delivery))) + return array(); - $mondialrelay = new MondialRelay(); - - $list = array(); - $iso = Tools::strtoupper(Country::getIsoById($address->id_country)); - $ens = $mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE'].$mondialrelay->account_shop['MR_CODE_MARQUE']; - $url = 'http://www.mondialrelay.com/public/permanent/details_relais.aspx?ens='.$ens; - foreach ($relayList as $relayNum) - { - $crc = Tools::strtoupper(md5('<'.Tools::strtoupper($ens).'>'.$relayNum.$iso.'<'.$mondialrelay->account_shop['MR_KEY_WEBSERVICE'].'>')); - $list[$relayNum] = $url.'&num='.$relayNum.'&pays='.$iso.'&crc='.$crc; - } - unset($address, $mondialrelay); - return $list; - } - - /* - * Get the values with associated fields name - */ - public function getFieldsList() - { - return $this->_fieldsList['list']; - } - - /* - * Get the result of one or multiple send request - */ - public function getResult() - { - return $this->_resultList; - } + $mondialrelay = new MondialRelay(); + + $list = array(); + $iso = Tools::strtoupper(Country::getIsoById($address->id_country)); + $ens = $mondialrelay->account_shop['MR_ENSEIGNE_WEBSERVICE'].$mondialrelay->account_shop['MR_CODE_MARQUE']; + $url = 'http://www.mondialrelay.com/public/permanent/details_relais.aspx?ens='.$ens; + foreach ($relayList as $relayNum) + { + $crc = Tools::strtoupper(md5('<'.Tools::strtoupper($ens).'>'.$relayNum.$iso.'<'.$mondialrelay->account_shop['MR_KEY_WEBSERVICE'].'>')); + $list[$relayNum] = $url.'&num='.$relayNum.'&pays='.$iso.'&crc='.$crc; + } + unset($address, $mondialrelay); + return $list; + } + + /* + * Get the values with associated fields name + */ + public function getFieldsList() + { + return $this->_fieldsList['list']; + } + + /* + * Get the result of one or multiple send request + */ + public function getResult() + { + return $this->_resultList; + } } \ No newline at end of file diff --git a/classes/MRTools.php b/classes/MRTools.php index 0747560..1ff2999 100644 --- a/classes/MRTools.php +++ b/classes/MRTools.php @@ -30,241 +30,241 @@ */ class MRTools { - const REGEX_CLEAN_ADDR = '/[^a-zA-Z0-9-\s\'\!\,\|\(\)\.\*\&\#\/\:]/'; + const REGEX_CLEAN_ADDR = '/[^a-zA-Z0-9-\s\'\!\,\|\(\)\.\*\&\#\/\:]/'; - const REGEX_CLEAN_PHONE = '/[^0-9+\(\)]*/'; + const REGEX_CLEAN_PHONE = '/[^0-9+\(\)]*/'; - /** - * Check if a string could be UTF8 one - * - * @static - * @param $str - * @return bool - */ - public static function seemsUTF8($str) - { - $length = strlen($str); + /** + * Check if a string could be UTF8 one + * + * @static + * @param $str + * @return bool + */ + public static function seemsUTF8($str) + { + $length = strlen($str); - for ($i = 0; $i < $length; $i++) - { - $c = ord($str[$i]); - if ($c < 0x80) - $n = 0; # 0bbbbbbb - else if (($c & 0xE0) == 0xC0) - $n = 1; # 110bbbbb - else if (($c & 0xF0) == 0xE0) - $n = 2; # 1110bbbb - else if (($c & 0xF8) == 0xF0) - $n = 3; # 11110bbb - else if (($c & 0xFC) == 0xF8) - $n = 4; # 111110bb - else if (($c & 0xFE) == 0xFC) - $n = 5; # 1111110b - else - return false; # Does not match any model - for ($j = 0; $j < $n; $j++) - { - # n bytes matching 10bbbbbb follow ? - if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) - return false; - } - } - return true; - } + for ($i = 0; $i < $length; $i++) + { + $c = ord($str[$i]); + if ($c < 0x80) + $n = 0; # 0bbbbbbb + else if (($c & 0xE0) == 0xC0) + $n = 1; # 110bbbbb + else if (($c & 0xF0) == 0xE0) + $n = 2; # 1110bbbb + else if (($c & 0xF8) == 0xF0) + $n = 3; # 11110bbb + else if (($c & 0xFC) == 0xF8) + $n = 4; # 111110bb + else if (($c & 0xFE) == 0xFC) + $n = 5; # 1111110b + else + return false; # Does not match any model + for ($j = 0; $j < $n; $j++) + { + # n bytes matching 10bbbbbb follow ? + if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80)) + return false; + } + } + return true; + } - /** - * Converts all accent characters to ASCII characters. - * - * If there are no accent characters, then the string given is just returned. - * - * @param string $string Text that might have accent characters - * @return string Filtered string with replaced "nice" characters. - */ - public static function removeAccents($string) - { - if (!preg_match('/[\x80-\xff]/', $string)) - return $string; + /** + * Converts all accent characters to ASCII characters. + * + * If there are no accent characters, then the string given is just returned. + * + * @param string $string Text that might have accent characters + * @return string Filtered string with replaced "nice" characters. + */ + public static function removeAccents($string) + { + if (!preg_match('/[\x80-\xff]/', $string)) + return $string; - if (MRTools::seemsUTF8($string)) - { - $chars = array( - // Decompositions for Latin-1 Supplement - chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', - chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', - chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', - chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', - chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', - chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', - chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', - chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', - chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', - chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', - chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', - chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', - chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', - chr(195).chr(159) => 's', chr(195).chr(160) => 'a', - chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', - chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', - chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', - chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', - chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', - chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', - chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', - chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', - chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', - chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', - chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', - chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', - chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', - chr(195).chr(191) => 'y', - // Decompositions for Latin Extended-A - chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', - chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', - chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', - chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', - chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', - chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', - chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', - chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', - chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', - chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', - chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', - chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', - chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', - chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', - chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', - chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', - chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', - chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', - chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', - chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', - chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', - chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', - chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', - chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', - chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', - chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', - chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', - chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', - chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', - chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', - chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', - chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', - chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', - chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', - chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', - chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', - chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', - chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', - chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', - chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', - chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', - chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', - chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', - chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', - chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', - chr(197).chr(154) => 'S',chr(197).chr(155) => 's', - chr(197).chr(156) => 'S',chr(197).chr(157) => 's', - chr(197).chr(158) => 'S',chr(197).chr(159) => 's', - chr(197).chr(160) => 'S', chr(197).chr(161) => 's', - chr(197).chr(162) => 'T', chr(197).chr(163) => 't', - chr(197).chr(164) => 'T', chr(197).chr(165) => 't', - chr(197).chr(166) => 'T', chr(197).chr(167) => 't', - chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', - chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', - chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', - chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', - chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', - chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', - chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', - chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', - chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', - chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', - chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', - chr(197).chr(190) => 'z', chr(197).chr(191) => 's', - // Euro Sign - chr(226).chr(130).chr(172) => 'E', - // GBP (Pound) Sign - chr(194).chr(163) => ''); + if (MRTools::seemsUTF8($string)) + { + $chars = array( + // Decompositions for Latin-1 Supplement + chr(195).chr(128) => 'A', chr(195).chr(129) => 'A', + chr(195).chr(130) => 'A', chr(195).chr(131) => 'A', + chr(195).chr(132) => 'A', chr(195).chr(133) => 'A', + chr(195).chr(135) => 'C', chr(195).chr(136) => 'E', + chr(195).chr(137) => 'E', chr(195).chr(138) => 'E', + chr(195).chr(139) => 'E', chr(195).chr(140) => 'I', + chr(195).chr(141) => 'I', chr(195).chr(142) => 'I', + chr(195).chr(143) => 'I', chr(195).chr(145) => 'N', + chr(195).chr(146) => 'O', chr(195).chr(147) => 'O', + chr(195).chr(148) => 'O', chr(195).chr(149) => 'O', + chr(195).chr(150) => 'O', chr(195).chr(153) => 'U', + chr(195).chr(154) => 'U', chr(195).chr(155) => 'U', + chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y', + chr(195).chr(159) => 's', chr(195).chr(160) => 'a', + chr(195).chr(161) => 'a', chr(195).chr(162) => 'a', + chr(195).chr(163) => 'a', chr(195).chr(164) => 'a', + chr(195).chr(165) => 'a', chr(195).chr(167) => 'c', + chr(195).chr(168) => 'e', chr(195).chr(169) => 'e', + chr(195).chr(170) => 'e', chr(195).chr(171) => 'e', + chr(195).chr(172) => 'i', chr(195).chr(173) => 'i', + chr(195).chr(174) => 'i', chr(195).chr(175) => 'i', + chr(195).chr(177) => 'n', chr(195).chr(178) => 'o', + chr(195).chr(179) => 'o', chr(195).chr(180) => 'o', + chr(195).chr(181) => 'o', chr(195).chr(182) => 'o', + chr(195).chr(182) => 'o', chr(195).chr(185) => 'u', + chr(195).chr(186) => 'u', chr(195).chr(187) => 'u', + chr(195).chr(188) => 'u', chr(195).chr(189) => 'y', + chr(195).chr(191) => 'y', + // Decompositions for Latin Extended-A + chr(196).chr(128) => 'A', chr(196).chr(129) => 'a', + chr(196).chr(130) => 'A', chr(196).chr(131) => 'a', + chr(196).chr(132) => 'A', chr(196).chr(133) => 'a', + chr(196).chr(134) => 'C', chr(196).chr(135) => 'c', + chr(196).chr(136) => 'C', chr(196).chr(137) => 'c', + chr(196).chr(138) => 'C', chr(196).chr(139) => 'c', + chr(196).chr(140) => 'C', chr(196).chr(141) => 'c', + chr(196).chr(142) => 'D', chr(196).chr(143) => 'd', + chr(196).chr(144) => 'D', chr(196).chr(145) => 'd', + chr(196).chr(146) => 'E', chr(196).chr(147) => 'e', + chr(196).chr(148) => 'E', chr(196).chr(149) => 'e', + chr(196).chr(150) => 'E', chr(196).chr(151) => 'e', + chr(196).chr(152) => 'E', chr(196).chr(153) => 'e', + chr(196).chr(154) => 'E', chr(196).chr(155) => 'e', + chr(196).chr(156) => 'G', chr(196).chr(157) => 'g', + chr(196).chr(158) => 'G', chr(196).chr(159) => 'g', + chr(196).chr(160) => 'G', chr(196).chr(161) => 'g', + chr(196).chr(162) => 'G', chr(196).chr(163) => 'g', + chr(196).chr(164) => 'H', chr(196).chr(165) => 'h', + chr(196).chr(166) => 'H', chr(196).chr(167) => 'h', + chr(196).chr(168) => 'I', chr(196).chr(169) => 'i', + chr(196).chr(170) => 'I', chr(196).chr(171) => 'i', + chr(196).chr(172) => 'I', chr(196).chr(173) => 'i', + chr(196).chr(174) => 'I', chr(196).chr(175) => 'i', + chr(196).chr(176) => 'I', chr(196).chr(177) => 'i', + chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij', + chr(196).chr(180) => 'J', chr(196).chr(181) => 'j', + chr(196).chr(182) => 'K', chr(196).chr(183) => 'k', + chr(196).chr(184) => 'k', chr(196).chr(185) => 'L', + chr(196).chr(186) => 'l', chr(196).chr(187) => 'L', + chr(196).chr(188) => 'l', chr(196).chr(189) => 'L', + chr(196).chr(190) => 'l', chr(196).chr(191) => 'L', + chr(197).chr(128) => 'l', chr(197).chr(129) => 'L', + chr(197).chr(130) => 'l', chr(197).chr(131) => 'N', + chr(197).chr(132) => 'n', chr(197).chr(133) => 'N', + chr(197).chr(134) => 'n', chr(197).chr(135) => 'N', + chr(197).chr(136) => 'n', chr(197).chr(137) => 'N', + chr(197).chr(138) => 'n', chr(197).chr(139) => 'N', + chr(197).chr(140) => 'O', chr(197).chr(141) => 'o', + chr(197).chr(142) => 'O', chr(197).chr(143) => 'o', + chr(197).chr(144) => 'O', chr(197).chr(145) => 'o', + chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe', + chr(197).chr(148) => 'R',chr(197).chr(149) => 'r', + chr(197).chr(150) => 'R',chr(197).chr(151) => 'r', + chr(197).chr(152) => 'R',chr(197).chr(153) => 'r', + chr(197).chr(154) => 'S',chr(197).chr(155) => 's', + chr(197).chr(156) => 'S',chr(197).chr(157) => 's', + chr(197).chr(158) => 'S',chr(197).chr(159) => 's', + chr(197).chr(160) => 'S', chr(197).chr(161) => 's', + chr(197).chr(162) => 'T', chr(197).chr(163) => 't', + chr(197).chr(164) => 'T', chr(197).chr(165) => 't', + chr(197).chr(166) => 'T', chr(197).chr(167) => 't', + chr(197).chr(168) => 'U', chr(197).chr(169) => 'u', + chr(197).chr(170) => 'U', chr(197).chr(171) => 'u', + chr(197).chr(172) => 'U', chr(197).chr(173) => 'u', + chr(197).chr(174) => 'U', chr(197).chr(175) => 'u', + chr(197).chr(176) => 'U', chr(197).chr(177) => 'u', + chr(197).chr(178) => 'U', chr(197).chr(179) => 'u', + chr(197).chr(180) => 'W', chr(197).chr(181) => 'w', + chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y', + chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z', + chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z', + chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z', + chr(197).chr(190) => 'z', chr(197).chr(191) => 's', + // Euro Sign + chr(226).chr(130).chr(172) => 'E', + // GBP (Pound) Sign + chr(194).chr(163) => ''); - $string = strtr($string, $chars); - } - else - { - // Assume ISO-8859-1 if not UTF-8 - $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) - .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) - .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202) - .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) - .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218) - .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) - .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235) - .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) - .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251) - .chr(252).chr(253).chr(255); + $string = strtr($string, $chars); + } + else + { + // Assume ISO-8859-1 if not UTF-8 + $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) + .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) + .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202) + .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) + .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218) + .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) + .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235) + .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) + .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251) + .chr(252).chr(253).chr(255); - $chars['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'; + $chars['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'; - $string = strtr($string, $chars['in'], $chars['out']); - $double_chars = array(); - $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); - $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); - $string = str_replace($double_chars['in'], $double_chars['out'], $string); - } - return $string; - } + $string = strtr($string, $chars['in'], $chars['out']); + $double_chars = array(); + $double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254)); + $double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); + $string = str_replace($double_chars['in'], $double_chars['out'], $string); + } + return $string; + } - /* Add for 1.3 compatibility and avoid duplicate code */ - public static function jsonEncode($result) - { - return (method_exists('Tools', 'jsonEncode')) ? - Tools::jsonEncode($result) : Tools::jsonEncode($result); - } - - /* - ** Fix security and compatibility for PS < 1.4.5 - */ - public static function bqSQL($string) - { - return str_replace('`', '\`', pSQL($string)); - } - - /* - ** Check zip code by country - */ - public static function checkZipcodeByCountry($zipcode, $params) - { - $id_country = $params['id_country']; - - $zipcodeFormat = Db::getInstance()->getValue(' - SELECT `zip_code_format` - FROM `'._DB_PREFIX_.'country` - WHERE `id_country` = '.(int)$id_country); + /* Add for 1.3 compatibility and avoid duplicate code */ + public static function jsonEncode($result) + { + return (method_exists('Tools', 'jsonEncode')) ? + Tools::jsonEncode($result) : Tools::jsonEncode($result); + } + + /* + ** Fix security and compatibility for PS < 1.4.5 + */ + public static function bqSQL($string) + { + return str_replace('`', '\`', pSQL($string)); + } + + /* + ** Check zip code by country + */ + public static function checkZipcodeByCountry($zipcode, $params) + { + $id_country = $params['id_country']; + + $zipcodeFormat = Db::getInstance()->getValue(' + SELECT `zip_code_format` + FROM `'._DB_PREFIX_.'country` + WHERE `id_country` = '.(int)$id_country); - // -1 to warn user that no layout exist - if (!$zipcodeFormat) - return -1; + // -1 to warn user that no layout exist + if (!$zipcodeFormat) + return -1; - $regxMask = str_replace( - array('N', 'C', 'L'), - array( - '[0-9]', - Country::getIsoById((int)$id_country), - '[a-zA-Z]'), - $zipcodeFormat); - if (preg_match('/'.$regxMask.'/', $zipcode)) - return true; - return false; - } + $regxMask = str_replace( + array('N', 'C', 'L'), + array( + '[0-9]', + Country::getIsoById((int)$id_country), + '[a-zA-Z]'), + $zipcodeFormat); + if (preg_match('/'.$regxMask.'/', $zipcode)) + return true; + return false; + } - public static function getFormatedPhone($phone_number) - { - $begin = Tools::substr($phone_number, 0, 3); - $pad_number = (strpos($begin, '+3') !== false) ? 12 : - (strpos($begin, '00') ? 13 : 10); + public static function getFormatedPhone($phone_number) + { + $begin = Tools::substr($phone_number, 0, 3); + $pad_number = (strpos($begin, '+3') !== false) ? 12 : + (strpos($begin, '00') ? 13 : 10); - return str_pad(Tools::substr(preg_replace(MRTools::REGEX_CLEAN_PHONE, '', $phone_number), 0, $pad_number), $pad_number, '0'); - } + return str_pad(Tools::substr(preg_replace(MRTools::REGEX_CLEAN_PHONE, '', $phone_number), 0, $pad_number), $pad_number, '0', STR_PAD_LEFT); + } } ?> diff --git a/classes/index.php b/classes/index.php index dd2cf57..59b98f1 100644 --- a/classes/index.php +++ b/classes/index.php @@ -26,10 +26,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/classes/lib/changelog b/classes/lib/changelog new file mode 100644 index 0000000..b76580e --- /dev/null +++ b/classes/lib/changelog @@ -0,0 +1,648 @@ +2003-07-21, version 0.6.5 +- soap_transport_http: SOAPAction header is quoted again, fixes problem w/ Weblogic Server +- applied Jason Levitt patch for proper array serialization, fixes problem w/ Amazon shopping cart services +- fixed null value serialization +- applied patch from "BZC ToOn'S" - fixes wsdl serialization when no parameters +- applied John's patch, implementing compression for the server + +2003-07-22, version 0.6.5 +- soap_server: fixed bug causing charset encoding not to be passed to the parser +- soap_fault: added default encoding to the fault serialization +- soap_parser: changed the parser to pre-load the parent's result array when processing scalar values. This increases parsing speed. + +2003-07-23, version 0.6.5 +- soap_base: fix code that overwrites user-supplied attributes in serialize_val +- soap_base: use arrays-of-arrays rather than attempting multi-dimensional in serialize_val +- xmlschema: emit import statements and qualify all elements with prefix in serializeSchema (better interop with validation tools) +- soapclient: get xml character encoding from HTTP Content-Type header if provided, e.g. text/xml;charset="UTF-8" +- soapclient: use headers in call if provided (previously ignored this parameter) +- soap_server: in parse_request, if neither getallheaders nor $_SERVER are available, use $HTTP_SERVER_VARS to get SOAPAction and xml encoding + +2003-07-24, version 0.6.5 +- soap_transport_http: apply patch from Steven Brown "if the server closes connection prematurely, nusoap would spin trying to read data that isn't there" + +2003-07-25, version 0.6.5 +- wsdl: apply patch from Sven to workaround single schema limitation +- wsdl: apply a variant of the patch from Holger to handle empty values for array by serializing an array with 0 elements +- xmlschema: remove the redundant default namespace attribute on the schema element; everything in xsd is explicitly specified as being from xsd +- soap_transport_http: fix setCredentials and add TODO comments in sendHTTPS about what to change if this setCredentials stays + +2003-07-30, version 0.6.5 +- nusoap_base: change documentation of soap_defencoding to specify it is the encoding for outgoing messages +- nusoap_base: only change &, <, > to entities, not all HTML entities +- soap_transport_http: update the Content-Type header in sendRequest, since soap_defencoding could be changed after ctor is called +- soap_server: use soap_defencoding instead of charset_encoding +- soap_server: read encoding from _SERVER if available +- nusoap_base: do entity translation for string parameters with an xsd type specified (thanks David Derr) + +2003-07-31, version 0.6.5 +- soap_transport_http: add proxy authentication +- soap_transport_http: build payload the same way for http and https +- wsdl: add proxy authentication +- soapclient: add proxy authentication +- soapclient: allow proxy information in ctor, so that it can be used for wsdl + +2003-08-01, version 0.6.5 +- soap_transport_http: close a persistent connection that's at EOF +- soap_transport_http: prevent conflicts between setEncoding and usePersistentConnection +- soap_transport_http: fix use of $headers instead of $this->incoming_headers in getResponse +- soapclient: improve handling of persistent connections +- soapclient: force xml_encoding to upper case +- soap_server: let the Web server decide whether to close the connection (no Connection: close header) +- soap_server: force xml_encoding to upper case + +2003-08-04, version 0.6.5 +- soap_parser: use XML type information to pick a PHP data type; also decode base64 +- soap_server: read all HTTP headers when using _SERVER or HTTP_SERVER_VARS +- soap_server: add gzip encoding support for outgoing messages +- soap_transport_http: deflate is gzcompress/gzuncompress (cf. http://archive.develooper.com/libwww@perl.org/msg04650.html) +- soap_transport_http: clean use of persistentConnection so it's always a set boolean +- soapclient: add responseData member to access deflated/gunzipped payload + +2003-08-05, version 0.6.5 +- soap_server: look multiple places when setting debug_flag + +2003-08-07, version 0.6.5 +- nusoap_base: serialize specified type (e.g. ArrayOfString) even for simple array +- wsdl: only specify encodingStyle in the input/output soap bindings when it is not empty (thanks Guillaume) + +2003-08-15, version 0.6.5 +- soap_parser: fix parsing of elements with no XSD type specified +- soap_parser: use PHP string type for XSD long and unsignedLong types + +2003-08-16, version 0.6.5 +- soap_parser: fix code generating warning (thanks Torsten) + +2003-08-19, version 0.6.5 +- soap_parser: fix another line of code generating a warning (thanks Torsten) + +2003-08-22, version 0.6.5 +- soap_server: remove all '--' from debug_str; previous code changed '---' to '- --' +- wsdl, soapclient, soap_parser: patch submitted by Mark Spavin as described by + the following... +> Changes for the multiple/nested imports from the wsdl file. This builds an +> array of files not just the last one and also checks for relative paths to +> the parent. This will then get the imported files from the remote site +> instead of your local disk. Local wsdl files should still work (untested). +> +> Changes for multiple encoding sytles as previously posted + +2003-08-24, version 0.6.5 +- wsdl, soapclient: fix some PHP notices from previous update + +2003-08-26, version 0.6.5 +- wsdl: support multiple SOAP ports +- soapclient, soap_server: when no charset is specified, use UTF-8, even though HTTP specifies US-ASCII. +- soap_transport_http: do not prepend $host with 'ssl://' for https (is this required for older cURL versions?) + +2003-08-27, version 0.6.5 +- soap_server: support compressed request messages (thanks John Huong) +- soap_parser: deserialize Apache Vector as an array +- xmlschema: use $this->typemap in getPHPType (which is not used) +- soapclient, wsdl: check for WSDL errors after serializing parameters +- nusoap_base: add serialization of Apache Map (when not using WSDL) +- wsdl: add serialization of Apache Map (when using WSDL) +- wsdl: only change &, <, > to entities, not all HTML entities + +2003-08-28, version 0.6.5 +- soap_transport_http: disable cURL verification of peer and server (formerly the cURL default) +- soap_transport_http: mingle cURL code with straight http, so sendHTTP is no longer needed + +2003-08-29, version 0.6.6 +- soap_transport_http: add setContentType +- soapclient: call setContentType using new getHTTPContentType and getHTTPContentTypeCharset + +2003-09-05, version 0.6.6 +- wsdl: add some more code to handle null/nil values (but there's still a way to go) + +2003-10-21, version 0.6.6 +- soap_transport_http: only include port in Host header if it was specified in the URL +- soap_transport_http: add some code to use OpenSSL for PHP ssl:// scheme, but comment out since it's not ready +- soap_server: use $_SERVER['PHP_SELF'] if $GLOBALS['PHP_SELF'] is not set +- wsdl: add WSDL request and response and transport debug to debug +- wsdl: handle custom type extending xmlschema namespace (GLUE ... Thanks Matt) +- soap_parser: add param to docs +- soapclient: add getHTTPBody, getHTTPContentType, getHTTPContentTypeCharset (anticipating MIME subclass) + +2003-10-28, version 0.6.6 +- nusoap_base: add expandEntities method +- wsdl: use expandEntities +- soap_fault: use expandEntities +- soap_transport_http: Allow credentials to be included in URL, rather than requiring setCredentials +- soap_transport_http: Merge HTTP headers that span multiple lines +- soap_parser: Properly set errors in ctor +- soapclient: Pass headers to parseResponse and parse them in that method + +2003-10-30, version 0.6.6 +- xmlschema: Add some information for the related type to an element + +2003-12-09, version 0.6.6 +- nusoap_base: Add some namespace methods previously in xmlschema +- xmlschema: Improve parsing of complexType, element and simpleType +- xmlschema: Improve serialization +- xmlschema: Track imports +- xmlschema: Track elementFormDefault and form attributes +- wsdl: Support multiple (note that setting $server->wsdl->schemaTargetNamespace no longer does anything! Use configureWSDL instead.) +- wsdl: Use form attribute of element to control namespace specification +- wsdl: Support chained imports (A imports B which imports C) +- wsdl: Include port in endpoint address when serializing +- soap_server: Fix use of style (rpc|document) and use (encoded|literal) +- soap_server: Support _SERVER[CONTENT_TYPE] in addition to _SERVER[HTTP_CONTENT_TYPE] +- soap_server: Support wsdl with multiple +- soap_client: Remove a var_dump +- soap_client: Add style and use parameters to call method to support doc/lit without WSDL +- soap_transport_http: Check that $this->fp exists when doing persistent connections + +2003-12-17, version 0.6.6 +- soap_server: pass namespaces to xmlschema constructor +- wsdl: post-process after all imports +- wsdl: remove some debug, add some error handling +- xmlschema: allow enclosing namespaces to be specified in constructor +- xmlschema: improve handling of compositors and simple types + +2004-01-08, version 0.6.6 +- soap_server: when requested WSDL is in a file, return to client using passthru (thanks Ingo Fischer) +- soapclient: have proxy inherit more client state +- soapclient: allow timeout and response timeout to be specified in the constructor +- wsdl: allow timeout and response timeout to be specified in the constructor +- soap_transport_http: allow response timeout to be specified in send and sendHTTPS + +2004-01-28, version 0.6.6 +- wsdl: add namespace for array and scalar when form is qualified +- wsdl: fix a bug in which data type of complexType elements were ignored in serialization +- wsdl: enhance handling of URLs with file scheme +- wsdl: add addSimpleType +- xmlschema: add addSimpleType +- xmlschema: always set phpType elements +- soapclient: allow a wsdl instance to be specified in constructor +- soap_server: allow a wsdl instance to be specified in constructor (not tested!) +- soap_server: fix default SOAPAction created in register method +- soap_transport_http: accept chunking with LF separators in addition to CRLF. +- wsdlcache: added class +- nusoapmime: fix comments + +2004-02-23, version 0.6.6 +- soap_transport_http: don't try to unchunk cURL data, since cURL already does it +- soap_transport_http: append CVS revision to version in User-Agent +- wsdl: serialize boolean as true|false, not 1|0, to agree with XML Schema +- soap_server: always exit() after returning WSDL +- soap_server: use the WSDL URL scheme as the default endpoint URL scheme +- soap_server: append CVS revision to version in X-SOAP-Server +- nusoap_base: add (CVS) revision +- wsdlcache: synchronize using a per-WSDL lock file (Thanks Ingo) +- wsdlcache: add cache lifetime, after which cache contents are invalidated (Thanks Ingo) + +2004-03-15, version 0.6.6 +- nusoap_base: add isArraySimpleOrStruct method +- soap_server: improve WSDL URL scheme determination +- soap_server: only deflate/gzip payloads > 1024 bytes +- soap_server: fix parameter order in fault method (always used as faultcode, faultstring) +- soap_server: refactor parse_request into multiple functions (for sanity) +- soap_server: set the namespace on the Response element to the same as the request +- soap_server: name the return value element 'return' by default +- soap_server: added and documented data fields, so that service programmers can use them if desired +- soap_parser: standardize parsing error message +- soap_parser: fix document and responseHeaders so they are the correct XML text (as documented) +- soap_transport_http: fix read from persistent connection +- soapclient: clean up debugging for persistent connection +- wsdl: enforce correct naming of messages parts when an associative array is used for parameters +- wsdl: better serialization of null values +- wsdl: standardize parsing error message +- xmlschema: standardize parsing error message + +2004-03-24, version 0.6.7 +- soap_transport_http: add digest authentication (based on code by Kevin A. Miller) +- xmlschema: improve parsing of import elements +- wsdl: do schema imports even if there are no wsdl imports + +2004-04-12, version 0.6.7 +- wsdl: serialize multiple elements when maxOccurs="unbounded" and value is an array +- wsdl: serialize soapval values (used to force an XML type, e.g. when WSDL uses an abstract type) +- nusoapmime: do not require nusoap.php (it is now the programmer's responsibility) + +2004-04-21, version 0.6.7 +- soap_parser: parse repeated element name into an array (de-serializes doc/lit array into a PHP array when there is more than 1 array element) +- soap_server: do not wrap response in a response element for a document style service + +2004-04-30, version 0.6.7 +- soap_transport_http: allow digest auth params to be separated by "," as well as ", " +- soap_transport_http: re-initialize incoming headers for each response +- soap_server: add methodreturnisliteralxml property to allow service function to return XML as a string +- soapclient: improve rpc/literal support +- soapclient: allow XML string as call params in addition to array +- soapclient: support document style and literal encoding when not using WSDL + +2004-05-05, version 0.6.7 +- wsdl: serialize PHP objects for WSDL XML Schema complexTypes, in addition to associative arrays +- wsdl: fix WSDL generation when there is no encodingStyle +- soap_transport_http: suppress fsockopen warnings +- soap_transport_http: detect socket timeouts when reading (0 bytes returned) +- soap_transport_http: read chunked content "in-line" so it works on a persistent connection +- nusoap_base: serialize boolean as true|false, not 1|0, to agree with XML Schema +- nusoap_base: serialize array of struct differently than array of array + +2004-06-25, version 0.6.8 +- soap_server: prefer gzip to deflate, since IE does not like our deflate +- soap_server: move webDescription to the wsdl class +- soap_server: allow class and instance method calls for service (thanks Ingo Fischer and Roland Knall) +- wsdl: get webDescription from the soap_server class +- wsdl: allow compression from the server +- wsdl: fix serialization of soapval without a type +- wsdl: propagate debug value from query string to SOAP endpoint in programmatic WSDL generation +- nusoap_base: add anyType, anySimpleType for 2001 XML Schema +- nusoap_base: provide additional debug functions +- soap_transport_http: ignore Content-Length when chunked encoding is used +- soap_transport_http: remove ':' from username for Basic authentication (cf. RFC 2617) +- soap_transport_http: urldecode username and password taken from URL +- soap_transport_http: use raw inflate/deflate for IE/IIS compatibility, rather than having Zlib headers according to HTTP 1.1 spec +- soap_transport_http: attempt to handle the case when both the service application and Web server compress the response +- soapclient: when creating proxy methods, replace '.' in operation name with '__' in function name +- soapclient: initialize requestHeaders in proxy +- general: use new debug methods; never access debug_str directly + +2004-09-30, version 0.6.8 +- soapclient: do not allow getProxy call when WSDL is not used +- soapclient: use ISO-8859-1 as the charset if not specified in the Content-Type header +- soapclient: when an empty string is specified for the call namespace, do not put the method element in a namespace +- soapclient: let soap_transport_http check for SSL support +- soapclient: have proxy inherit soap_defencoding from the client from which it is generated +- soapclient: do not assume that 'ns1' is an unused namespace prefix; always generate namespace prefixes randomly +- soap_parser: compare any encoding in the XML declaration to the charset from the HTTP Content-Type header (thanks Ingo Fischer) +- soap_parser: improve parse repeated element name into an array (de-serializes doc/lit array into a PHP array when there is more than 1 array element) +- soap_server: use ISO-8859-1 as the charset if not specified in the Content-Type header +- soap_server: allow suppression of automatic UTF-8 decoding +- soap_server: fix a bug when call_user_func_array() is used +- soap_transport_http: correct digest authentication through a proxy +- wsdl: serialize SOAP-ENC types similarly to XSD types +- xmlschema: force unprefixed type into default namespace +- xmlschema: fix serialization of definition of simple types + +2004-10-01, version 0.6.8 +- soap_parser: handle default namespace attributes +- soap_server: add default_utf8 field +- soap_server: support literal encoding (with RPC style) +- soap_transport_http: parse HTTP status and generate error for 300, 302-307, 400, 401-417, 501-505 (thanks for the idea Ghislain) +- soap_transport_http: follow HTTP redirection (HTTP status 301 and Location header) (thanks for the idea Ghislain) +- xmlschema: allow any attributes to be specified in an element of a complexType, e.g., abstract, default, form, minOccurs, maxOccurs, nillable (thanks Jirka Pech for the original patch) + +2004-10-02, version 0.6.8 +- soapclient: read/write cookies (thanks Ingo) +- soap_server: change faultcode on non-resendable faults to Client +- soap_transport_http: read/write cookies (thanks Ingo) + +2004-10-05, version 0.6.8 +- wsdl: add addElement method +- wsdl: support the document style in the register method +- xmlschema: parse unnamed simpleTypes, rather than ignoring them +- xmlschema: include untyped elements when parsing a complexType +- xmlschema: add addElement method + +2004-10-14, version 0.6.8 +- soapclient: support client certificates +- soap_parser: deserialize attributes, prefixing names with "!" +- soap_server: notify the client with HTML when WSDL is requested but not supported by service +- soap_transport_http: support client certificates +- wsdl: support defaults for elements of a complexType +- wsdl: serialize elements from complexType extension base +- wsdl: serialize data (associative array elements) as attributes according to XML Schema +- xmlschema: record extension base if present for a complexType + +2004-12-15, version 0.6.8 +- nusoap_base: add 2000 XML Schema (rare, but used by Akamai) +- soap_parser: avoid deserializing more common attributes that are not data +- soap_parser: be lax when HTTP specifies ISO-8859-1 (the default) and XML specifies UTF-8 (the norm) +- soap_server: account for the fact that get_class_methods returns methods in all lower case (thanks Steve Haldane) +- soap_transport_http: parse digest info that includes '=' in the data (thanks Jinsuk Kim) +- wsdl: feably handle some cases for literal serialization of form="unqualified" elements +- wsdl: don't serialize the decimal portion of a PHP double when the XML type is long +- wsdl: fix serialization of attributes for complexType that is an extension +- wsdlcache: enhance diagnostics +- xmlschema: handle untyped elements +- xmlschema: handle WSDL for SOAP Array that uses the base attribute plus a sequence of element + +2005-01-22, version 0.6.8 +- wsdl: allow an element in one schema to have a type from another schema + +2005-01-24, version 0.6.8 +- xmlschema: correctly parse nested complexType definitions + +2005-02-14, version 0.6.8 +- nusoap_base: fix a bug in which attributes were sometimes not serialized with a value +- nusoap_base: improve serialization of null values (thanks Dominique Stender) +- soap_parser: parse null values by handling the nil attribute (thanks Dominique Stender) +- soap_server: set character encoding for a fault to be the same as for the server (thanks Mark Scott) +- soap_server: correctly check for null value returned from method when WSDL is used (without WSDL, cannot distinguish whether NULL or void return is desired) +- soapclient: for document style, call should always return an array rooted at the response part (all bets are off when there are multiple parts) +- xmlschema: save enumeration values parsed from WSDL + +2005-02-10, version 0.6.9 +- soapclient: only set SOAP headers when they are specified in call params (so setHeaders still works) + +2005-04-04, version 0.6.9 +- soap_server: use get_class instead of is_a (thanks Thomas Noel) +- soapclient: use get_class instead of is_a (thanks Thomas Noel) +- soapclient: add setEndpoint method +- soap_transport_http: fix client certificates (thanks Doug Anarino and Eryan Eriobowo) + +2005-04-29, version 0.6.9 +- nusoap_base: add global variable and methods for setting debug level +- nusoap_base: use xsd:anyType instead of xsd:ur-type to serialize arrays with multiple element types (thanks Ingo Fischer) +- nusoap_base: expand entities in attributes (thanks Gaetano Giunta) +- soapclient: call parent constructor +- soapval: call parent constructor +- soap_fault: call parent constructor +- soap_parser: call parent constructor +- soap_server: assume get_class_methods always returns lower case for PHP 4.x only +- soap_server: call parent constructor +- soap_transport_http: do nothing in setEncoding if gzdeflate is not present (thanks Franck Touanen for pointing this out) +- soap_transport_http: fix check for server request for digest authentication (thanks Mark Spavin) +- soap_transport_http: call parent constructor +- wsdl: fix documentation page popup of one method after another (thanks Owen) +- wsdl: call parent constructor +- wsdl: expand entities in attributes (thanks Gaetano Giunta) +- xmlschema: call parent constructor + +2005-06-03, version 0.6.9 +- nusoap_base: serialize empty arrays as having elements xsd:anyType[0] +- nusoap_base: add encodingStyle parameter to serializeEnvelope +- nusoap_base: serialize xsi:type with nil values +- nusoap_base: improve debug and comments +- soap_parser: correctly parse an empty array to an empty array, not an empty string +- soap_parser: improve debug and comments +- soap_server: specify encodingStyle for envelope when WSDL is used +- soapclient: factor out new getProxyClassCode method +- soapclient: specify encodingStyle for envelope +- soapclient: improve debug and comments +- wsdl: add namespace for Apache SOAP types if a variable of such type is serialized +- wsdl: serialize nil value for nillable elements when no value is provided +- wsdl: serialize xsi:type with nil values +- wsdl: copy attributes as well as elements to an element from its complexType +- wsdl: specify encodingStyle for operations +- wsdl: improve debug and comments +- xmlschema: improve debug and comments + +2005-06-03, version 0.7.0 +- nusoap_base: improve debug and comments +- nusoap_base: fix version, which should have been 0.7.0 since 2005-03-04 + +2005-06-06, version 0.7.1 +- nusoap_base: adjust numeric element names for serialization, instead of forcing them to 'soapVal' +- nusoapmime: add type=text/xml to multipart/related (thanks Emmanuel Cordonnier) +- soap_fault: fix serialization of detail +- soap_server: check required parameters for register method +- soap_server: when getallheaders is used, massage header names +- soap_server: use SOAPAction to determine operation when doc/lit service does not wrap parameters in an element with the method name (thanks Peter Hrastnik) +- soap_transport_http: correctly handle multiple HTTP/1.1 100 responses for https (thanks Jan Slabon) +- wsdl: fixed documentation for addComplexType (thanks Csintalan Įdįm) +- wsdl: serialize array data when maxOccurs = 'unbounded' OR maxOccurs > 1 (thanks Dominique Schreckling) +- wsdl: when serializing a string == 'false' as a boolean, set the value to false +- wsdl: when serializing a complexType, require the PHP value supplied to be an array + +2005-07-01, version 0.7.1 +- nusoap_base: Allow SOAP headers to be supplied as an array like parameters +- soap_parser: de-serialize simpleContent that accompanies complexContent +- soap_server: append debug information when programmatically-defined WSDL is returned +- soap_transport_http: Add debug when an outgoing header is set +- soapclient: Allow SOAP headers to be supplied as an array like parameters +- xmlschema: serialize attributes more generally, rather than assuming they are for SOAP 1.1 Array +- wsdl: when serializing, look up types by namespace, not prefix (simple programmatic doc/lit WSDL now seems to work) +- wsdl: process namespace declarations first when parsing an element + +2005-07-27, version 0.7.1 +- nusoap_base: do not override supplied element name with class name when serializing an object in serialize_val +- nusoap_base: remove http://soapinterop.org/xsd (si) from namespaces array +- nusoapmime: add nusoapservermime class to implement MIME attachments on the server +- soap_fault: improve documentation +- soap_server: improve documentation +- soap_server: make consistent use of _SERVER and HTTP_SERVER_VARS +- soap_server: make all incoming HTTP header keys lower case +- soap_server: add hook functions to support subclassing for MIME attachments +- soap_transport_http: remove an unnecessary global statement +- soapclient: when creating a proxy, make $params within each function an associative array +- soapval: improve documentation +- wsdl: when serializing complexType elements, used typed serialization if there is either a type or a reference for the element +- wsdl: allow PHP objects to be serialized as SOAP structs in serializeType +- wsdl: for WSDL and XML Schema imports, don't forget to use the TCP port number (thanks Luca GIOPPO) +- wsdl: make consistent use of _SERVER and HTTP_SERVER_VARS +- xmlschema: improve documentation + +2005-07-31, version 0.7.2 +- nusoap_base: correctly serialize attributes in serialize_val (thanks Hidran Arias) +- soap_parser: when resolving references, do not assume that buildVal returns an array (thanks Akshell) +- soap_parser: removed decode_entities, which does not work (thanks Martin Sarsale) +- soap_server: fix a bug parsing headers from _SERVER and HTTP_SERVER_VARS (thanks Bert Catsburg) +- soap_server: parse all "headers" from HTTP_SERVER_VARS (not just HTTP_*) +- soap_server: use PHP_SELF instead of SCRIPT_NAME for WSDL endpoint +- soap_server: when generating a fault while debug_flag is true, put debug into faultdetail +- wsdl: add enumeration parameter to addSimpleType +- xmlschema: add enumeration parameter to addSimpleType + +2006-02-02, version 0.7.2 +- soapclient: initialize paramArrayStr to improve proxy generation +- soap_parser: handle PHP5 soapclient's incorrect transmission of WSDL-described SOAP encoded arrays. +- soap_server: don't assume _SERVER['HTTPS'] is set; try HTTP_SERVER_VARS['HTTPS'] if it is not +- soap_server: "flatten out" the parameter array to call_user_func_array (thanks André Mamitzsch) +- soap_server: make thrown exceptions conform to specs +- wsdl: use serialize_val to serialize an array when the XSD type is soapenc:Array (JBoss/Axis does this) +- wsdl: change formatting of serialized XML for the WSDL +- xmlschema: change formatting of namespaces when serializing XML for the schema + +2006-04-07, version 0.7.2 +- soap_server: if methodparams is not an array, call call_user_func_array with an empty array (thanks Eric Grossi) +- wsdl: distinguish parts with element specified from those with type specified by suffixing element names with ^ +- wsdl: do a case-insensitive match on schema URI when looking for type +- xmlschema: only get element (not type) when name has ^ suffix + +2006-05-16, version 0.7.2 +- soapclient: add getHeader to get parsed SOAP Header +- soap_parser: check status when receiving Header or Body element +- soap_parser: add soapheader +- soap_server: add requestHeader with parsed SOAP Header + +2006-06-15, version 0.7.2 +- wsdl: fix bug in addComplexType (thanks Maarten Meijer) +- soap_transport_http: change cURL message + +2007-03-19, version 0.7.2 +- soapclient: declare as nusoapclient, then also subclass soapclient if SOAP extension not loaded +- soapclientmime: declare as nusoapclientmime, then also subclass soapclientmime if SOAP extension not loaded + +2007-03-28, version 0.7.2 +- nusoap_base: fix serialization of a soapval when its value is a soapval +- soapval: fix serialization of a soapval when its value is a soapval +- soapval: add __toString (cf. http://article.gmane.org/gmane.comp.php.nusoap.general/2776) +- nusoapclient: use lazy retrieval of WSDL instead of always getting it in the constructor +- nusoapclient: fix getProxy that was broken in last revision +- wsdl: add ability to set authorization credentials and retrieve WSDL outside of constructor + +2007-04-05, version 0.7.2 +- nusoapclientmime: don't rely exclusively on Content-Disposition to distinguish the root part from attachment; also check Content-Type (thanks Ben Bosman) +- nusoapclientmime: obey RFC 2045 Section 5.1 (thanks Chris Butler) +- nusoapservermime: don't rely exclusively on Content-Disposition to distinguish the root part from attachment; also check Content-Type (thanks Ben Bosman) +- nusoapservermime: obey RFC 2045 Section 5.1 (thanks Chris Butler) +- nusoap_base: remove extra whitespace from some XML elements +- nusoap_base: allow SOAP headers to be specified as an associative array (thanks Unique) +- nusoap_base: implement __toString +- nusoap_base: improve doc accuracy and consistency (thanks Martin K?gler) +- iso8601_to_timestamp: avoid problem with negative hours after calculation, etc. (thanks Guntram Trebs) +- nusoapclient: support user-settable cURL options (thanks Ciprian Popovici) +- nusoapclient: call SOAP 1.2 binding operations if no SOAP 1.1 present (there is no reason to believe this will always work!) +- nusoapclient: improve doc accuracy and consistency (thanks Martin K?gler) +- soap_server: don't try to use eval to call function when any parameter is an object +- soap_server: don't print return value within debug string; returned objects would need __toString in PHP 5.2 +- soap_server: use URL scheme for WSDL access as the scheme in SOAPAction +- soap_server: strip port number from server name (some FastCGI implementations include port in server name) +- soap_transport_http: support user-settable cURL options (thanks Ciprian Popovici) +- soap_transport_http: use cURL for NTLM authentication +- soap_transport_http: make digest authentication work for GET as well as POST +- soap_transport_http: improve doc accuracy and consistency (thanks Martin K?gler) +- soapval: remove __toString +- wsdl: set operation style if necessary, but do not override one already provided (thanks Raffaele Capobianco) +- wsdl: check SOAP 1.2 binding operations if no SOAP 1.1 present +- wsdl: improve doc accuracy and consistency (thanks Martin K?gler) +- xmlschema: fix simpleType serialization +- xmlschema: improve doc accuracy and consistency (thanks Martin K?gler) + +2007-04-09, version 0.7.2 +- nusoapclient: set decode_utf8 when creating a proxy (thanks Dmitri Dmitrienko) +- nusoapclient: rename class to nusoap_client +- soap_fault: also provide a class named nusoap_fault +- soap_parser: also provide a class named nusoap_parser +- soap_server: also provide a class named nusoap_server +- soap_transport_http: skip HTTP responses 301 and 401 when using cURL +- soap_transport_http: don't force HTTP Connection header when using cURL +- soap_transport_http: don't set HTTP Host and Content-Length headers when using cURL +- soap_transport_http: support CURLOPT_SSLCERTPASSWD (thanks David Blanco) +- wsdl: support user-settable cURL options (thanks Ciprian Popovici) +- wsdl: serialize parameters for non-SOAP 1.1 binding operations (there is no reason to believe this will always work!) +- xmlschema: also provide a class named nusoap_xmlschema +- nusoapclientmime: rename class to nusoap_client_mime +- nusoapservermime: rename class to nusoap_server_mime + +2007-04-11, version 0.7.2 +- nusoap_client: enable cURL usage to be forced (thanks Giunta Gaetano) +- soap_transport_http: enable cURL proxy usage (thanks Giunta Gaetano) +- soap_transport_http: enable cURL usage to be forced (thanks Giunta Gaetano) +- soap_transport_http: use cURL's HTTP authentication options for basic, digest +- wsdl: enable cURL usage to be forced (thanks Giunta Gaetano) + +2007-04-12, version 0.7.2 +- nusoap_client: add debug +- nusoap_xmlschema: don't add elements of complexTypes to elements array (thanks Heiko Hund) +- soap_transport_http: set cURL connection timeout if supported +- soap_transport_http: add debug when setting cURL option +- soap_transport_http: fix digest authentication broken in previous revision +- wsdl: add debug +- wsdlcache: address some issues with non-existing cache-files and PHP Warnings which came in such cases (thanks Ingo Fischer) +- wsdlcache: change class name to nusoap_wsdlcache + +2007-04-13, version 0.7.2 +- wsdl: wrap parameters if unwrapped values are supplied and WSDL specifies Microsoft-style wrapping + +2007-04-16, version 0.7.2 +- nusoap_base: avoid warning in getDebugAsXMLComment +- nusoap_client: small debug change +- nusoap_client_mime: set responseData when the root part is found + +2007-04-17, version 0.7.2 +- soap_transport_http: improve detection of undefined cURL options (thanks Ingo Fischer) + +2007-05-28, version 0.7.2 +- soap_transport_http: support digest authentication opaque feature (cf. RFC 2617) (thanks Daniel Lacroix) +- soap_transport_http: check safe_mode and open_basedir before setting CURLOPT_FOLLOWLOCATION +- soap_transport_http: skip "HTTP/1.0 200 Connection established" header when cURL returns it (thanks Raimund Jacob) +- nusoap_client: improve handling when getProxy is called and WSDL is not being used +- nusoap_base: add comments about which specifications are used/implemented by NuSOAP +- nusoap_xmlschema: create names for unnamed types that are unique by scope within XML Schema + +2007-06-11, version 0.7.2 +- wsdl: wrap return value if unwrapped value is supplied and WSDL specifies Microsoft-style wrapping + +2007-06-22, version 0.7.2 +- nusoap_xmlschema: fix serialization of simpleType restriction (thanks Rizwan Tejpar) + +2007-07-30, version 0.7.2 +- nusoap_server: Per http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735, rpc/literal accessor elements should not be in a namespace (thanks Kostas Kalevras) +- nusoap_client: Per http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735, rpc/literal accessor elements should not be in a namespace (thanks Kostas Kalevras) + +2007-10-21, version 0.7.2 +- nusoap_server: Per http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735, rpc/literal accessor elements should not be in a namespace (thanks Kostas Kalevras) +- nusoap_client: Per http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735, rpc/literal accessor elements should not be in a namespace (thanks Kostas Kalevras) + +2007-10-26, version 0.7.2 +- nusoap_server: Fix munging of _SERVER variables that start with HTTP_ (thanks Thomas Wieczorek) + +2007-10-30, version 0.7.2 +- nusoap_xmlschema: Serialize values for elementFormDefault, attributeFormDefault +- wsdl: Improve consistency between doc/lit schema auto-wrapping and client's parsed schema +- nusoap_server: Correct bug that placed encodingType in Envelope for doc/lit +- nusoap_server: Specify elementFormDefault for schema within doc/lit wsdl + +2007-10-31, version 0.7.2 +- wsdl: Fix typo in parametersMatchWrapped (thanks Sam Stepanyan) +- soap_transport_http: Fix three typos in setProxy (thanks Sam Stepanyan) +- nusoap_xmlschema: Fix typo in serializeTypeDef (thanks Sam Stepanyan) + +2007-11-06, version 1.0rc1 +- wsdl: Improve handling of return values from doc/lit methods +- nusoap_server: Handle case when method is not in a namespace + +2007-11-27, version 1.0rc1 +- nusoap_server: always try to invoke service for a POST +- nusoap_server: only return Location: for WSDL at http://... +- nusoap_base: change some syntax associated with globalDebugLevel + +2008-01-08, version 1.0rc1 +- nusoap_server: fix a typo where = was used instead of == (thanks J. (Johan) Bosma) + +2008-01-10, version 1.0rc1 +- nusoap_client: handle case where request or response has no content-type header (thanks Ingo Fischer) +- nusoap_server: handle case where request or response has no content-type header (thanks Ingo Fischer) +- wsdl: change CSS for .title in webDescription (thanks Marcus Uy) + +2008-01-25, version 1.0rc1 +- nusoap_xmlschema: when an element is of a complexType that is an extension, copy extensionBase from the type +- nusoap_xmlschema: do not apply elementFormDefault to globally defined elements + +2008-02-11, version 1.0rc1 +- wsdl: internally set form of wrapped parameter elements to unqualified (so server handles correctly) + +2008-03-03, version 1.0.rc1 +- nusoap_xmlschema: fix extension when base type has no explicit prefix +- nusoap_xmlschema: support XML Schema include +- wsdl: improve support for sequence by serializing inherited attributes and elements first + +2008-03-04, version 1.0.rc1 +- wsdl: allow WSDL port name to be specified in getOperations +- nusoap_client: allow WSDL port name to be specified in ctor + +2008-03-06, version 1.0rc1 +- wsdl: fix some port name variable references +- nusoap_base: change comments regarding preferred mode of support +- wsdl2nusoap: initial revision + +2008-03-14, version 1.0rc1 +- nusoap_base: fix timezone offset in timestamp_to_iso8601 (thanks Mario Trojan) + +2008-03-27, version 1.0rc1 +- nusoap_server: fix bug setting encodingStyle in serialize_return (thanks Luca Gobbo) + +2008-05-15, version 1.0rc1 +- nusoap_parser: handle case where Header or Body tags are used within SOAP messages (thanks Sergey Zhuravlev) + +2008-08-26, version 1.0rc1 +- wsdl: serialize simpleContent for complexType +- wsdl: avoid serializing complexType elements with no value and minOccurs = 0 regardless of nillability + +2010-04-26, version 0.9.5 +- nusoap_xmlschema: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- wsdl: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- soap_transport_http: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- soap_transport_http: remove call to deprecated function set_magic_quotes_runtime +- nusoap_server: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- nusoap_server: check that value is an object before calling get_class (thanks Pier-Luc Duchaine) +- nusoap_parser: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- nusoap_client: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- nusoap_client: do not assign the return value of new by reference (it is deprecated) (thanks Pier-Luc Duchaine) +- nusoap_base: replace regex function calls (ereg, eregi, split) with PCRE calls (preg_match, preg_split) (thanks Pier-Luc Duchaine) +- nusoapmime: do not assign the return value of new by reference (it is deprecated) diff --git a/classes/lib/class.nusoap_base.php b/classes/lib/class.nusoap_base.php new file mode 100644 index 0000000..4ed92a7 --- /dev/null +++ b/classes/lib/class.nusoap_base.php @@ -0,0 +1,996 @@ + +* @author Scott Nichol +* @version $Id: class.nusoap_base.php,v 1.56 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_base { + /** + * Identification for HTTP headers. + * + * @var string + * @access private + */ + var $title = 'NuSOAP'; + /** + * Version for HTTP headers. + * + * @var string + * @access private + */ + var $version = '0.9.5'; + /** + * CVS revision for HTTP headers. + * + * @var string + * @access private + */ + var $revision = '$Revision: 1.56 $'; + /** + * Current error string (manipulated by getError/setError) + * + * @var string + * @access private + */ + var $error_str = ''; + /** + * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment) + * + * @var string + * @access private + */ + var $debug_str = ''; + /** + * toggles automatic encoding of special characters as entities + * (should always be true, I think) + * + * @var boolean + * @access private + */ + var $charencoding = true; + /** + * the debug level for this instance + * + * @var integer + * @access private + */ + var $debugLevel; + + /** + * set schema version + * + * @var string + * @access public + */ + var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema'; + + /** + * charset encoding for outgoing messages + * + * @var string + * @access public + */ + var $soap_defencoding = 'ISO-8859-1'; + //var $soap_defencoding = 'UTF-8'; + + /** + * namespaces in an array of prefix => uri + * + * this is "seeded" by a set of constants, but it may be altered by code + * + * @var array + * @access public + */ + var $namespaces = array( + 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/', + 'xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/' + ); + + /** + * namespaces used in the current context, e.g. during serialization + * + * @var array + * @access private + */ + var $usedNamespaces = array(); + + /** + * XML Schema types in an array of uri => (array of xml type => php type) + * is this legacy yet? + * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings. + * @var array + * @access public + */ + var $typemap = array( + 'http://www.w3.org/2001/XMLSchema' => array( + 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double', + 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'', + 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string', + // abstract "any" types + 'anyType'=>'string','anySimpleType'=>'string', + // derived datatypes + 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'', + 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer', + 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer', + 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''), + 'http://www.w3.org/2000/10/XMLSchema' => array( + 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', + 'float'=>'double','dateTime'=>'string', + 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), + 'http://www.w3.org/1999/XMLSchema' => array( + 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', + 'float'=>'double','dateTime'=>'string', + 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), + 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'), + 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'), + 'http://xml.apache.org/xml-soap' => array('Map') + ); + + /** + * XML entities to convert + * + * @var array + * @access public + * @deprecated + * @see expandEntities + */ + var $xmlEntities = array('quot' => '"','amp' => '&', + 'lt' => '<','gt' => '>','apos' => "'"); + + /** + * constructor + * + * @access public + */ + function nusoap_base() { + $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel']; + } + + /** + * gets the global debug level, which applies to future instances + * + * @return integer Debug level 0-9, where 0 turns off + * @access public + */ + function getGlobalDebugLevel() { + return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel']; + } + + /** + * sets the global debug level, which applies to future instances + * + * @param int $level Debug level 0-9, where 0 turns off + * @access public + */ + function setGlobalDebugLevel($level) { + $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level; + } + + /** + * gets the debug level for this instance + * + * @return int Debug level 0-9, where 0 turns off + * @access public + */ + function getDebugLevel() { + return $this->debugLevel; + } + + /** + * sets the debug level for this instance + * + * @param int $level Debug level 0-9, where 0 turns off + * @access public + */ + function setDebugLevel($level) { + $this->debugLevel = $level; + } + + /** + * adds debug data to the instance debug string with formatting + * + * @param string $string debug data + * @access private + */ + function debug($string){ + if ($this->debugLevel > 0) { + $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n"); + } + } + + /** + * adds debug data to the instance debug string without formatting + * + * @param string $string debug data + * @access public + */ + function appendDebug($string){ + if ($this->debugLevel > 0) { + // it would be nice to use a memory stream here to use + // memory more efficiently + $this->debug_str .= $string; + } + } + + /** + * clears the current debug data for this instance + * + * @access public + */ + function clearDebug() { + // it would be nice to use a memory stream here to use + // memory more efficiently + $this->debug_str = ''; + } + + /** + * gets the current debug data for this instance + * + * @return debug data + * @access public + */ + function &getDebug() { + // it would be nice to use a memory stream here to use + // memory more efficiently + return $this->debug_str; + } + + /** + * gets the current debug data for this instance as an XML comment + * this may change the contents of the debug data + * + * @return debug data as an XML comment + * @access public + */ + function &getDebugAsXMLComment() { + // it would be nice to use a memory stream here to use + // memory more efficiently + while (strpos($this->debug_str, '--')) { + $this->debug_str = str_replace('--', '- -', $this->debug_str); + } + $ret = ""; + return $ret; + } + + /** + * expands entities, e.g. changes '<' to '<'. + * + * @param string $val The string in which to expand entities. + * @access private + */ + function expandEntities($val) { + if ($this->charencoding) { + $val = str_replace('&', '&', $val); + $val = str_replace("'", ''', $val); + $val = str_replace('"', '"', $val); + $val = str_replace('<', '<', $val); + $val = str_replace('>', '>', $val); + } + return $val; + } + + /** + * returns error string if present + * + * @return mixed error string or false + * @access public + */ + function getError(){ + if($this->error_str != ''){ + return $this->error_str; + } + return false; + } + + /** + * sets error string + * + * @return boolean $string error string + * @access private + */ + function setError($str){ + $this->error_str = $str; + } + + /** + * detect if array is a simple array or a struct (associative array) + * + * @param mixed $val The PHP array + * @return string (arraySimple|arrayStruct) + * @access private + */ + function isArraySimpleOrStruct($val) { + $keyList = array_keys($val); + foreach ($keyList as $keyListValue) { + if (!is_int($keyListValue)) { + return 'arrayStruct'; + } + } + return 'arraySimple'; + } + + /** + * serializes PHP values in accordance w/ section 5. Type information is + * not serialized if $use == 'literal'. + * + * @param mixed $val The value to serialize + * @param string $name The name (local part) of the XML element + * @param string $type The XML schema type (local part) for the element + * @param string $name_ns The namespace for the name of the XML element + * @param string $type_ns The namespace for the type of the element + * @param array $attributes The attributes to serialize as name=>value pairs + * @param string $use The WSDL "use" (encoded|literal) + * @param boolean $soapval Whether this is called from soapval. + * @return string The serialized element, possibly with child elements + * @access public + */ + function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) { + $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval"); + $this->appendDebug('value=' . $this->varDump($val)); + $this->appendDebug('attributes=' . $this->varDump($attributes)); + + if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) { + $this->debug("serialize_val: serialize soapval"); + $xml = $val->serialize($use); + $this->appendDebug($val->getDebug()); + $val->clearDebug(); + $this->debug("serialize_val of soapval returning $xml"); + return $xml; + } + // force valid name if necessary + if (is_numeric($name)) { + $name = '__numeric_' . $name; + } elseif (! $name) { + $name = 'noname'; + } + // if name has ns, add ns prefix to name + $xmlns = ''; + if($name_ns){ + $prefix = 'nu'.rand(1000,9999); + $name = $prefix.':'.$name; + $xmlns .= " xmlns:$prefix=\"$name_ns\""; + } + // if type is prefixed, create type prefix + if($type_ns != '' && $type_ns == $this->namespaces['xsd']){ + // need to fix this. shouldn't default to xsd if no ns specified + // w/o checking against typemap + $type_prefix = 'xsd'; + } elseif($type_ns){ + $type_prefix = 'ns'.rand(1000,9999); + $xmlns .= " xmlns:$type_prefix=\"$type_ns\""; + } + // serialize attributes if present + $atts = ''; + if($attributes){ + foreach($attributes as $k => $v){ + $atts .= " $k=\"".$this->expandEntities($v).'"'; + } + } + // serialize null value + if (is_null($val)) { + $this->debug("serialize_val: serialize null"); + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$xmlns$atts/>"; + $this->debug("serialize_val returning $xml"); + return $xml; + } else { + if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>"; + $this->debug("serialize_val returning $xml"); + return $xml; + } + } + // serialize if an xsd built-in primitive type + if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){ + $this->debug("serialize_val: serialize xsd built-in primitive type"); + if (is_bool($val)) { + if ($type == 'boolean') { + $val = $val ? 'true' : 'false'; + } elseif (! $val) { + $val = 0; + } + } else if (is_string($val)) { + $val = $this->expandEntities($val); + } + if ($use == 'literal') { + $xml = "<$name$xmlns$atts>$val"; + $this->debug("serialize_val returning $xml"); + return $xml; + } else { + $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val"; + $this->debug("serialize_val returning $xml"); + return $xml; + } + } + // detect type and serialize + $xml = ''; + switch(true) { + case (is_bool($val) || $type == 'boolean'): + $this->debug("serialize_val: serialize boolean"); + if ($type == 'boolean') { + $val = $val ? 'true' : 'false'; + } elseif (! $val) { + $val = 0; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val"; + } + break; + case (is_int($val) || is_long($val) || $type == 'int'): + $this->debug("serialize_val: serialize int"); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val"; + } + break; + case (is_float($val)|| is_double($val) || $type == 'float'): + $this->debug("serialize_val: serialize float"); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val"; + } + break; + case (is_string($val) || $type == 'string'): + $this->debug("serialize_val: serialize string"); + $val = $this->expandEntities($val); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val"; + } + break; + case is_object($val): + $this->debug("serialize_val: serialize object"); + if (get_class($val) == 'soapval') { + $this->debug("serialize_val: serialize soapval object"); + $pXml = $val->serialize($use); + $this->appendDebug($val->getDebug()); + $val->clearDebug(); + } else { + if (! $name) { + $name = get_class($val); + $this->debug("In serialize_val, used class name $name as element name"); + } else { + $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val)); + } + foreach(get_object_vars($val) as $k => $v){ + $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + if(isset($type) && isset($type_prefix)){ + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$pXml"; + } else { + $xml .= "<$name$xmlns$type_str$atts>$pXml"; + } + break; + break; + case (is_array($val) || $type): + // detect if struct or array + $valueType = $this->isArraySimpleOrStruct($val); + if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){ + $this->debug("serialize_val: serialize array"); + $i = 0; + if(is_array($val) && count($val)> 0){ + foreach($val as $v){ + if(is_object($v) && get_class($v) == 'soapval'){ + $tt_ns = $v->type_ns; + $tt = $v->type; + } elseif (is_array($v)) { + $tt = $this->isArraySimpleOrStruct($v); + } else { + $tt = gettype($v); + } + $array_types[$tt] = 1; + // TODO: for literal, the name should be $name + $xml .= $this->serialize_val($v,'item',false,false,false,false,$use); + ++$i; + } + if(count($array_types) > 1){ + $array_typename = 'xsd:anyType'; + } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) { + if ($tt == 'integer') { + $tt = 'int'; + } + $array_typename = 'xsd:'.$tt; + } elseif(isset($tt) && $tt == 'arraySimple'){ + $array_typename = 'SOAP-ENC:Array'; + } elseif(isset($tt) && $tt == 'arrayStruct'){ + $array_typename = 'unnamed_struct_use_soapval'; + } else { + // if type is prefixed, create type prefix + if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){ + $array_typename = 'xsd:' . $tt; + } elseif ($tt_ns) { + $tt_prefix = 'ns' . rand(1000, 9999); + $array_typename = "$tt_prefix:$tt"; + $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\""; + } else { + $array_typename = $tt; + } + } + $array_type = $i; + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\""; + } + // empty array + } else { + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\""; + } + } + // TODO: for array in literal, there is no wrapper here + $xml = "<$name$xmlns$type_str$atts>".$xml.""; + } else { + // got a struct + $this->debug("serialize_val: serialize struct"); + if(isset($type) && isset($type_prefix)){ + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>"; + } else { + $xml .= "<$name$xmlns$type_str$atts>"; + } + foreach($val as $k => $v){ + // Apache Map + if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') { + $xml .= ''; + $xml .= $this->serialize_val($k,'key',false,false,false,false,$use); + $xml .= $this->serialize_val($v,'value',false,false,false,false,$use); + $xml .= ''; + } else { + $xml .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + $xml .= ""; + } + break; + default: + $this->debug("serialize_val: serialize unknown"); + $xml .= 'not detected, got '.gettype($val).' for '.$val; + break; + } + $this->debug("serialize_val returning $xml"); + return $xml; + } + + /** + * serializes a message + * + * @param string $body the XML of the SOAP body + * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array + * @param array $namespaces optional the namespaces used in generating the body and headers + * @param string $style optional (rpc|document) + * @param string $use optional (encoded|literal) + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @return string the message + * @access public + */ + function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){ + // TODO: add an option to automatically run utf8_encode on $body and $headers + // if $this->soap_defencoding is UTF-8. Not doing this automatically allows + // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1 + + $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle"); + $this->debug("headers:"); + $this->appendDebug($this->varDump($headers)); + $this->debug("namespaces:"); + $this->appendDebug($this->varDump($namespaces)); + + // serialize namespaces + $ns_string = ''; + foreach(array_merge($this->namespaces,$namespaces) as $k => $v){ + $ns_string .= " xmlns:$k=\"$v\""; + } + if($encodingStyle) { + $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string"; + } + + // serialize headers + if($headers){ + if (is_array($headers)) { + $xml = ''; + foreach ($headers as $k => $v) { + if (is_object($v) && get_class($v) == 'soapval') { + $xml .= $this->serialize_val($v, false, false, false, false, false, $use); + } else { + $xml .= $this->serialize_val($v, $k, false, false, false, false, $use); + } + } + $headers = $xml; + $this->debug("In serializeEnvelope, serialized array of headers to $headers"); + } + $headers = "".$headers.""; + } + // serialize envelope + return + 'soap_defencoding .'"?'.">". + '". + $headers. + "". + $body. + "". + ""; + } + + /** + * formats a string to be inserted into an HTML stream + * + * @param string $str The string to format + * @return string The formatted string + * @access public + * @deprecated + */ + function formatDump($str){ + $str = htmlspecialchars($str); + return nl2br($str); + } + + /** + * contracts (changes namespace to prefix) a qualified name + * + * @param string $qname qname + * @return string contracted qname + * @access private + */ + function contractQname($qname){ + // get element namespace + //$this->xdebug("Contract $qname"); + if (strrpos($qname, ':')) { + // get unqualified name + $name = substr($qname, strrpos($qname, ':') + 1); + // get ns + $ns = substr($qname, 0, strrpos($qname, ':')); + $p = $this->getPrefixFromNamespace($ns); + if ($p) { + return $p . ':' . $name; + } + return $qname; + } else { + return $qname; + } + } + + /** + * expands (changes prefix to namespace) a qualified name + * + * @param string $qname qname + * @return string expanded qname + * @access private + */ + function expandQname($qname){ + // get element prefix + if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){ + // get unqualified name + $name = substr(strstr($qname,':'),1); + // get ns prefix + $prefix = substr($qname,0,strpos($qname,':')); + if(isset($this->namespaces[$prefix])){ + return $this->namespaces[$prefix].':'.$name; + } else { + return $qname; + } + } else { + return $qname; + } + } + + /** + * returns the local part of a prefixed string + * returns the original string, if not prefixed + * + * @param string $str The prefixed string + * @return string The local part + * @access public + */ + function getLocalPart($str){ + if($sstr = strrchr($str,':')){ + // get unqualified name + return substr( $sstr, 1 ); + } else { + return $str; + } + } + + /** + * returns the prefix part of a prefixed string + * returns false, if not prefixed + * + * @param string $str The prefixed string + * @return mixed The prefix or false if there is no prefix + * @access public + */ + function getPrefix($str){ + if($pos = strrpos($str,':')){ + // get prefix + return substr($str,0,$pos); + } + return false; + } + + /** + * pass it a prefix, it returns a namespace + * + * @param string $prefix The prefix + * @return mixed The namespace, false if no namespace has the specified prefix + * @access public + */ + function getNamespaceFromPrefix($prefix){ + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + //$this->setError("No namespace registered for prefix '$prefix'"); + return false; + } + + /** + * returns the prefix for a given namespace (or prefix) + * or false if no prefixes registered for the given namespace + * + * @param string $ns The namespace + * @return mixed The prefix, false if the namespace has no prefixes + * @access public + */ + function getPrefixFromNamespace($ns) { + foreach ($this->namespaces as $p => $n) { + if ($ns == $n || $ns == $p) { + $this->usedNamespaces[$p] = $n; + return $p; + } + } + return false; + } + + /** + * returns the time in ODBC canonical form with microseconds + * + * @return string The time in ODBC canonical form with microseconds + * @access public + */ + function getmicrotime() { + if (function_exists('gettimeofday')) { + $tod = gettimeofday(); + $sec = $tod['sec']; + $usec = $tod['usec']; + } else { + $sec = time(); + $usec = 0; + } + return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec); + } + + /** + * Returns a string with the output of var_dump + * + * @param mixed $data The variable to var_dump + * @return string The output of var_dump + * @access public + */ + function varDump($data) { + ob_start(); + var_dump($data); + $ret_val = ob_get_contents(); + ob_end_clean(); + return $ret_val; + } + + /** + * represents the object as a string + * + * @return string + * @access public + */ + function __toString() { + return $this->varDump($this); + } +} + +// XML Schema Datatype Helper Functions + +//xsd:dateTime helpers + +/** +* convert unix timestamp to ISO 8601 compliant date string +* +* @param int $timestamp Unix time stamp +* @param boolean $utc Whether the time stamp is UTC or local +* @return mixed ISO 8601 date string or false +* @access public +*/ +function timestamp_to_iso8601($timestamp,$utc=true){ + $datestr = date('Y-m-d\TH:i:sO',$timestamp); + $pos = strrpos($datestr, "+"); + if ($pos === FALSE) { + $pos = strrpos($datestr, "-"); + } + if ($pos !== FALSE) { + if (strlen($datestr) == $pos + 5) { + $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2); + } + } + if($utc){ + $pattern = '/'. + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + '/'; + + if(preg_match($pattern,$datestr,$regs)){ + return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]); + } + return false; + } else { + return $datestr; + } +} + +/** +* convert ISO 8601 compliant date string to unix timestamp +* +* @param string $datestr ISO 8601 compliant date string +* @return mixed Unix timestamp (int) or false +* @access public +*/ +function iso8601_to_timestamp($datestr){ + $pattern = '/'. + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + '/'; + if(preg_match($pattern,$datestr,$regs)){ + // not utc + if($regs[8] != 'Z'){ + $op = substr($regs[8],0,1); + $h = substr($regs[8],1,2); + $m = substr($regs[8],strlen($regs[8])-2,2); + if($op == '-'){ + $regs[4] = $regs[4] + $h; + $regs[5] = $regs[5] + $m; + } elseif($op == '+'){ + $regs[4] = $regs[4] - $h; + $regs[5] = $regs[5] - $m; + } + } + return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); +// return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z"); + } else { + return false; + } +} + +/** +* sleeps some number of microseconds +* +* @param string $usec the number of microseconds to sleep +* @access public +* @deprecated +*/ +function usleepWindows($usec) +{ + $start = gettimeofday(); + + do + { + $stop = gettimeofday(); + $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + + $stop['usec'] - $start['usec']; + } + while ($timePassed < $usec); +} + + +?> \ No newline at end of file diff --git a/classes/lib/class.soap_fault.php b/classes/lib/class.soap_fault.php new file mode 100644 index 0000000..1515b50 --- /dev/null +++ b/classes/lib/class.soap_fault.php @@ -0,0 +1,90 @@ + +* @version $Id: class.soap_fault.php,v 1.14 2007/04/11 15:49:47 snichol Exp $ +* @access public +*/ +class nusoap_fault extends nusoap_base { + /** + * The fault code (client|server) + * @var string + * @access private + */ + var $faultcode; + /** + * The fault actor + * @var string + * @access private + */ + var $faultactor; + /** + * The fault string, a description of the fault + * @var string + * @access private + */ + var $faultstring; + /** + * The fault detail, typically a string or array of string + * @var mixed + * @access private + */ + var $faultdetail; + + /** + * constructor + * + * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server) + * @param string $faultactor only used when msg routed between multiple actors + * @param string $faultstring human readable error message + * @param mixed $faultdetail detail, typically a string or array of string + */ + function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){ + parent::nusoap_base(); + $this->faultcode = $faultcode; + $this->faultactor = $faultactor; + $this->faultstring = $faultstring; + $this->faultdetail = $faultdetail; + } + + /** + * serialize a fault + * + * @return string The serialization of the fault instance. + * @access public + */ + function serialize(){ + $ns_string = ''; + foreach($this->namespaces as $k => $v){ + $ns_string .= "\n xmlns:$k=\"$v\""; + } + $return_msg = + 'soap_defencoding.'"?>'. + '\n". + ''. + ''. + $this->serialize_val($this->faultcode, 'faultcode'). + $this->serialize_val($this->faultactor, 'faultactor'). + $this->serialize_val($this->faultstring, 'faultstring'). + $this->serialize_val($this->faultdetail, 'detail'). + ''. + ''. + ''; + return $return_msg; + } +} + +/** + * Backward compatibility + */ +class soap_fault extends nusoap_fault { +} + + +?> \ No newline at end of file diff --git a/classes/lib/class.soap_parser.php b/classes/lib/class.soap_parser.php new file mode 100644 index 0000000..f6932aa --- /dev/null +++ b/classes/lib/class.soap_parser.php @@ -0,0 +1,643 @@ + +* @author Scott Nichol +* @version $Id: class.soap_parser.php,v 1.42 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_parser extends nusoap_base { + + var $xml = ''; + var $xml_encoding = ''; + var $method = ''; + var $root_struct = ''; + var $root_struct_name = ''; + var $root_struct_namespace = ''; + var $root_header = ''; + var $document = ''; // incoming SOAP body (text) + // determines where in the message we are (envelope,header,body,method) + var $status = ''; + var $position = 0; + var $depth = 0; + var $default_namespace = ''; + var $namespaces = array(); + var $message = array(); + var $parent = ''; + var $fault = false; + var $fault_code = ''; + var $fault_str = ''; + var $fault_detail = ''; + var $depth_array = array(); + var $debug_flag = true; + var $soapresponse = NULL; // parsed SOAP Body + var $soapheader = NULL; // parsed SOAP Header + var $responseHeaders = ''; // incoming SOAP headers (text) + var $body_position = 0; + // for multiref parsing: + // array of id => pos + var $ids = array(); + // array of id => hrefs => pos + var $multirefs = array(); + // toggle for auto-decoding element content + var $decode_utf8 = true; + + /** + * constructor that actually does the parsing + * + * @param string $xml SOAP message + * @param string $encoding character encoding scheme of message + * @param string $method method for which XML is parsed (unused?) + * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1 + * @access public + */ + function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){ + parent::nusoap_base(); + $this->xml = $xml; + $this->xml_encoding = $encoding; + $this->method = $method; + $this->decode_utf8 = $decode_utf8; + + // Check whether content has been read. + if(!empty($xml)){ + // Check XML encoding + $pos_xml = strpos($xml, '', $pos_xml + 2) - $pos_xml + 1); + if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) { + $xml_encoding = $res[1]; + if (strtoupper($xml_encoding) != $encoding) { + $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'"; + $this->debug($err); + if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') { + $this->setError($err); + return; + } + // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed + } else { + $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration'); + } + } else { + $this->debug('No encoding specified in XML declaration'); + } + } else { + $this->debug('No XML declaration'); + } + $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding); + // Create an XML parser - why not xml_parser_create_ns? + $this->parser = xml_parser_create($this->xml_encoding); + // Set the options for parsing the XML data. + //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element','end_element'); + xml_set_character_data_handler($this->parser,'character_data'); + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $err = sprintf('XML error parsing SOAP payload on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser))); + $this->debug($err); + $this->debug("XML payload:\n" . $xml); + $this->setError($err); + } else { + $this->debug('in nusoap_parser ctor, message:'); + $this->appendDebug($this->varDump($this->message)); + $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); + // get final value + $this->soapresponse = $this->message[$this->root_struct]['result']; + // get header value + if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){ + $this->soapheader = $this->message[$this->root_header]['result']; + } + // resolve hrefs/ids + if(sizeof($this->multirefs) > 0){ + foreach($this->multirefs as $id => $hrefs){ + $this->debug('resolving multirefs for id: '.$id); + $idVal = $this->buildVal($this->ids[$id]); + if (is_array($idVal) && isset($idVal['!id'])) { + unset($idVal['!id']); + } + foreach($hrefs as $refPos => $ref){ + $this->debug('resolving href at pos '.$refPos); + $this->multirefs[$id][$refPos] = $idVal; + } + } + } + } + xml_parser_free($this->parser); + } else { + $this->debug('xml was empty, didn\'t parse!'); + $this->setError('xml was empty, didn\'t parse!'); + } + } + + /** + * start-element handler + * + * @param resource $parser XML parser object + * @param string $name element name + * @param array $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) { + // position in a total number of elements, starting from 0 + // update class level pos + $pos = $this->position++; + // and set mine + $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>''); + // depth = how many levels removed from root? + // set mine as current global depth and increment global depth value + $this->message[$pos]['depth'] = $this->depth++; + + // else add self as child to whoever the current parent is + if($pos != 0){ + $this->message[$this->parent]['children'] .= '|'.$pos; + } + // set my parent + $this->message[$pos]['parent'] = $this->parent; + // set self as current parent + $this->parent = $pos; + // set self as current value for this depth + $this->depth_array[$this->depth] = $pos; + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + // set status + if ($name == 'Envelope' && $this->status == '') { + $this->status = 'envelope'; + } elseif ($name == 'Header' && $this->status == 'envelope') { + $this->root_header = $pos; + $this->status = 'header'; + } elseif ($name == 'Body' && $this->status == 'envelope'){ + $this->status = 'body'; + $this->body_position = $pos; + // set method + } elseif($this->status == 'body' && $pos == ($this->body_position+1)) { + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->message[$pos]['type'] = 'struct'; + $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); + } + // set my status + $this->message[$pos]['status'] = $this->status; + // set name + $this->message[$pos]['name'] = htmlspecialchars($name); + // set attrs + $this->message[$pos]['attrs'] = $attrs; + + // loop through atts, logging ns and type declarations + $attstr = ''; + foreach($attrs as $key => $value){ + $key_prefix = $this->getPrefix($key); + $key_localpart = $this->getLocalPart($key); + // if ns declarations, add to class level array of valid namespaces + if($key_prefix == 'xmlns'){ + if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){ + $this->XMLSchemaVersion = $value; + $this->namespaces['xsd'] = $this->XMLSchemaVersion; + $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; + } + $this->namespaces[$key_localpart] = $value; + // set method namespace + if($name == $this->root_struct_name){ + $this->methodNamespace = $value; + } + // if it's a type declaration, set type + } elseif($key_localpart == 'type'){ + if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') { + // do nothing: already processed arrayType + } else { + $value_prefix = $this->getPrefix($value); + $value_localpart = $this->getLocalPart($value); + $this->message[$pos]['type'] = $value_localpart; + $this->message[$pos]['typePrefix'] = $value_prefix; + if(isset($this->namespaces[$value_prefix])){ + $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; + } else if(isset($attrs['xmlns:'.$value_prefix])) { + $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; + } + // should do something here with the namespace of specified type? + } + } elseif($key_localpart == 'arrayType'){ + $this->message[$pos]['type'] = 'array'; + /* do arrayType ereg here + [1] arrayTypeValue ::= atype asize + [2] atype ::= QName rank* + [3] rank ::= '[' (',')* ']' + [4] asize ::= '[' length~ ']' + [5] length ::= nextDimension* Digit+ + [6] nextDimension ::= Digit+ ',' + */ + $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/'; + if(preg_match($expr,$value,$regs)){ + $this->message[$pos]['typePrefix'] = $regs[1]; + $this->message[$pos]['arrayTypePrefix'] = $regs[1]; + if (isset($this->namespaces[$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]]; + } else if (isset($attrs['xmlns:'.$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]]; + } + $this->message[$pos]['arrayType'] = $regs[2]; + $this->message[$pos]['arraySize'] = $regs[3]; + $this->message[$pos]['arrayCols'] = $regs[4]; + } + // specifies nil value (or not) + } elseif ($key_localpart == 'nil'){ + $this->message[$pos]['nil'] = ($value == 'true' || $value == '1'); + // some other attribute + } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') { + $this->message[$pos]['xattrs']['!' . $key] = $value; + } + + if ($key == 'xmlns') { + $this->default_namespace = $value; + } + // log id + if($key == 'id'){ + $this->ids[$value] = $pos; + } + // root + if($key_localpart == 'root' && $value == 1){ + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->debug("found root struct $this->root_struct_name, pos $pos"); + } + // for doclit + $attstr .= " $key=\"$value\""; + } + // get namespace - must be done after namespace atts are processed + if(isset($prefix)){ + $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; + $this->default_namespace = $this->namespaces[$prefix]; + } else { + $this->message[$pos]['namespace'] = $this->default_namespace; + } + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } elseif($this->root_struct_name != ''){ + $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } + + /** + * end-element handler + * + * @param resource $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name) { + // position of current element is equal to the last value left in depth_array for my depth + $pos = $this->depth_array[$this->depth--]; + + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + + // build to native type + if(isset($this->body_position) && $pos > $this->body_position){ + // deal w/ multirefs + if(isset($this->message[$pos]['attrs']['href'])){ + // get id + $id = substr($this->message[$pos]['attrs']['href'],1); + // add placeholder to href array + $this->multirefs[$id][$pos] = 'placeholder'; + // add set a reference to it as the result value + $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; + // build complexType values + } elseif($this->message[$pos]['children'] != ''){ + // if result has already been generated (struct/array) + if(!isset($this->message[$pos]['result'])){ + $this->message[$pos]['result'] = $this->buildVal($pos); + } + // build complexType values of attributes and possibly simpleContent + } elseif (isset($this->message[$pos]['xattrs'])) { + if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { + $this->message[$pos]['xattrs']['!'] = null; + } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { + if (isset($this->message[$pos]['type'])) { + $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata']; + } + } + } + $this->message[$pos]['result'] = $this->message[$pos]['xattrs']; + // set value of simpleType (or nil complexType) + } else { + //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); + if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { + $this->message[$pos]['xattrs']['!'] = null; + } elseif (isset($this->message[$pos]['type'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $this->message[$pos]['result'] = $this->message[$pos]['cdata']; + } + } + + /* add value to parent's result, if parent is struct/array + $parent = $this->message[$pos]['parent']; + if($this->message[$parent]['type'] != 'map'){ + if(strtolower($this->message[$parent]['type']) == 'array'){ + $this->message[$parent]['result'][] = $this->message[$pos]['result']; + } else { + $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result']; + } + } + */ + } + } + + // for doclit + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= ""; + } + } elseif($pos >= $this->root_struct){ + $this->document .= ""; + } + // switch status + if ($pos == $this->root_struct){ + $this->status = 'body'; + $this->root_struct_namespace = $this->message[$pos]['namespace']; + } elseif ($pos == $this->root_header) { + $this->status = 'envelope'; + } elseif ($name == 'Body' && $this->status == 'body') { + $this->status = 'envelope'; + } elseif ($name == 'Header' && $this->status == 'header') { // will never happen + $this->status = 'envelope'; + } elseif ($name == 'Envelope' && $this->status == 'envelope') { + $this->status = ''; + } + // set parent back to my parent + $this->parent = $this->message[$pos]['parent']; + } + + /** + * element content handler + * + * @param resource $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data){ + $pos = $this->depth_array[$this->depth]; + if ($this->xml_encoding=='UTF-8'){ + // TODO: add an option to disable this for folks who want + // raw UTF-8 that, e.g., might not map to iso-8859-1 + // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1"); + if($this->decode_utf8){ + $data = utf8_decode($data); + } + } + $this->message[$pos]['cdata'] .= $data; + // for doclit + if($this->status == 'header'){ + $this->responseHeaders .= $data; + } else { + $this->document .= $data; + } + } + + /** + * get the parsed message (SOAP Body) + * + * @return mixed + * @access public + * @deprecated use get_soapbody instead + */ + function get_response(){ + return $this->soapresponse; + } + + /** + * get the parsed SOAP Body (NULL if there was none) + * + * @return mixed + * @access public + */ + function get_soapbody(){ + return $this->soapresponse; + } + + /** + * get the parsed SOAP Header (NULL if there was none) + * + * @return mixed + * @access public + */ + function get_soapheader(){ + return $this->soapheader; + } + + /** + * get the unparsed SOAP Header + * + * @return string XML or empty if no Header + * @access public + */ + function getHeaders(){ + return $this->responseHeaders; + } + + /** + * decodes simple types into PHP variables + * + * @param string $value value to decode + * @param string $type XML type to decode + * @param string $typens XML type namespace to decode + * @return mixed PHP value + * @access private + */ + function decodeSimple($value, $type, $typens) { + // TODO: use the namespace! + if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') { + return (string) $value; + } + if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') { + return (int) $value; + } + if ($type == 'float' || $type == 'double' || $type == 'decimal') { + return (double) $value; + } + if ($type == 'boolean') { + if (strtolower($value) == 'false' || strtolower($value) == 'f') { + return false; + } + return (boolean) $value; + } + if ($type == 'base64' || $type == 'base64Binary') { + $this->debug('Decode base64 value'); + return base64_decode($value); + } + // obscure numeric types + if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' + || $type == 'nonNegativeInteger' || $type == 'positiveInteger' + || $type == 'unsignedInt' + || $type == 'unsignedShort' || $type == 'unsignedByte') { + return (int) $value; + } + // bogus: parser treats array with no elements as a simple type + if ($type == 'array') { + return array(); + } + // everything else + return (string) $value; + } + + /** + * builds response structures for compound values (arrays/structs) + * and scalars + * + * @param integer $pos position in node tree + * @return mixed PHP value + * @access private + */ + function buildVal($pos){ + if(!isset($this->message[$pos]['type'])){ + $this->message[$pos]['type'] = ''; + } + $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); + // if there are children... + if($this->message[$pos]['children'] != ''){ + $this->debug('in buildVal, there are children'); + $children = explode('|',$this->message[$pos]['children']); + array_shift($children); // knock off empty + // md array + if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){ + $r=0; // rowcount + $c=0; // colcount + foreach($children as $child_pos){ + $this->debug("in buildVal, got an MD array element: $r, $c"); + $params[$r][] = $this->message[$child_pos]['result']; + $c++; + if($c == $this->message[$pos]['arrayCols']){ + $c = 0; + $r++; + } + } + // array + } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){ + $this->debug('in buildVal, adding array '.$this->message[$pos]['name']); + foreach($children as $child_pos){ + $params[] = &$this->message[$child_pos]['result']; + } + // apache Map type: java hashtable + } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){ + $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']); + foreach($children as $child_pos){ + $kv = explode("|",$this->message[$child_pos]['children']); + $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; + } + // generic compound type + //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { + } else { + // Apache Vector type: treat as an array + $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']); + if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { + $notstruct = 1; + } else { + $notstruct = 0; + } + // + foreach($children as $child_pos){ + if($notstruct){ + $params[] = &$this->message[$child_pos]['result']; + } else { + if (isset($params[$this->message[$child_pos]['name']])) { + // de-serialize repeated element name into an array + if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) { + $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]); + } + $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result']; + } else { + $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; + } + } + } + } + if (isset($this->message[$pos]['xattrs'])) { + $this->debug('in buildVal, handling attributes'); + foreach ($this->message[$pos]['xattrs'] as $n => $v) { + $params[$n] = $v; + } + } + // handle simpleContent + if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { + $this->debug('in buildVal, handling simpleContent'); + if (isset($this->message[$pos]['type'])) { + $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $params['!'] = $this->message[$pos]['cdata']; + } + } + } + $ret = is_array($params) ? $params : array(); + $this->debug('in buildVal, return:'); + $this->appendDebug($this->varDump($ret)); + return $ret; + } else { + $this->debug('in buildVal, no children, building scalar'); + $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : ''; + if (isset($this->message[$pos]['type'])) { + $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + $this->debug("in buildVal, return: $ret"); + return $ret; + } + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + $this->debug("in buildVal, return: $ret"); + return $ret; + } + $ret = $this->message[$pos]['cdata']; + $this->debug("in buildVal, return: $ret"); + return $ret; + } + } +} + +/** + * Backward compatibility + */ +class soap_parser extends nusoap_parser { +} + + +?> \ No newline at end of file diff --git a/classes/lib/class.soap_server.php b/classes/lib/class.soap_server.php new file mode 100644 index 0000000..4f1321d --- /dev/null +++ b/classes/lib/class.soap_server.php @@ -0,0 +1,1127 @@ + +* @author Scott Nichol +* @version $Id: class.soap_server.php,v 1.63 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_server extends nusoap_base { + /** + * HTTP headers of request + * @var array + * @access private + */ + var $headers = array(); + /** + * HTTP request + * @var string + * @access private + */ + var $request = ''; + /** + * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) + * @var string + * @access public + */ + var $requestHeaders = ''; + /** + * SOAP Headers from request (parsed) + * @var mixed + * @access public + */ + var $requestHeader = NULL; + /** + * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) + * @var string + * @access public + */ + var $document = ''; + /** + * SOAP payload for request (text) + * @var string + * @access public + */ + var $requestSOAP = ''; + /** + * requested method namespace URI + * @var string + * @access private + */ + var $methodURI = ''; + /** + * name of method requested + * @var string + * @access private + */ + var $methodname = ''; + /** + * method parameters from request + * @var array + * @access private + */ + var $methodparams = array(); + /** + * SOAP Action from request + * @var string + * @access private + */ + var $SOAPAction = ''; + /** + * character set encoding of incoming (request) messages + * @var string + * @access public + */ + var $xml_encoding = ''; + /** + * toggles whether the parser decodes element content w/ utf8_decode() + * @var boolean + * @access public + */ + var $decode_utf8 = true; + + /** + * HTTP headers of response + * @var array + * @access public + */ + var $outgoing_headers = array(); + /** + * HTTP response + * @var string + * @access private + */ + var $response = ''; + /** + * SOAP headers for response (text or array of soapval or associative array) + * @var mixed + * @access public + */ + var $responseHeaders = ''; + /** + * SOAP payload for response (text) + * @var string + * @access private + */ + var $responseSOAP = ''; + /** + * method return value to place in response + * @var mixed + * @access private + */ + var $methodreturn = false; + /** + * whether $methodreturn is a string of literal XML + * @var boolean + * @access public + */ + var $methodreturnisliteralxml = false; + /** + * SOAP fault for response (or false) + * @var mixed + * @access private + */ + var $fault = false; + /** + * text indication of result (for debugging) + * @var string + * @access private + */ + var $result = 'successful'; + + /** + * assoc array of operations => opData; operations are added by the register() + * method or by parsing an external WSDL definition + * @var array + * @access private + */ + var $operations = array(); + /** + * wsdl instance (if one) + * @var mixed + * @access private + */ + var $wsdl = false; + /** + * URL for WSDL (if one) + * @var mixed + * @access private + */ + var $externalWSDLURL = false; + /** + * whether to append debug to response as XML comment + * @var boolean + * @access public + */ + var $debug_flag = false; + + + /** + * constructor + * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. + * + * @param mixed $wsdl file path or URL (string), or wsdl instance (object) + * @access public + */ + function nusoap_server($wsdl=false){ + parent::nusoap_base(); + // turn on debugging? + global $debug; + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $this->debug("_SERVER is defined:"); + $this->appendDebug($this->varDump($_SERVER)); + } elseif (isset($HTTP_SERVER_VARS)) { + $this->debug("HTTP_SERVER_VARS is defined:"); + $this->appendDebug($this->varDump($HTTP_SERVER_VARS)); + } else { + $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined."); + } + + if (isset($debug)) { + $this->debug("In nusoap_server, set debug_flag=$debug based on global flag"); + $this->debug_flag = $debug; + } elseif (isset($_SERVER['QUERY_STRING'])) { + $qs = explode('&', $_SERVER['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1"); + $this->debug_flag = substr($v, 6); + } + } + } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { + $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2"); + $this->debug_flag = substr($v, 6); + } + } + } + + // wsdl + if($wsdl){ + $this->debug("In nusoap_server, WSDL is specified"); + if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) { + $this->wsdl = $wsdl; + $this->externalWSDLURL = $this->wsdl->wsdl; + $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); + } else { + $this->debug('Create wsdl from ' . $wsdl); + $this->wsdl = new wsdl($wsdl); + $this->externalWSDLURL = $wsdl; + } + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if($err = $this->wsdl->getError()){ + die('WSDL ERROR: '.$err); + } + } + } + + /** + * processes request and returns response + * + * @param string $data usually is the value of $HTTP_RAW_POST_DATA + * @access public + */ + function service($data){ + global $HTTP_SERVER_VARS; + + if (isset($_SERVER['REQUEST_METHOD'])) { + $rm = $_SERVER['REQUEST_METHOD']; + } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) { + $rm = $HTTP_SERVER_VARS['REQUEST_METHOD']; + } else { + $rm = ''; + } + + if (isset($_SERVER['QUERY_STRING'])) { + $qs = $_SERVER['QUERY_STRING']; + } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { + $qs = $HTTP_SERVER_VARS['QUERY_STRING']; + } else { + $qs = ''; + } + $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data)); + + if ($rm == 'POST') { + $this->debug("In service, invoke the request"); + $this->parse_request($data); + if (! $this->fault) { + $this->invoke_method(); + } + if (! $this->fault) { + $this->serialize_return(); + } + $this->send_response(); + } elseif (preg_match('/wsdl/', $qs) ){ + $this->debug("In service, this is a request for WSDL"); + if ($this->externalWSDLURL){ + if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL + $this->debug("In service, re-direct for WSDL"); + header('Location: '.$this->externalWSDLURL); + } else { // assume file + $this->debug("In service, use file passthru for WSDL"); + header("Content-Type: text/xml\r\n"); + $pos = strpos($this->externalWSDLURL, "file://"); + if ($pos === false) { + $filename = $this->externalWSDLURL; + } else { + $filename = substr($this->externalWSDLURL, $pos + 7); + } + $fp = fopen($this->externalWSDLURL, 'r'); + fpassthru($fp); + } + } elseif ($this->wsdl) { + $this->debug("In service, serialize WSDL"); + header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); + print $this->wsdl->serialize($this->debug_flag); + if ($this->debug_flag) { + $this->debug('wsdl:'); + $this->appendDebug($this->varDump($this->wsdl)); + print $this->getDebugAsXMLComment(); + } + } else { + $this->debug("In service, there is no WSDL"); + header("Content-Type: text/html; charset=ISO-8859-1\r\n"); + print "This service does not provide WSDL"; + } + } elseif ($this->wsdl) { + $this->debug("In service, return Web description"); + print $this->wsdl->webDescription(); + } else { + $this->debug("In service, no Web description"); + header("Content-Type: text/html; charset=ISO-8859-1\r\n"); + print "This service does not provide a Web description"; + } + } + + /** + * parses HTTP request headers. + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * + * @access private + */ + function parse_http_headers() { + global $HTTP_SERVER_VARS; + + $this->request = ''; + $this->SOAPAction = ''; + if(function_exists('getallheaders')){ + $this->debug("In parse_http_headers, use getallheaders"); + $headers = getallheaders(); + foreach($headers as $k=>$v){ + $k = strtolower($k); + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + // get SOAPAction header + if(isset($this->headers['soapaction'])){ + $this->SOAPAction = str_replace('"','',$this->headers['soapaction']); + } + // get the character encoding of the incoming request + if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){ + $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1)); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } elseif(isset($_SERVER) && is_array($_SERVER)){ + $this->debug("In parse_http_headers, use _SERVER"); + foreach ($_SERVER as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); + } else { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); + } + if ($k == 'soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'content-type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } elseif (is_array($HTTP_SERVER_VARS)) { + $this->debug("In parse_http_headers, use HTTP_SERVER_VARS"); + foreach ($HTTP_SERVER_VARS as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); + } else { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); + } + if ($k == 'soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'content-type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } else { + $this->debug("In parse_http_headers, HTTP headers not accessible"); + $this->setError("HTTP headers not accessible"); + } + } + + /** + * parses a request + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * request + * requestSOAP + * methodURI + * methodname + * methodparams + * requestHeaders + * document + * + * This sets the fault field on error + * + * @param string $data XML string + * @access private + */ + function parse_request($data='') { + $this->debug('entering parse_request()'); + $this->parse_http_headers(); + $this->debug('got character encoding: '.$this->xml_encoding); + // uncompress if necessary + if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') { + $this->debug('got content encoding: ' . $this->headers['content-encoding']); + if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') { + // if decoding works, use it. else assume data wasn't gzencoded + if (function_exists('gzuncompress')) { + if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { + $data = $degzdata; + } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { + $data = $degzdata; + } else { + $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data'); + return; + } + } else { + $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data'); + return; + } + } + } + $this->request .= "\r\n".$data; + $data = $this->parseRequest($this->headers, $data); + $this->requestSOAP = $data; + $this->debug('leaving parse_request'); + } + + /** + * invokes a PHP function for the requested SOAP method + * + * The following fields are set by this function (when successful) + * + * methodreturn + * + * Note that the PHP function that is called may also set the following + * fields to affect the response sent to the client + * + * responseHeaders + * outgoing_headers + * + * This sets the fault field on error + * + * @access private + */ + function invoke_method() { + $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction); + + // + // if you are debugging in this area of the code, your service uses a class to implement methods, + // you use SOAP RPC, and the client is .NET, please be aware of the following... + // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the + // method name. that is fine for naming the .NET methods. it is not fine for properly constructing + // the XML request and reading the XML response. you need to add the RequestElementName and + // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe + // generates for the method. these parameters are used to specify the correct XML element names + // for .NET to use, i.e. the names with the '.' in them. + // + $orig_methodname = $this->methodname; + if ($this->wsdl) { + if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { + $this->debug('in invoke_method, found WSDL operation=' . $this->methodname); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { + // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element + $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + $this->methodname = $this->opData['name']; + } else { + $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname); + $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service"); + return; + } + } else { + $this->debug('in invoke_method, no WSDL to validate method'); + } + + // if a . is present in $this->methodname, we see if there is a class in scope, + // which could be referred to. We will also distinguish between two deliminators, + // to allow methods to be called a the class or an instance + if (strpos($this->methodname, '..') > 0) { + $delim = '..'; + } else if (strpos($this->methodname, '.') > 0) { + $delim = '.'; + } else { + $delim = ''; + } + $this->debug("in invoke_method, delim=$delim"); + + $class = ''; + $method = ''; + if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) { + $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim)); + if (class_exists($try_class)) { + // get the class and method name + $class = $try_class; + $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim)); + $this->debug("in invoke_method, class=$class method=$method delim=$delim"); + } else { + $this->debug("in invoke_method, class=$try_class not found"); + } + } else { + $try_class = ''; + $this->debug("in invoke_method, no class to try"); + } + + // does method exist? + if ($class == '') { + if (!function_exists($this->methodname)) { + $this->debug("in invoke_method, function '$this->methodname' not found!"); + $this->result = 'fault: method not found'; + $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')"); + return; + } + } else { + $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method; + if (!in_array($method_to_compare, get_class_methods($class))) { + $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!"); + $this->result = 'fault: method not found'; + $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')"); + return; + } + } + + // evaluate message, getting back parameters + // verify that request parameters match the method's signature + if(! $this->verify_method($this->methodname,$this->methodparams)){ + // debug + $this->debug('ERROR: request not verified against method signature'); + $this->result = 'fault: request failed validation against method signature'; + // return fault + $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service."); + return; + } + + // if there are parameters to pass + $this->debug('in invoke_method, params:'); + $this->appendDebug($this->varDump($this->methodparams)); + $this->debug("in invoke_method, calling '$this->methodname'"); + if (!function_exists('call_user_func_array')) { + if ($class == '') { + $this->debug('in invoke_method, calling function using eval()'); + $funcCall = "\$this->methodreturn = $this->methodname("; + } else { + if ($delim == '..') { + $this->debug('in invoke_method, calling class method using eval()'); + $funcCall = "\$this->methodreturn = ".$class."::".$method."("; + } else { + $this->debug('in invoke_method, calling instance method using eval()'); + // generate unique instance name + $instname = "\$inst_".time(); + $funcCall = $instname." = new ".$class."(); "; + $funcCall .= "\$this->methodreturn = ".$instname."->".$method."("; + } + } + if ($this->methodparams) { + foreach ($this->methodparams as $param) { + if (is_array($param) || is_object($param)) { + $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available'); + return; + } + $funcCall .= "\"$param\","; + } + $funcCall = substr($funcCall, 0, -1); + } + $funcCall .= ');'; + $this->debug('in invoke_method, function call: '.$funcCall); + @eval($funcCall); + } else { + if ($class == '') { + $this->debug('in invoke_method, calling function using call_user_func_array()'); + $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array() + } elseif ($delim == '..') { + $this->debug('in invoke_method, calling class method using call_user_func_array()'); + $call_arg = array ($class, $method); + } else { + $this->debug('in invoke_method, calling instance method using call_user_func_array()'); + $instance = new $class (); + $call_arg = array(&$instance, $method); + } + if (is_array($this->methodparams)) { + $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams)); + } else { + $this->methodreturn = call_user_func_array($call_arg, array()); + } + } + $this->debug('in invoke_method, methodreturn:'); + $this->appendDebug($this->varDump($this->methodreturn)); + $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn)); + } + + /** + * serializes the return value from a PHP function into a full SOAP Envelope + * + * The following fields are set by this function (when successful) + * + * responseSOAP + * + * This sets the fault field on error + * + * @access private + */ + function serialize_return() { + $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI); + // if fault + if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) { + $this->debug('got a fault object from method'); + $this->fault = $this->methodreturn; + return; + } elseif ($this->methodreturnisliteralxml) { + $return_val = $this->methodreturn; + // returned value(s) + } else { + $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); + $this->debug('serializing return value'); + if($this->wsdl){ + if (sizeof($this->opData['output']['parts']) > 1) { + $this->debug('more than one output part, so use the method return unchanged'); + $opParams = $this->methodreturn; + } elseif (sizeof($this->opData['output']['parts']) == 1) { + $this->debug('exactly one output part, so wrap the method return in a simple array'); + // TODO: verify that it is not already wrapped! + //foreach ($this->opData['output']['parts'] as $name => $type) { + // $this->debug('wrap in element named ' . $name); + //} + $opParams = array($this->methodreturn); + } + $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams); + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if($errstr = $this->wsdl->getError()){ + $this->debug('got wsdl error: '.$errstr); + $this->fault('SOAP-ENV:Server', 'unable to serialize result'); + return; + } + } else { + if (isset($this->methodreturn)) { + $return_val = $this->serialize_val($this->methodreturn, 'return'); + } else { + $return_val = ''; + $this->debug('in absence of WSDL, assume void return for backward compatibility'); + } + } + } + $this->debug('return value:'); + $this->appendDebug($this->varDump($return_val)); + + $this->debug('serializing response'); + if ($this->wsdl) { + $this->debug('have WSDL for serialization: style is ' . $this->opData['style']); + if ($this->opData['style'] == 'rpc') { + $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']); + if ($this->opData['output']['use'] == 'literal') { + // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace + if ($this->methodURI) { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } else { + $payload = '<'.$this->methodname.'Response>'.$return_val.'methodname.'Response>'; + } + } else { + if ($this->methodURI) { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } else { + $payload = '<'.$this->methodname.'Response>'.$return_val.'methodname.'Response>'; + } + } + } else { + $this->debug('style is not rpc for serialization: assume document'); + $payload = $return_val; + } + } else { + $this->debug('do not have WSDL for serialization: assume rpc/encoded'); + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } + $this->result = 'successful'; + if($this->wsdl){ + //if($this->debug_flag){ + $this->appendDebug($this->wsdl->getDebug()); + // } + if (isset($this->opData['output']['encodingStyle'])) { + $encodingStyle = $this->opData['output']['encodingStyle']; + } else { + $encodingStyle = ''; + } + // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle); + } else { + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders); + } + $this->debug("Leaving serialize_return"); + } + + /** + * sends an HTTP response + * + * The following fields are set by this function (when successful) + * + * outgoing_headers + * response + * + * @access private + */ + function send_response() { + $this->debug('Enter send_response'); + if ($this->fault) { + $payload = $this->fault->serialize(); + $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; + $this->outgoing_headers[] = "Status: 500 Internal Server Error"; + } else { + $payload = $this->responseSOAP; + // Some combinations of PHP+Web server allow the Status + // to come through as a header. Since OK is the default + // just do nothing. + // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; + // $this->outgoing_headers[] = "Status: 200 OK"; + } + // add debug data if in debug mode + if(isset($this->debug_flag) && $this->debug_flag){ + $payload .= $this->getDebugAsXMLComment(); + } + $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; + preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); + $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; + // Let the Web server decide about this + //$this->outgoing_headers[] = "Connection: Close\r\n"; + $payload = $this->getHTTPBody($payload); + $type = $this->getHTTPContentType(); + $charset = $this->getHTTPContentTypeCharset(); + $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : ''); + //begin code to compress payload - by John + // NOTE: there is no way to know whether the Web server will also compress + // this data. + if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) { + if (strstr($this->headers['accept-encoding'], 'gzip')) { + if (function_exists('gzencode')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: gzip"; + $payload = gzencode($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } elseif (strstr($this->headers['accept-encoding'], 'deflate')) { + // Note: MSIE requires gzdeflate output (no Zlib header and checksum), + // instead of gzcompress output, + // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5) + if (function_exists('gzdeflate')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: deflate"; + $payload = gzdeflate($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } + } + //end code + $this->outgoing_headers[] = "Content-Length: ".strlen($payload); + reset($this->outgoing_headers); + foreach($this->outgoing_headers as $hdr){ + header($hdr, false); + } + print $payload; + $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload; + } + + /** + * takes the value that was created by parsing the request + * and compares to the method's signature, if available. + * + * @param string $operation The operation to be invoked + * @param array $request The array of parameter values + * @return boolean Whether the operation was found + * @access private + */ + function verify_method($operation,$request){ + if(isset($this->wsdl) && is_object($this->wsdl)){ + if($this->wsdl->getOperationData($operation)){ + return true; + } + } elseif(isset($this->operations[$operation])){ + return true; + } + return false; + } + + /** + * processes SOAP message received from client + * + * @param array $headers The HTTP headers + * @param string $data unprocessed request data from client + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseRequest($headers, $data) { + $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:'); + $this->appendDebug($this->varDump($headers)); + if (!isset($headers['content-type'])) { + $this->setError('Request not of type text/xml (no content-type header)'); + return false; + } + if (!strstr($headers['content-type'], 'text/xml')) { + $this->setError('Request not of type text/xml'); + return false; + } + if (strpos($headers['content-type'], '=')) { + $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); + $this->debug('Got response encoding: ' . $enc); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); + // parse response, get soap parser obj + $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8); + // parser debug + $this->debug("parser debug: \n".$parser->getDebug()); + // if fault occurred during message parsing + if($err = $parser->getError()){ + $this->result = 'fault: error in msg parsing: '.$err; + $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err); + // else successfully parsed request into soapval object + } else { + // get/set methodname + $this->methodURI = $parser->root_struct_namespace; + $this->methodname = $parser->root_struct_name; + $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI); + $this->debug('calling parser->get_soapbody()'); + $this->methodparams = $parser->get_soapbody(); + // get SOAP headers + $this->requestHeaders = $parser->getHeaders(); + // get SOAP Header + $this->requestHeader = $parser->get_soapheader(); + // add document for doclit support + $this->document = $parser->document; + } + } + + /** + * gets the HTTP body for the current response. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + return $soapmsg; + } + + /** + * gets the HTTP content type for the current response. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current response. + * @access private + */ + function getHTTPContentType() { + return 'text/xml'; + } + + /** + * gets the HTTP content type charset for the current response. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current response. + * @access private + */ + function getHTTPContentTypeCharset() { + return $this->soap_defencoding; + } + + /** + * add a method to the dispatch map (this has been replaced by the register method) + * + * @param string $methodname + * @param string $in array of input values + * @param string $out array of output values + * @access public + * @deprecated + */ + function add_to_map($methodname,$in,$out){ + $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out); + } + + /** + * register a service function with the server + * + * @param string $name the name of the PHP function, class.method or class..method + * @param array $in assoc array of input values: key = param name, value = param type + * @param array $out assoc array of output values: key = param name, value = param type + * @param mixed $namespace the element namespace for the method or false + * @param mixed $soapaction the soapaction for the method or false + * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically + * @param mixed $use optional (encoded|literal) or false + * @param string $documentation optional Description to include in WSDL + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @access public + */ + function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){ + global $HTTP_SERVER_VARS; + + if($this->externalWSDLURL){ + die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); + } + if (! $name) { + die('You must specify a name when you register an operation'); + } + if (!is_array($in)) { + die('You must provide an array for operation inputs'); + } + if (!is_array($out)) { + die('You must provide an array for operation outputs'); + } + if(false == $namespace) { + } + if(false == $soapaction) { + if (isset($_SERVER)) { + $SERVER_NAME = $_SERVER['SERVER_NAME']; + $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; + $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); + } elseif (isset($HTTP_SERVER_VARS)) { + $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; + $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; + $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + if ($HTTPS == '1' || $HTTPS == 'on') { + $SCHEME = 'https'; + } else { + $SCHEME = 'http'; + } + $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name"; + } + if(false == $style) { + $style = "rpc"; + } + if(false == $use) { + $use = "encoded"; + } + if ($use == 'encoded' && $encodingStyle == '') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } + + $this->operations[$name] = array( + 'name' => $name, + 'in' => $in, + 'out' => $out, + 'namespace' => $namespace, + 'soapaction' => $soapaction, + 'style' => $style); + if($this->wsdl){ + $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle); + } + return true; + } + + /** + * Specify a fault to be returned to the client. + * This also acts as a flag to the server that a fault has occured. + * + * @param string $faultcode + * @param string $faultstring + * @param string $faultactor + * @param string $faultdetail + * @access public + */ + function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){ + if ($faultdetail == '' && $this->debug_flag) { + $faultdetail = $this->getDebug(); + } + $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail); + $this->fault->soap_defencoding = $this->soap_defencoding; + } + + /** + * Sets up wsdl object. + * Acts as a flag to enable internal WSDL generation + * + * @param string $serviceName, name of the service + * @param mixed $namespace optional 'tns' service namespace or false + * @param mixed $endpoint optional URL of service endpoint or false + * @param string $style optional (rpc|document) WSDL style (also specified by operation) + * @param string $transport optional SOAP transport + * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false + */ + function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) + { + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $SERVER_NAME = $_SERVER['SERVER_NAME']; + $SERVER_PORT = $_SERVER['SERVER_PORT']; + $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; + $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); + } elseif (isset($HTTP_SERVER_VARS)) { + $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; + $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT']; + $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; + $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI) + $colon = strpos($SERVER_NAME,":"); + if ($colon) { + $SERVER_NAME = substr($SERVER_NAME, 0, $colon); + } + if ($SERVER_PORT == 80) { + $SERVER_PORT = ''; + } else { + $SERVER_PORT = ':' . $SERVER_PORT; + } + if(false == $namespace) { + $namespace = "http://$SERVER_NAME/soap/$serviceName"; + } + + if(false == $endpoint) { + if ($HTTPS == '1' || $HTTPS == 'on') { + $SCHEME = 'https'; + } else { + $SCHEME = 'http'; + } + $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME"; + } + + if(false == $schemaTargetNamespace) { + $schemaTargetNamespace = $namespace; + } + + $this->wsdl = new wsdl; + $this->wsdl->serviceName = $serviceName; + $this->wsdl->endpoint = $endpoint; + $this->wsdl->namespaces['tns'] = $namespace; + $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; + $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; + if ($schemaTargetNamespace != $namespace) { + $this->wsdl->namespaces['types'] = $schemaTargetNamespace; + } + $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces); + if ($style == 'document') { + $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified'; + } + $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace; + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->bindings[$serviceName.'Binding'] = array( + 'name'=>$serviceName.'Binding', + 'style'=>$style, + 'transport'=>$transport, + 'portType'=>$serviceName.'PortType'); + $this->wsdl->ports[$serviceName.'Port'] = array( + 'binding'=>$serviceName.'Binding', + 'location'=>$endpoint, + 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/'); + } +} + +/** + * Backward compatibility + */ +class soap_server extends nusoap_server { +} + + +?> \ No newline at end of file diff --git a/classes/lib/class.soap_transport_http.php b/classes/lib/class.soap_transport_http.php new file mode 100644 index 0000000..4698209 --- /dev/null +++ b/classes/lib/class.soap_transport_http.php @@ -0,0 +1,1307 @@ + +* @author Scott Nichol +* @version $Id: class.soap_transport_http.php,v 1.68 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class soap_transport_http extends nusoap_base { + + var $url = ''; + var $uri = ''; + var $digest_uri = ''; + var $scheme = ''; + var $host = ''; + var $port = ''; + var $path = ''; + var $request_method = 'POST'; + var $protocol_version = '1.0'; + var $encoding = ''; + var $outgoing_headers = array(); + var $incoming_headers = array(); + var $incoming_cookies = array(); + var $outgoing_payload = ''; + var $incoming_payload = ''; + var $response_status_line; // HTTP response status line + var $useSOAPAction = true; + var $persistentConnection = false; + var $ch = false; // cURL handle + var $ch_options = array(); // cURL custom options + var $use_curl = false; // force cURL use + var $proxy = null; // proxy information (associative array) + var $username = ''; + var $password = ''; + var $authtype = ''; + var $digestRequest = array(); + var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional) + // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem' + // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem' + // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem' + // passphrase: SSL key password/passphrase + // certpassword: SSL certificate password + // verifypeer: default is 1 + // verifyhost: default is 1 + + /** + * constructor + * + * @param string $url The URL to which to connect + * @param array $curl_options User-specified cURL options + * @param boolean $use_curl Whether to try to force cURL use + * @access public + */ + function soap_transport_http($url, $curl_options = NULL, $use_curl = false){ + parent::nusoap_base(); + $this->debug("ctor url=$url use_curl=$use_curl curl_options:"); + $this->appendDebug($this->varDump($curl_options)); + $this->setURL($url); + if (is_array($curl_options)) { + $this->ch_options = $curl_options; + } + $this->use_curl = $use_curl; + preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); + $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')'); + } + + /** + * sets a cURL option + * + * @param mixed $option The cURL option (always integer?) + * @param mixed $value The cURL option value + * @access private + */ + function setCurlOption($option, $value) { + $this->debug("setCurlOption option=$option, value="); + $this->appendDebug($this->varDump($value)); + curl_setopt($this->ch, $option, $value); + } + + /** + * sets an HTTP header + * + * @param string $name The name of the header + * @param string $value The value of the header + * @access private + */ + function setHeader($name, $value) { + $this->outgoing_headers[$name] = $value; + $this->debug("set header $name: $value"); + } + + /** + * unsets an HTTP header + * + * @param string $name The name of the header + * @access private + */ + function unsetHeader($name) { + if (isset($this->outgoing_headers[$name])) { + $this->debug("unset header $name"); + unset($this->outgoing_headers[$name]); + } + } + + /** + * sets the URL to which to connect + * + * @param string $url The URL to which to connect + * @access private + */ + function setURL($url) { + $this->url = $url; + + $u = parse_url($url); + foreach($u as $k => $v){ + $this->debug("parsed URL $k = $v"); + $this->$k = $v; + } + + // add any GET params to path + if(isset($u['query']) && $u['query'] != ''){ + $this->path .= '?' . $u['query']; + } + + // set default port + if(!isset($u['port'])){ + if($u['scheme'] == 'https'){ + $this->port = 443; + } else { + $this->port = 80; + } + } + + $this->uri = $this->path; + $this->digest_uri = $this->uri; + + // build headers + if (!isset($u['port'])) { + $this->setHeader('Host', $this->host); + } else { + $this->setHeader('Host', $this->host.':'.$this->port); + } + + if (isset($u['user']) && $u['user'] != '') { + $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : ''); + } + } + + /** + * gets the I/O method to use + * + * @return string I/O method to use (socket|curl|unknown) + * @access private + */ + function io_method() { + if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) + return 'curl'; + if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) + return 'socket'; + return 'unknown'; + } + + /** + * establish an HTTP connection + * + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return boolean true if connected, false if not + * @access private + */ + function connect($connection_timeout=0,$response_timeout=30){ + // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like + // "regular" socket. + // TODO: disabled for now because OpenSSL must be *compiled* in (not just + // loaded), and until PHP5 stream_get_wrappers is not available. +// if ($this->scheme == 'https') { +// if (version_compare(phpversion(), '4.3.0') >= 0) { +// if (extension_loaded('openssl')) { +// $this->scheme = 'ssl'; +// $this->debug('Using SSL over OpenSSL'); +// } +// } +// } + $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port"); + if ($this->io_method() == 'socket') { + if (!is_array($this->proxy)) { + $host = $this->host; + $port = $this->port; + } else { + $host = $this->proxy['host']; + $port = $this->proxy['port']; + } + + // use persistent connection + if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){ + if (!feof($this->fp)) { + $this->debug('Re-use persistent connection'); + return true; + } + fclose($this->fp); + $this->debug('Closed persistent connection at EOF'); + } + + // munge host if using OpenSSL + if ($this->scheme == 'ssl') { + $host = 'ssl://' . $host; + } + $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout); + + // open socket + if($connection_timeout > 0){ + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); + } else { + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); + } + + // test pointer + if(!$this->fp) { + $msg = 'Couldn\'t open socket connection to server ' . $this->url; + if ($this->errno) { + $msg .= ', Error ('.$this->errno.'): '.$this->error_str; + } else { + $msg .= ' prior to connect(). This is often a problem looking up the host name.'; + } + $this->debug($msg); + $this->setError($msg); + return false; + } + + // set response timeout + $this->debug('set response timeout to ' . $response_timeout); + socket_set_timeout( $this->fp, $response_timeout); + + $this->debug('socket connected'); + return true; + } else if ($this->io_method() == 'curl') { + if (!extension_loaded('curl')) { +// $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); + $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.'); + return false; + } + // Avoid warnings when PHP does not have these options + if (defined('CURLOPT_CONNECTIONTIMEOUT')) + $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT; + else + $CURLOPT_CONNECTIONTIMEOUT = 78; + if (defined('CURLOPT_HTTPAUTH')) + $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH; + else + $CURLOPT_HTTPAUTH = 107; + if (defined('CURLOPT_PROXYAUTH')) + $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH; + else + $CURLOPT_PROXYAUTH = 111; + if (defined('CURLAUTH_BASIC')) + $CURLAUTH_BASIC = CURLAUTH_BASIC; + else + $CURLAUTH_BASIC = 1; + if (defined('CURLAUTH_DIGEST')) + $CURLAUTH_DIGEST = CURLAUTH_DIGEST; + else + $CURLAUTH_DIGEST = 2; + if (defined('CURLAUTH_NTLM')) + $CURLAUTH_NTLM = CURLAUTH_NTLM; + else + $CURLAUTH_NTLM = 8; + + $this->debug('connect using cURL'); + // init CURL + $this->ch = curl_init(); + // set url + $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host"; + // add path + $hostURL .= $this->path; + $this->setCurlOption(CURLOPT_URL, $hostURL); + // follow location headers (re-directs) + if (ini_get('safe_mode') || ini_get('open_basedir')) { + $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION'); + $this->debug('safe_mode = '); + $this->appendDebug($this->varDump(ini_get('safe_mode'))); + $this->debug('open_basedir = '); + $this->appendDebug($this->varDump(ini_get('open_basedir'))); + } else { + $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1); + } + // ask for headers in the response output + $this->setCurlOption(CURLOPT_HEADER, 1); + // ask for the response output as the return value + $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1); + // encode + // We manage this ourselves through headers and encoding +// if(function_exists('gzuncompress')){ +// $this->setCurlOption(CURLOPT_ENCODING, 'deflate'); +// } + // persistent connection + if ($this->persistentConnection) { + // I believe the following comment is now bogus, having applied to + // the code when it used CURLOPT_CUSTOMREQUEST to send the request. + // The way we send data, we cannot use persistent connections, since + // there will be some "junk" at the end of our request. + //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true); + $this->persistentConnection = false; + $this->setHeader('Connection', 'close'); + } + // set timeouts + if ($connection_timeout != 0) { + $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout); + } + if ($response_timeout != 0) { + $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout); + } + + if ($this->scheme == 'https') { + $this->debug('set cURL SSL verify options'); + // recent versions of cURL turn on peer/host checking by default, + // while PHP binaries are not compiled with a default location for the + // CA cert bundle, so disable peer/host checking. + //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt'); + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0); + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0); + + // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo) + if ($this->authtype == 'certificate') { + $this->debug('set cURL certificate options'); + if (isset($this->certRequest['cainfofile'])) { + $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']); + } + if (isset($this->certRequest['verifypeer'])) { + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']); + } else { + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1); + } + if (isset($this->certRequest['verifyhost'])) { + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']); + } else { + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1); + } + if (isset($this->certRequest['sslcertfile'])) { + $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']); + } + if (isset($this->certRequest['sslkeyfile'])) { + $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']); + } + if (isset($this->certRequest['passphrase'])) { + $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']); + } + if (isset($this->certRequest['certpassword'])) { + $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']); + } + } + } + if ($this->authtype && ($this->authtype != 'certificate')) { + if ($this->username) { + $this->debug('set cURL username/password'); + $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password"); + } + if ($this->authtype == 'basic') { + $this->debug('set cURL for Basic authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC); + } + if ($this->authtype == 'digest') { + $this->debug('set cURL for digest authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST); + } + if ($this->authtype == 'ntlm') { + $this->debug('set cURL for NTLM authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM); + } + } + if (is_array($this->proxy)) { + $this->debug('set cURL proxy options'); + if ($this->proxy['port'] != '') { + $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']); + } else { + $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']); + } + if ($this->proxy['username'] || $this->proxy['password']) { + $this->debug('set cURL proxy authentication options'); + $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']); + if ($this->proxy['authtype'] == 'basic') { + $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC); + } + if ($this->proxy['authtype'] == 'ntlm') { + $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM); + } + } + } + $this->debug('cURL connection set up'); + return true; + } else { + $this->setError('Unknown scheme ' . $this->scheme); + $this->debug('Unknown scheme ' . $this->scheme); + return false; + } + } + + /** + * sends the SOAP request and gets the SOAP response via HTTP[S] + * + * @param string $data message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @param array $cookies cookies to send + * @return string data + * @access public + */ + function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) { + + $this->debug('entered send() with data of length: '.strlen($data)); + + $this->tryagain = true; + $tries = 0; + while ($this->tryagain) { + $this->tryagain = false; + if ($tries++ < 2) { + // make connnection + if (!$this->connect($timeout, $response_timeout)){ + return false; + } + + // send request + if (!$this->sendRequest($data, $cookies)){ + return false; + } + + // get response + $respdata = $this->getResponse(); + } else { + $this->setError("Too many tries to get an OK response ($this->response_status_line)"); + } + } + $this->debug('end of send()'); + return $respdata; + } + + + /** + * sends the SOAP request and gets the SOAP response via HTTPS using CURL + * + * @param string $data message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @param array $cookies cookies to send + * @return string data + * @access public + * @deprecated + */ + function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) { + return $this->send($data, $timeout, $response_timeout, $cookies); + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $digestRequest (keys must be nonce, nc, realm, qop) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype digestRequest="); + $this->appendDebug($this->varDump($digestRequest)); + $this->debug("certRequest="); + $this->appendDebug($this->varDump($certRequest)); + // cf. RFC 2617 + if ($authtype == 'basic') { + $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password)); + } elseif ($authtype == 'digest') { + if (isset($digestRequest['nonce'])) { + $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; + + // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) + + // A1 = unq(username-value) ":" unq(realm-value) ":" passwd + $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password; + + // H(A1) = MD5(A1) + $HA1 = md5($A1); + + // A2 = Method ":" digest-uri-value + $A2 = $this->request_method . ':' . $this->digest_uri; + + // H(A2) + $HA2 = md5($A2); + + // KD(secret, data) = H(concat(secret, ":", data)) + // if qop == auth: + // request-digest = <"> < KD ( H(A1), unq(nonce-value) + // ":" nc-value + // ":" unq(cnonce-value) + // ":" unq(qop-value) + // ":" H(A2) + // ) <"> + // if qop is missing, + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> + + $unhashedDigest = ''; + $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; + $cnonce = $nonce; + if ($digestRequest['qop'] != '') { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; + } else { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; + } + + $hashedDigest = md5($unhashedDigest); + + $opaque = ''; + if (isset($digestRequest['opaque'])) { + $opaque = ', opaque="' . $digestRequest['opaque'] . '"'; + } + + $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'); + } + } elseif ($authtype == 'certificate') { + $this->certRequest = $certRequest; + $this->debug('Authorization header not set for certificate'); + } elseif ($authtype == 'ntlm') { + // do nothing + $this->debug('Authorization header not set for ntlm'); + } + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->digestRequest = $digestRequest; + } + + /** + * set the soapaction value + * + * @param string $soapaction + * @access public + */ + function setSOAPAction($soapaction) { + $this->setHeader('SOAPAction', '"' . $soapaction . '"'); + } + + /** + * use http encoding + * + * @param string $enc encoding style. supported values: gzip, deflate, or both + * @access public + */ + function setEncoding($enc='gzip, deflate') { + if (function_exists('gzdeflate')) { + $this->protocol_version = '1.1'; + $this->setHeader('Accept-Encoding', $enc); + if (!isset($this->outgoing_headers['Connection'])) { + $this->setHeader('Connection', 'close'); + $this->persistentConnection = false; + } + // deprecated as of PHP 5.3.0 + //set_magic_quotes_runtime(0); + $this->encoding = $enc; + } + } + + /** + * set proxy info here + * + * @param string $proxyhost use an empty string to remove proxy + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param string $proxyauthtype (basic|ntlm) + * @access public + */ + function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') { + if ($proxyhost) { + $this->proxy = array( + 'host' => $proxyhost, + 'port' => $proxyport, + 'username' => $proxyusername, + 'password' => $proxypassword, + 'authtype' => $proxyauthtype + ); + if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') { + $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword)); + } + } else { + $this->debug('remove proxy'); + $proxy = null; + unsetHeader('Proxy-Authorization'); + } + } + + + /** + * Test if the given string starts with a header that is to be skipped. + * Skippable headers result from chunked transfer and proxy requests. + * + * @param string $data The string to check. + * @returns boolean Whether a skippable header was found. + * @access private + */ + function isSkippableCurlHeader(&$data) { + $skipHeaders = array( 'HTTP/1.1 100', + 'HTTP/1.0 301', + 'HTTP/1.1 301', + 'HTTP/1.0 302', + 'HTTP/1.1 302', + 'HTTP/1.0 401', + 'HTTP/1.1 401', + 'HTTP/1.0 200 Connection established'); + foreach ($skipHeaders as $hd) { + $prefix = substr($data, 0, strlen($hd)); + if ($prefix == $hd) return true; + } + + return false; + } + + /** + * decode a string that is encoded w/ "chunked' transfer encoding + * as defined in RFC2068 19.4.6 + * + * @param string $buffer + * @param string $lb + * @returns string + * @access public + * @deprecated + */ + function decodeChunked($buffer, $lb){ + // length := 0 + $length = 0; + $new = ''; + + // read chunk-size, chunk-extension (if any) and CRLF + // get the position of the linebreak + $chunkend = strpos($buffer, $lb); + if ($chunkend == FALSE) { + $this->debug('no linebreak found in decodeChunked'); + return $new; + } + $temp = substr($buffer,0,$chunkend); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend + strlen($lb); + // while (chunk-size > 0) { + while ($chunk_size > 0) { + $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); + $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); + + // Just in case we got a broken connection + if ($chunkend == FALSE) { + $chunk = substr($buffer,$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + $length += strlen($chunk); + break; + } + + // read chunk-data and CRLF + $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + // length := length + chunk-size + $length += strlen($chunk); + // read chunk-size and CRLF + $chunkstart = $chunkend + strlen($lb); + + $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); + if ($chunkend == FALSE) { + break; //Just in case we got a broken connection + } + $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend; + } + return $new; + } + + /** + * Writes the payload, including HTTP headers, to $this->outgoing_payload. + * + * @param string $data HTTP body + * @param string $cookie_str data for HTTP Cookie header + * @return void + * @access private + */ + function buildPayload($data, $cookie_str = '') { + // Note: for cURL connections, $this->outgoing_payload is ignored, + // as is the Content-Length header, but these are still created as + // debugging guides. + + // add content-length header + if ($this->request_method != 'GET') { + $this->setHeader('Content-Length', strlen($data)); + } + + // start building outgoing payload: + if ($this->proxy) { + $uri = $this->url; + } else { + $uri = $this->uri; + } + $req = "$this->request_method $uri HTTP/$this->protocol_version"; + $this->debug("HTTP request: $req"); + $this->outgoing_payload = "$req\r\n"; + + // loop thru headers, serializing + foreach($this->outgoing_headers as $k => $v){ + $hdr = $k.': '.$v; + $this->debug("HTTP header: $hdr"); + $this->outgoing_payload .= "$hdr\r\n"; + } + + // add any cookies + if ($cookie_str != '') { + $hdr = 'Cookie: '.$cookie_str; + $this->debug("HTTP header: $hdr"); + $this->outgoing_payload .= "$hdr\r\n"; + } + + // header/body separator + $this->outgoing_payload .= "\r\n"; + + // add data + $this->outgoing_payload .= $data; + } + + /** + * sends the SOAP request via HTTP[S] + * + * @param string $data message data + * @param array $cookies cookies to send + * @return boolean true if OK, false if problem + * @access private + */ + function sendRequest($data, $cookies = NULL) { + // build cookie string + $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https'))); + + // build payload + $this->buildPayload($data, $cookie_str); + + if ($this->io_method() == 'socket') { + // send payload + if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { + $this->setError('couldn\'t write message data to socket'); + $this->debug('couldn\'t write message data to socket'); + return false; + } + $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); + return true; + } else if ($this->io_method() == 'curl') { + // set payload + // cURL does say this should only be the verb, and in fact it + // turns out that the URI and HTTP version are appended to this, which + // some servers refuse to work with (so we no longer use this method!) + //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); + $curl_headers = array(); + foreach($this->outgoing_headers as $k => $v){ + if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') { + $this->debug("Skip cURL header $k: $v"); + } else { + $curl_headers[] = "$k: $v"; + } + } + if ($cookie_str != '') { + $curl_headers[] = 'Cookie: ' . $cookie_str; + } + $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers); + $this->debug('set cURL HTTP headers'); + if ($this->request_method == "POST") { + $this->setCurlOption(CURLOPT_POST, 1); + $this->setCurlOption(CURLOPT_POSTFIELDS, $data); + $this->debug('set cURL POST data'); + } else { + } + // insert custom user-set cURL options + foreach ($this->ch_options as $key => $val) { + $this->setCurlOption($key, $val); + } + + $this->debug('set cURL payload'); + return true; + } + } + + /** + * gets the SOAP response via HTTP[S] + * + * @return string the response (also sets member variables like incoming_payload) + * @access private + */ + function getResponse(){ + $this->incoming_payload = ''; + + if ($this->io_method() == 'socket') { + // loop until headers have been retrieved + $data = ''; + while (!isset($lb)){ + + // We might EOF during header read. + if(feof($this->fp)) { + $this->incoming_payload = $data; + $this->debug('found no headers before EOF after length ' . strlen($data)); + $this->debug("received before EOF:\n" . $data); + $this->setError('server failed to send headers'); + return false; + } + + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read line of $tmplen bytes: " . trim($tmp)); + + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of headers timed out after length ' . strlen($data)); + $this->debug("read before timeout: " . $data); + $this->setError('socket read of headers timed out'); + return false; + } + + $data .= $tmp; + $pos = strpos($data,"\r\n\r\n"); + if($pos > 1){ + $lb = "\r\n"; + } else { + $pos = strpos($data,"\n\n"); + if($pos > 1){ + $lb = "\n"; + } + } + // remove 100 headers + if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) { + unset($lb); + $data = ''; + }// + } + // store header data + $this->incoming_payload .= $data; + $this->debug('found end of headers after length ' . strlen($data)); + // process headers + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + $this->incoming_headers = array(); + $this->incoming_cookies = array(); + foreach($header_array as $header_line){ + $arr = explode(':',$header_line, 2); + if(count($arr) > 1){ + $header_name = strtolower(trim($arr[0])); + $this->incoming_headers[$header_name] = trim($arr[1]); + if ($header_name == 'set-cookie') { + // TODO: allow multiple cookies from parseCookie + $cookie = $this->parseCookie(trim($arr[1])); + if ($cookie) { + $this->incoming_cookies[] = $cookie; + $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); + } else { + $this->debug('did not find cookie in ' . trim($arr[1])); + } + } + } else if (isset($header_name)) { + // append continuation line to previous header + $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; + } + } + + // loop until msg has been received + if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { + $content_length = 2147483647; // ignore any content-length header + $chunked = true; + $this->debug("want to read chunked content"); + } elseif (isset($this->incoming_headers['content-length'])) { + $content_length = $this->incoming_headers['content-length']; + $chunked = false; + $this->debug("want to read content of length $content_length"); + } else { + $content_length = 2147483647; + $chunked = false; + $this->debug("want to read content to EOF"); + } + $data = ''; + do { + if ($chunked) { + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read chunk line of $tmplen bytes"); + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of chunk length timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of chunk length timed out'); + return false; + } + $content_length = hexdec(trim($tmp)); + $this->debug("chunk length $content_length"); + } + $strlen = 0; + while (($strlen < $content_length) && (!feof($this->fp))) { + $readlen = min(8192, $content_length - $strlen); + $tmp = fread($this->fp, $readlen); + $tmplen = strlen($tmp); + $this->debug("read buffer of $tmplen bytes"); + if (($tmplen == 0) && (!feof($this->fp))) { + $this->incoming_payload = $data; + $this->debug('socket read of body timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of body timed out'); + return false; + } + $strlen += $tmplen; + $data .= $tmp; + } + if ($chunked && ($content_length > 0)) { + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read chunk terminator of $tmplen bytes"); + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of chunk terminator timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of chunk terminator timed out'); + return false; + } + } + } while ($chunked && ($content_length > 0) && (!feof($this->fp))); + if (feof($this->fp)) { + $this->debug('read to EOF'); + } + $this->debug('read body of length ' . strlen($data)); + $this->incoming_payload .= $data; + $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); + + // close filepointer + if( + (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') || + (! $this->persistentConnection) || feof($this->fp)){ + fclose($this->fp); + $this->fp = false; + $this->debug('closed socket'); + } + + // connection was closed unexpectedly + if($this->incoming_payload == ''){ + $this->setError('no response from server'); + return false; + } + + // decode transfer-encoding +// if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){ +// if(!$data = $this->decodeChunked($data, $lb)){ +// $this->setError('Decoding of chunked data failed'); +// return false; +// } + //print "
\nde-chunked:\n---------------\n$data\n\n---------------\n
"; + // set decoded payload +// $this->incoming_payload = $header_data.$lb.$lb.$data; +// } + + } else if ($this->io_method() == 'curl') { + // send and receive + $this->debug('send and receive with cURL'); + $this->incoming_payload = curl_exec($this->ch); + $data = $this->incoming_payload; + + $cErr = curl_error($this->ch); + if ($cErr != '') { + $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'
'; + // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE + foreach(curl_getinfo($this->ch) as $k => $v){ + $err .= "$k: $v
"; + } + $this->debug($err); + $this->setError($err); + curl_close($this->ch); + return false; + } else { + //echo '
';
+			//var_dump(curl_getinfo($this->ch));
+			//echo '
'; + } + // close curl + $this->debug('No cURL error, closing cURL'); + curl_close($this->ch); + + // try removing skippable headers + $savedata = $data; + while ($this->isSkippableCurlHeader($data)) { + $this->debug("Found HTTP header to skip"); + if ($pos = strpos($data,"\r\n\r\n")) { + $data = ltrim(substr($data,$pos)); + } elseif($pos = strpos($data,"\n\n") ) { + $data = ltrim(substr($data,$pos)); + } + } + + if ($data == '') { + // have nothing left; just remove 100 header(s) + $data = $savedata; + while (preg_match('/^HTTP\/1.1 100/',$data)) { + if ($pos = strpos($data,"\r\n\r\n")) { + $data = ltrim(substr($data,$pos)); + } elseif($pos = strpos($data,"\n\n") ) { + $data = ltrim(substr($data,$pos)); + } + } + } + + // separate content from HTTP headers + if ($pos = strpos($data,"\r\n\r\n")) { + $lb = "\r\n"; + } elseif( $pos = strpos($data,"\n\n")) { + $lb = "\n"; + } else { + $this->debug('no proper separation of headers and document'); + $this->setError('no proper separation of headers and document'); + return false; + } + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + $data = ltrim(substr($data,$pos)); + $this->debug('found proper separation of headers and document'); + $this->debug('cleaned data, stringlen: '.strlen($data)); + // clean headers + foreach ($header_array as $header_line) { + $arr = explode(':',$header_line,2); + if(count($arr) > 1){ + $header_name = strtolower(trim($arr[0])); + $this->incoming_headers[$header_name] = trim($arr[1]); + if ($header_name == 'set-cookie') { + // TODO: allow multiple cookies from parseCookie + $cookie = $this->parseCookie(trim($arr[1])); + if ($cookie) { + $this->incoming_cookies[] = $cookie; + $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); + } else { + $this->debug('did not find cookie in ' . trim($arr[1])); + } + } + } else if (isset($header_name)) { + // append continuation line to previous header + $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; + } + } + } + + $this->response_status_line = $header_array[0]; + $arr = explode(' ', $this->response_status_line, 3); + $http_version = $arr[0]; + $http_status = intval($arr[1]); + $http_reason = count($arr) > 2 ? $arr[2] : ''; + + // see if we need to resend the request with http digest authentication + if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) { + $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']); + $this->setURL($this->incoming_headers['location']); + $this->tryagain = true; + return false; + } + + // see if we need to resend the request with http digest authentication + if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) { + $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']); + if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) { + $this->debug('Server wants digest authentication'); + // remove "Digest " from our elements + $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); + + // parse elements into array + $digestElements = explode(',', $digestString); + foreach ($digestElements as $val) { + $tempElement = explode('=', trim($val), 2); + $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); + } + + // should have (at least) qop, realm, nonce + if (isset($digestRequest['nonce'])) { + $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); + $this->tryagain = true; + return false; + } + } + $this->debug('HTTP authentication failed'); + $this->setError('HTTP authentication failed'); + return false; + } + + if ( + ($http_status >= 300 && $http_status <= 307) || + ($http_status >= 400 && $http_status <= 417) || + ($http_status >= 501 && $http_status <= 505) + ) { + $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)"); + return false; + } + + // decode content-encoding + if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){ + if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){ + // if decoding works, use it. else assume data wasn't gzencoded + if(function_exists('gzinflate')){ + //$timer->setMarker('starting decoding of gzip/deflated content'); + // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress) + // this means there are no Zlib headers, although there should be + $this->debug('The gzinflate function exists'); + $datalen = strlen($data); + if ($this->incoming_headers['content-encoding'] == 'deflate') { + if ($degzdata = @gzinflate($data)) { + $data = $degzdata; + $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes'); + if (strlen($data) < $datalen) { + // test for the case that the payload has been compressed twice + $this->debug('The inflated payload is smaller than the gzipped one; try again'); + if ($degzdata = @gzinflate($data)) { + $data = $degzdata; + $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes'); + } + } + } else { + $this->debug('Error using gzinflate to inflate the payload'); + $this->setError('Error using gzinflate to inflate the payload'); + } + } elseif ($this->incoming_headers['content-encoding'] == 'gzip') { + if ($degzdata = @gzinflate(substr($data, 10))) { // do our best + $data = $degzdata; + $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes'); + if (strlen($data) < $datalen) { + // test for the case that the payload has been compressed twice + $this->debug('The un-gzipped payload is smaller than the gzipped one; try again'); + if ($degzdata = @gzinflate(substr($data, 10))) { + $data = $degzdata; + $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes'); + } + } + } else { + $this->debug('Error using gzinflate to un-gzip the payload'); + $this->setError('Error using gzinflate to un-gzip the payload'); + } + } + //$timer->setMarker('finished decoding of gzip/deflated content'); + //print "\nde-inflated:\n---------------\n$data\n-------------\n"; + // set decoded payload + $this->incoming_payload = $header_data.$lb.$lb.$data; + } else { + $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); + $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); + } + } else { + $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); + $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); + } + } else { + $this->debug('No Content-Encoding header'); + } + + if(strlen($data) == 0){ + $this->debug('no data after headers!'); + $this->setError('no data present after HTTP headers'); + return false; + } + + return $data; + } + + /** + * sets the content-type for the SOAP message to be sent + * + * @param string $type the content type, MIME style + * @param mixed $charset character set used for encoding (or false) + * @access public + */ + function setContentType($type, $charset = false) { + $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); + } + + /** + * specifies that an HTTP persistent connection should be used + * + * @return boolean whether the request was honored by this method. + * @access public + */ + function usePersistentConnection(){ + if (isset($this->outgoing_headers['Accept-Encoding'])) { + return false; + } + $this->protocol_version = '1.1'; + $this->persistentConnection = true; + $this->setHeader('Connection', 'Keep-Alive'); + return true; + } + + /** + * parse an incoming Cookie into it's parts + * + * @param string $cookie_str content of cookie + * @return array with data of that cookie + * @access private + */ + /* + * TODO: allow a Set-Cookie string to be parsed into multiple cookies + */ + function parseCookie($cookie_str) { + $cookie_str = str_replace('; ', ';', $cookie_str) . ';'; + $data = preg_split('/;/', $cookie_str); + $value_str = $data[0]; + + $cookie_param = 'domain='; + $start = strpos($cookie_str, $cookie_param); + if ($start > 0) { + $domain = substr($cookie_str, $start + strlen($cookie_param)); + $domain = substr($domain, 0, strpos($domain, ';')); + } else { + $domain = ''; + } + + $cookie_param = 'expires='; + $start = strpos($cookie_str, $cookie_param); + if ($start > 0) { + $expires = substr($cookie_str, $start + strlen($cookie_param)); + $expires = substr($expires, 0, strpos($expires, ';')); + } else { + $expires = ''; + } + + $cookie_param = 'path='; + $start = strpos($cookie_str, $cookie_param); + if ( $start > 0 ) { + $path = substr($cookie_str, $start + strlen($cookie_param)); + $path = substr($path, 0, strpos($path, ';')); + } else { + $path = '/'; + } + + $cookie_param = ';secure;'; + if (strpos($cookie_str, $cookie_param) !== FALSE) { + $secure = true; + } else { + $secure = false; + } + + $sep_pos = strpos($value_str, '='); + + if ($sep_pos) { + $name = substr($value_str, 0, $sep_pos); + $value = substr($value_str, $sep_pos + 1); + $cookie= array( 'name' => $name, + 'value' => $value, + 'domain' => $domain, + 'path' => $path, + 'expires' => $expires, + 'secure' => $secure + ); + return $cookie; + } + return false; + } + + /** + * sort out cookies for the current request + * + * @param array $cookies array with all cookies + * @param boolean $secure is the send-content secure or not? + * @return string for Cookie-HTTP-Header + * @access private + */ + function getCookiesForRequest($cookies, $secure=false) { + $cookie_str = ''; + if ((! is_null($cookies)) && (is_array($cookies))) { + foreach ($cookies as $cookie) { + if (! is_array($cookie)) { + continue; + } + $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']); + if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { + if (strtotime($cookie['expires']) <= time()) { + $this->debug('cookie has expired'); + continue; + } + } + if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) { + $domain = preg_quote($cookie['domain']); + if (! preg_match("'.*$domain$'i", $this->host)) { + $this->debug('cookie has different domain'); + continue; + } + } + if ((isset($cookie['path'])) && (! empty($cookie['path']))) { + $path = preg_quote($cookie['path']); + if (! preg_match("'^$path.*'i", $this->path)) { + $this->debug('cookie is for a different path'); + continue; + } + } + if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) { + $this->debug('cookie is secure, transport is not'); + continue; + } + $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; '; + $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']); + } + } + return $cookie_str; + } +} + + +?> \ No newline at end of file diff --git a/classes/lib/class.soap_val.php b/classes/lib/class.soap_val.php new file mode 100644 index 0000000..4438f5c --- /dev/null +++ b/classes/lib/class.soap_val.php @@ -0,0 +1,107 @@ + +* @version $Id: class.soap_val.php,v 1.11 2007/04/06 13:56:32 snichol Exp $ +* @access public +*/ +class soapval extends nusoap_base { + /** + * The XML element name + * + * @var string + * @access private + */ + var $name; + /** + * The XML type name (string or false) + * + * @var mixed + * @access private + */ + var $type; + /** + * The PHP value + * + * @var mixed + * @access private + */ + var $value; + /** + * The XML element namespace (string or false) + * + * @var mixed + * @access private + */ + var $element_ns; + /** + * The XML type namespace (string or false) + * + * @var mixed + * @access private + */ + var $type_ns; + /** + * The XML element attributes (array or false) + * + * @var mixed + * @access private + */ + var $attributes; + + /** + * constructor + * + * @param string $name optional name + * @param mixed $type optional type name + * @param mixed $value optional value + * @param mixed $element_ns optional namespace of value + * @param mixed $type_ns optional namespace of type + * @param mixed $attributes associative array of attributes to add to element serialization + * @access public + */ + function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) { + parent::nusoap_base(); + $this->name = $name; + $this->type = $type; + $this->value = $value; + $this->element_ns = $element_ns; + $this->type_ns = $type_ns; + $this->attributes = $attributes; + } + + /** + * return serialized value + * + * @param string $use The WSDL use value (encoded|literal) + * @return string XML data + * @access public + */ + function serialize($use='encoded') { + return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true); + } + + /** + * decodes a soapval object into a PHP native type + * + * @return mixed + * @access public + */ + function decode(){ + return $this->value; + } +} + + + + +?> \ No newline at end of file diff --git a/classes/lib/class.soapclient.php b/classes/lib/class.soapclient.php new file mode 100644 index 0000000..6944c2a --- /dev/null +++ b/classes/lib/class.soapclient.php @@ -0,0 +1,991 @@ +call( string methodname [ ,array parameters] ); +* +* // bye bye client +* unset($soapclient); +* +* @author Dietrich Ayala +* @author Scott Nichol +* @version $Id: class.soapclient.php,v 1.69 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_client extends nusoap_base { + + var $username = ''; // Username for HTTP authentication + var $password = ''; // Password for HTTP authentication + var $authtype = ''; // Type of HTTP authentication + var $certRequest = array(); // Certificate for HTTP SSL authentication + var $requestHeaders = false; // SOAP headers in request (text) + var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text) + var $responseHeader = NULL; // SOAP Header from response (parsed) + var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text) + var $endpoint; + var $forceEndpoint = ''; // overrides WSDL endpoint + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $portName = ''; // port name to use in WSDL + var $xml_encoding = ''; // character set encoding of incoming (response) messages + var $http_encoding = false; + var $timeout = 0; // HTTP connection timeout + var $response_timeout = 30; // HTTP response timeout + var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error + var $persistentConnection = false; + var $defaultRpcParams = false; // This is no longer used + var $request = ''; // HTTP request + var $response = ''; // HTTP response + var $responseData = ''; // SOAP payload of response + var $cookies = array(); // Cookies from response or for request + var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode() + var $operations = array(); // WSDL operations, empty for WSDL initialization error + var $curl_options = array(); // User-specified cURL options + var $bindingType = ''; // WSDL operation binding type + var $use_curl = false; // whether to always try to use cURL + + /* + * fault related variables + */ + /** + * @var fault + * @access public + */ + var $fault; + /** + * @var faultcode + * @access public + */ + var $faultcode; + /** + * @var faultstring + * @access public + */ + var $faultstring; + /** + * @var faultdetail + * @access public + */ + var $faultdetail; + + /** + * constructor + * + * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object) + * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL + * @param string $proxyhost optional + * @param string $proxyport optional + * @param string $proxyusername optional + * @param string $proxypassword optional + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @param string $portName optional portName in WSDL document + * @access public + */ + function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){ + parent::nusoap_base(); + $this->endpoint = $endpoint; + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + $this->portName = $portName; + + $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout"); + $this->appendDebug('endpoint=' . $this->varDump($endpoint)); + + // make values + if($wsdl){ + if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) { + $this->wsdl = $endpoint; + $this->endpoint = $this->wsdl->wsdl; + $this->wsdlFile = $this->endpoint; + $this->debug('existing wsdl instance created from ' . $this->endpoint); + $this->checkWSDL(); + } else { + $this->wsdlFile = $this->endpoint; + $this->wsdl = null; + $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint); + } + $this->endpointType = 'wsdl'; + } else { + $this->debug("instantiate SOAP with endpoint at $endpoint"); + $this->endpointType = 'soap'; + } + } + + /** + * calls method, returns PHP native type + * + * @param string $operation SOAP server URL or path + * @param mixed $params An array, associative or simple, of the parameters + * for the method call, or a string that is the XML + * for the call. For rpc style, this call will + * wrap the XML in a tag named after the method, as + * well as the SOAP Envelope and Body. For document + * style, this will only wrap with the Envelope and Body. + * IMPORTANT: when using an array with document style, + * in which case there + * is really one parameter, the root of the fragment + * used in the call, which encloses what programmers + * normally think of parameters. A parameter array + * *must* include the wrapper. + * @param string $namespace optional method namespace (WSDL can override) + * @param string $soapAction optional SOAPAction value (WSDL can override) + * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array + * @param boolean $rpcParams optional (no longer used) + * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override) + * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override) + * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors + * @access public + */ + function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){ + $this->operation = $operation; + $this->fault = false; + $this->setError(''); + $this->request = ''; + $this->response = ''; + $this->responseData = ''; + $this->faultstring = ''; + $this->faultcode = ''; + $this->opData = array(); + + $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType"); + $this->appendDebug('params=' . $this->varDump($params)); + $this->appendDebug('headers=' . $this->varDump($headers)); + if ($headers) { + $this->requestHeaders = $headers; + } + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) + return false; + } + // serialize parameters + if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){ + // use WSDL for operation + $this->opData = $opData; + $this->debug("found operation"); + $this->appendDebug('opData=' . $this->varDump($opData)); + if (isset($opData['soapAction'])) { + $soapAction = $opData['soapAction']; + } + if (! $this->forceEndpoint) { + $this->endpoint = $opData['endpoint']; + } else { + $this->endpoint = $this->forceEndpoint; + } + $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace; + $style = $opData['style']; + $use = $opData['input']['use']; + // add ns to ns array + if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){ + $nsPrefix = 'ns' . rand(1000, 9999); + $this->wsdl->namespaces[$nsPrefix] = $namespace; + } + $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace); + // serialize payload + if (is_string($params)) { + $this->debug("serializing param string for WSDL operation $operation"); + $payload = $params; + } elseif (is_array($params)) { + $this->debug("serializing param array for WSDL operation $operation"); + $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType); + } else { + $this->debug('params must be array or string'); + $this->setError('params must be array or string'); + return false; + } + $usedNamespaces = $this->wsdl->usedNamespaces; + if (isset($opData['input']['encodingStyle'])) { + $encodingStyle = $opData['input']['encodingStyle']; + } else { + $encodingStyle = ''; + } + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if ($errstr = $this->wsdl->getError()) { + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + return false; + } + } elseif($this->endpointType == 'wsdl') { + // operation not in WSDL + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->setError('operation '.$operation.' not present in WSDL.'); + $this->debug("operation '$operation' not present in WSDL."); + return false; + } else { + // no WSDL + //$this->namespaces['ns1'] = $namespace; + $nsPrefix = 'ns' . rand(1000, 9999); + // serialize + $payload = ''; + if (is_string($params)) { + $this->debug("serializing param string for operation $operation"); + $payload = $params; + } elseif (is_array($params)) { + $this->debug("serializing param array for operation $operation"); + foreach($params as $k => $v){ + $payload .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } else { + $this->debug('params must be array or string'); + $this->setError('params must be array or string'); + return false; + } + $usedNamespaces = array(); + if ($use == 'encoded') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } else { + $encodingStyle = ''; + } + } + // wrap RPC calls with method element + if ($style == 'rpc') { + if ($use == 'literal') { + $this->debug("wrapping RPC request with literal method element"); + if ($namespace) { + // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace + $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . + $payload . + ""; + } else { + $payload = "<$operation>" . $payload . ""; + } + } else { + $this->debug("wrapping RPC request with encoded method element"); + if ($namespace) { + $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . + $payload . + ""; + } else { + $payload = "<$operation>" . + $payload . + ""; + } + } + } + // serialize envelope + $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle); + $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle"); + $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000)); + // send + $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout); + if($errstr = $this->getError()){ + $this->debug('Error: '.$errstr); + return false; + } else { + $this->return = $return; + $this->debug('sent message successfully and got a(n) '.gettype($return)); + $this->appendDebug('return=' . $this->varDump($return)); + + // fault? + if(is_array($return) && isset($return['faultcode'])){ + $this->debug('got fault'); + $this->setError($return['faultcode'].': '.$return['faultstring']); + $this->fault = true; + foreach($return as $k => $v){ + $this->$k = $v; + $this->debug("$k = $v
"); + } + return $return; + } elseif ($style == 'document') { + // NOTE: if the response is defined to have multiple parts (i.e. unwrapped), + // we are only going to return the first part here...sorry about that + return $return; + } else { + // array of return values + if(is_array($return)){ + // multiple 'out' parameters, which we return wrapped up + // in the array + if(sizeof($return) > 1){ + return $return; + } + // single 'out' parameter (normally the return value) + $return = array_shift($return); + $this->debug('return shifted value: '); + $this->appendDebug($this->varDump($return)); + return $return; + // nothing returned (ie, echoVoid) + } else { + return ""; + } + } + } + } + + /** + * check WSDL passed as an instance or pulled from an endpoint + * + * @access private + */ + function checkWSDL() { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('checkWSDL'); + // catch errors + if ($errstr = $this->wsdl->getError()) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->bindingType = 'soap'; + $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType); + } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->bindingType = 'soap12'; + $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType); + $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************'); + } else { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('getOperations returned false'); + $this->setError('no operations defined in the WSDL document!'); + } + } + + /** + * instantiate wsdl object and parse wsdl file + * + * @access public + */ + function loadWSDL() { + $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile); + $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl); + $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest); + $this->wsdl->fetchWSDL($this->wsdlFile); + $this->checkWSDL(); + } + + /** + * get available data pertaining to an operation + * + * @param string $operation operation name + * @return array array of data pertaining to the operation + * @access public + */ + function getOperationData($operation){ + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) + return false; + } + if(isset($this->operations[$operation])){ + return $this->operations[$operation]; + } + $this->debug("No data for operation: $operation"); + } + + /** + * send the SOAP message + * + * Note: if the operation has multiple return values + * the return value of this method will be an array + * of those values. + * + * @param string $msg a SOAPx4 soapmsg object + * @param string $soapaction SOAPAction value + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return mixed native PHP types. + * @access private + */ + function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) { + $this->checkCookies(); + // detect transport + switch(true){ + // http(s) + case preg_match('/^http/',$this->endpoint): + $this->debug('transporting via HTTP'); + if($this->persistentConnection == true && is_object($this->persistentConnection)){ + $http =& $this->persistentConnection; + } else { + $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl); + if ($this->persistentConnection) { + $http->usePersistentConnection(); + } + } + $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset()); + $http->setSOAPAction($soapaction); + if($this->proxyhost && $this->proxyport){ + $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if($this->authtype != '') { + $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest); + } + if($this->http_encoding != ''){ + $http->setEncoding($this->http_encoding); + } + $this->debug('sending message, length='.strlen($msg)); + if(preg_match('/^http:/',$this->endpoint)){ + //if(strpos($this->endpoint,'http:')){ + $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies); + } elseif(preg_match('/^https/',$this->endpoint)){ + //} elseif(strpos($this->endpoint,'https:')){ + //if(phpversion() == '4.3.0-dev'){ + //$response = $http->send($msg,$timeout,$response_timeout); + //$this->request = $http->outgoing_payload; + //$this->response = $http->incoming_payload; + //} else + $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies); + } else { + $this->setError('no http/s in endpoint url'); + } + $this->request = $http->outgoing_payload; + $this->response = $http->incoming_payload; + $this->appendDebug($http->getDebug()); + $this->UpdateCookies($http->incoming_cookies); + + // save transport object if using persistent connections + if ($this->persistentConnection) { + $http->clearDebug(); + if (!is_object($this->persistentConnection)) { + $this->persistentConnection = $http; + } + } + + if($err = $http->getError()){ + $this->setError('HTTP Error: '.$err); + return false; + } elseif($this->getError()){ + return false; + } else { + $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']); + return $this->parseResponse($http->incoming_headers, $this->responseData); + } + break; + default: + $this->setError('no transport found, or selected transport is not yet supported!'); + return false; + break; + } + } + + /** + * processes SOAP message returned from server + * + * @param array $headers The HTTP headers + * @param string $data unprocessed response data from server + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseResponse($headers, $data) { + $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:'); + $this->appendDebug($this->varDump($headers)); + if (!isset($headers['content-type'])) { + $this->setError('Response not of type text/xml (no content-type header)'); + return false; + } + if (!strstr($headers['content-type'], 'text/xml')) { + $this->setError('Response not of type text/xml: ' . $headers['content-type']); + return false; + } + if (strpos($headers['content-type'], '=')) { + $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); + $this->debug('Got response encoding: ' . $enc); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); + $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8); + // add parser debug data to our debug + $this->appendDebug($parser->getDebug()); + // if parse errors + if($errstr = $parser->getError()){ + $this->setError( $errstr); + // destroy the parser object + unset($parser); + return false; + } else { + // get SOAP headers + $this->responseHeaders = $parser->getHeaders(); + // get SOAP headers + $this->responseHeader = $parser->get_soapheader(); + // get decoded message + $return = $parser->get_soapbody(); + // add document for doclit support + $this->document = $parser->document; + // destroy the parser object + unset($parser); + // return decode message + return $return; + } + } + + /** + * sets user-specified cURL options + * + * @param mixed $option The cURL option (always integer?) + * @param mixed $value The cURL option value + * @access public + */ + function setCurlOption($option, $value) { + $this->debug("setCurlOption option=$option, value="); + $this->appendDebug($this->varDump($value)); + $this->curl_options[$option] = $value; + } + + /** + * sets the SOAP endpoint, which can override WSDL + * + * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override + * @access public + */ + function setEndpoint($endpoint) { + $this->debug("setEndpoint(\"$endpoint\")"); + $this->forceEndpoint = $endpoint; + } + + /** + * set the SOAP headers + * + * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers + * @access public + */ + function setHeaders($headers){ + $this->debug("setHeaders headers="); + $this->appendDebug($this->varDump($headers)); + $this->requestHeaders = $headers; + } + + /** + * get the SOAP response headers (namespace resolution incomplete) + * + * @return string + * @access public + */ + function getHeaders(){ + return $this->responseHeaders; + } + + /** + * get the SOAP response Header (parsed) + * + * @return mixed + * @access public + */ + function getHeader(){ + return $this->responseHeader; + } + + /** + * set proxy info here + * + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @access public + */ + function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') { + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype certRequest="); + $this->appendDebug($this->varDump($certRequest)); + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->certRequest = $certRequest; + } + + /** + * use HTTP encoding + * + * @param string $enc HTTP encoding + * @access public + */ + function setHTTPEncoding($enc='gzip, deflate'){ + $this->debug("setHTTPEncoding(\"$enc\")"); + $this->http_encoding = $enc; + } + + /** + * Set whether to try to use cURL connections if possible + * + * @param boolean $use Whether to try to use cURL + * @access public + */ + function setUseCURL($use) { + $this->debug("setUseCURL($use)"); + $this->use_curl = $use; + } + + /** + * use HTTP persistent connections if possible + * + * @access public + */ + function useHTTPPersistentConnection(){ + $this->debug("useHTTPPersistentConnection"); + $this->persistentConnection = true; + } + + /** + * gets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style. + * Each call() can override this value. + * + * This is no longer used. + * + * @return boolean + * @access public + * @deprecated + */ + function getDefaultRpcParams() { + return $this->defaultRpcParams; + } + + /** + * sets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style + * Each call() can override this value. + * + * This is no longer used. + * + * @param boolean $rpcParams + * @access public + * @deprecated + */ + function setDefaultRpcParams($rpcParams) { + $this->defaultRpcParams = $rpcParams; + } + + /** + * dynamically creates an instance of a proxy class, + * allowing user to directly call methods from wsdl + * + * @return object soap_proxy object + * @access public + */ + function getProxy() { + $r = rand(); + $evalStr = $this->_getProxyClassCode($r); + //$this->debug("proxy class: $evalStr"); + if ($this->getError()) { + $this->debug("Error from _getProxyClassCode, so return NULL"); + return null; + } + // eval the class + eval($evalStr); + // instantiate proxy object + eval("\$proxy = new nusoap_proxy_$r('');"); + // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice + $proxy->endpointType = 'wsdl'; + $proxy->wsdlFile = $this->wsdlFile; + $proxy->wsdl = $this->wsdl; + $proxy->operations = $this->operations; + $proxy->defaultRpcParams = $this->defaultRpcParams; + // transfer other state + $proxy->soap_defencoding = $this->soap_defencoding; + $proxy->username = $this->username; + $proxy->password = $this->password; + $proxy->authtype = $this->authtype; + $proxy->certRequest = $this->certRequest; + $proxy->requestHeaders = $this->requestHeaders; + $proxy->endpoint = $this->endpoint; + $proxy->forceEndpoint = $this->forceEndpoint; + $proxy->proxyhost = $this->proxyhost; + $proxy->proxyport = $this->proxyport; + $proxy->proxyusername = $this->proxyusername; + $proxy->proxypassword = $this->proxypassword; + $proxy->http_encoding = $this->http_encoding; + $proxy->timeout = $this->timeout; + $proxy->response_timeout = $this->response_timeout; + $proxy->persistentConnection = &$this->persistentConnection; + $proxy->decode_utf8 = $this->decode_utf8; + $proxy->curl_options = $this->curl_options; + $proxy->bindingType = $this->bindingType; + $proxy->use_curl = $this->use_curl; + return $proxy; + } + + /** + * dynamically creates proxy class code + * + * @return string PHP/NuSOAP code for the proxy class + * @access private + */ + function _getProxyClassCode($r) { + $this->debug("in getProxy endpointType=$this->endpointType"); + $this->appendDebug("wsdl=" . $this->varDump($this->wsdl)); + if ($this->endpointType != 'wsdl') { + $evalStr = 'A proxy can only be created for a WSDL client'; + $this->setError($evalStr); + $evalStr = "echo \"$evalStr\";"; + return $evalStr; + } + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) { + return "echo \"" . $this->getError() . "\";"; + } + } + $evalStr = ''; + foreach ($this->operations as $operation => $opData) { + if ($operation != '') { + // create param string and param comment string + if (sizeof($opData['input']['parts']) > 0) { + $paramStr = ''; + $paramArrayStr = ''; + $paramCommentStr = ''; + foreach ($opData['input']['parts'] as $name => $type) { + $paramStr .= "\$$name, "; + $paramArrayStr .= "'$name' => \$$name, "; + $paramCommentStr .= "$type \$$name, "; + } + $paramStr = substr($paramStr, 0, strlen($paramStr)-2); + $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2); + $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2); + } else { + $paramStr = ''; + $paramArrayStr = ''; + $paramCommentStr = 'void'; + } + $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace']; + $evalStr .= "// $paramCommentStr + function " . str_replace('.', '__', $operation) . "($paramStr) { + \$params = array($paramArrayStr); + return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."'); + } + "; + unset($paramStr); + unset($paramCommentStr); + } + } + $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client { + '.$evalStr.' +}'; + return $evalStr; + } + + /** + * dynamically creates proxy class code + * + * @return string PHP/NuSOAP code for the proxy class + * @access public + */ + function getProxyClassCode() { + $r = rand(); + return $this->_getProxyClassCode($r); + } + + /** + * gets the HTTP body for the current request. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + return $soapmsg; + } + + /** + * gets the HTTP content type for the current request. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current request. + * @access private + */ + function getHTTPContentType() { + return 'text/xml'; + } + + /** + * gets the HTTP content type charset for the current request. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current request. + * @access private + */ + function getHTTPContentTypeCharset() { + return $this->soap_defencoding; + } + + /* + * whether or not parser should decode utf8 element content + * + * @return always returns true + * @access public + */ + function decodeUTF8($bool){ + $this->decode_utf8 = $bool; + return true; + } + + /** + * adds a new Cookie into $this->cookies array + * + * @param string $name Cookie Name + * @param string $value Cookie Value + * @return boolean if cookie-set was successful returns true, else false + * @access public + */ + function setCookie($name, $value) { + if (strlen($name) == 0) { + return false; + } + $this->cookies[] = array('name' => $name, 'value' => $value); + return true; + } + + /** + * gets all Cookies + * + * @return array with all internal cookies + * @access public + */ + function getCookies() { + return $this->cookies; + } + + /** + * checks all Cookies and delete those which are expired + * + * @return boolean always return true + * @access private + */ + function checkCookies() { + if (sizeof($this->cookies) == 0) { + return true; + } + $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies'); + $curr_cookies = $this->cookies; + $this->cookies = array(); + foreach ($curr_cookies as $cookie) { + if (! is_array($cookie)) { + $this->debug('Remove cookie that is not an array'); + continue; + } + if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { + if (strtotime($cookie['expires']) > time()) { + $this->cookies[] = $cookie; + } else { + $this->debug('Remove expired cookie ' . $cookie['name']); + } + } else { + $this->cookies[] = $cookie; + } + } + $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array'); + return true; + } + + /** + * updates the current cookies with a new set + * + * @param array $cookies new cookies with which to update current ones + * @return boolean always return true + * @access private + */ + function UpdateCookies($cookies) { + if (sizeof($this->cookies) == 0) { + // no existing cookies: take whatever is new + if (sizeof($cookies) > 0) { + $this->debug('Setting new cookie(s)'); + $this->cookies = $cookies; + } + return true; + } + if (sizeof($cookies) == 0) { + // no new cookies: keep what we've got + return true; + } + // merge + foreach ($cookies as $newCookie) { + if (!is_array($newCookie)) { + continue; + } + if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) { + continue; + } + $newName = $newCookie['name']; + + $found = false; + for ($i = 0; $i < count($this->cookies); $i++) { + $cookie = $this->cookies[$i]; + if (!is_array($cookie)) { + continue; + } + if (!isset($cookie['name'])) { + continue; + } + if ($newName != $cookie['name']) { + continue; + } + $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN'; + $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN'; + if ($newDomain != $domain) { + continue; + } + $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH'; + $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH'; + if ($newPath != $path) { + continue; + } + $this->cookies[$i] = $newCookie; + $found = true; + $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']); + break; + } + if (! $found) { + $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']); + $this->cookies[] = $newCookie; + } + } + return true; + } +} + +if (!extension_loaded('soap')) { + /** + * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded. + */ + class soapclient extends nusoap_client { + } +} +?> diff --git a/classes/lib/class.wsdl.php b/classes/lib/class.wsdl.php new file mode 100644 index 0000000..6d2a693 --- /dev/null +++ b/classes/lib/class.wsdl.php @@ -0,0 +1,1938 @@ + +* @author Scott Nichol +* @version $Id: class.wsdl.php,v 1.76 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class wsdl extends nusoap_base { + // URL or filename of the root of this WSDL + var $wsdl; + // define internal arrays of bindings, ports, operations, messages, etc. + var $schemas = array(); + var $currentSchema; + var $message = array(); + var $complexTypes = array(); + var $messages = array(); + var $currentMessage; + var $currentOperation; + var $portTypes = array(); + var $currentPortType; + var $bindings = array(); + var $currentBinding; + var $ports = array(); + var $currentPort; + var $opData = array(); + var $status = ''; + var $documentation = false; + var $endpoint = ''; + // array of wsdl docs to import + var $import = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + // for getting wsdl + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $timeout = 0; + var $response_timeout = 30; + var $curl_options = array(); // User-specified cURL options + var $use_curl = false; // whether to always try to use cURL + // for HTTP authentication + var $username = ''; // Username for HTTP authentication + var $password = ''; // Password for HTTP authentication + var $authtype = ''; // Type of HTTP authentication + var $certRequest = array(); // Certificate for HTTP SSL authentication + + /** + * constructor + * + * @param string $wsdl WSDL document URL + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @param array $curl_options user-specified cURL options + * @param boolean $use_curl try to use cURL + * @access public + */ + function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){ + parent::nusoap_base(); + $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout"); + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + if (is_array($curl_options)) + $this->curl_options = $curl_options; + $this->use_curl = $use_curl; + $this->fetchWSDL($wsdl); + } + + /** + * fetches the WSDL document and parses it + * + * @access public + */ + function fetchWSDL($wsdl) { + $this->debug("parse and process WSDL path=$wsdl"); + $this->wsdl = $wsdl; + // parse wsdl file + if ($this->wsdl != "") { + $this->parseWSDL($this->wsdl); + } + // imports + // TODO: handle imports more properly, grabbing them in-line and nesting them + $imported_urls = array(); + $imported = 1; + while ($imported > 0) { + $imported = 0; + // Schema imports + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($xs->imports as $ns2 => $list2) { + for ($ii = 0; $ii < count($list2); $ii++) { + if (! $list2[$ii]['loaded']) { + $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true; + $url = $list2[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + } + // WSDL imports + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($this->import as $ns => $list) { + for ($ii = 0; $ii < count($list); $ii++) { + if (! $list[$ii]['loaded']) { + $this->import[$ns][$ii]['loaded'] = true; + $url = $list[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + // add new data to operation data + foreach($this->bindings as $binding => $bindingData) { + if (isset($bindingData['operations']) && is_array($bindingData['operations'])) { + foreach($bindingData['operations'] as $operation => $data) { + $this->debug('post-parse data gathering for ' . $operation); + $this->bindings[$binding]['operations'][$operation]['input'] = + isset($this->bindings[$binding]['operations'][$operation]['input']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['input']; + $this->bindings[$binding]['operations'][$operation]['output'] = + isset($this->bindings[$binding]['operations'][$operation]['output']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['output']; + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ]; + } + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ]; + } + // Set operation style if necessary, but do not override one already provided + if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) { + $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style']; + } + $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : ''; + $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : ''; + $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : ''; + } + } + } + } + + /** + * parses the wsdl document + * + * @param string $wsdl path or URL + * @access private + */ + function parseWSDL($wsdl = '') { + $this->debug("parse WSDL at path=$wsdl"); + + if ($wsdl == '') { + $this->debug('no wsdl passed to parseWSDL()!!'); + $this->setError('no wsdl passed to parseWSDL()!!'); + return false; + } + + // parse $wsdl for url format + $wsdl_props = parse_url($wsdl); + + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) { + $this->debug('getting WSDL http(s) URL ' . $wsdl); + // get wsdl + $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl); + $tr->request_method = 'GET'; + $tr->useSOAPAction = false; + if($this->proxyhost && $this->proxyport){ + $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if ($this->authtype != '') { + $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest); + } + $tr->setEncoding('gzip, deflate'); + $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout); + //$this->debug("WSDL request\n" . $tr->outgoing_payload); + //$this->debug("WSDL response\n" . $tr->incoming_payload); + $this->appendDebug($tr->getDebug()); + // catch errors + if($err = $tr->getError() ){ + $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err; + $this->debug($errstr); + $this->setError($errstr); + unset($tr); + return false; + } + unset($tr); + $this->debug("got WSDL URL"); + } else { + // $wsdl is not http(s), so treat it as a file URL or plain file path + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) { + $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path']; + } else { + $path = $wsdl; + } + $this->debug('getting WSDL file ' . $path); + if ($fp = @fopen($path, 'r')) { + $wsdl_string = ''; + while ($data = fread($fp, 32768)) { + $wsdl_string .= $data; + } + fclose($fp); + } else { + $errstr = "Bad path to WSDL file $path"; + $this->debug($errstr); + $this->setError($errstr); + return false; + } + } + $this->debug('Parse WSDL'); + // end new code added + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element', 'end_element'); + xml_set_character_data_handler($this->parser, 'character_data'); + // Parse the XML file. + if (!xml_parse($this->parser, $wsdl_string, true)) { + // Display an error message. + $errstr = sprintf( + 'XML error parsing WSDL from %s on line %d: %s', + $wsdl, + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $wsdl_string); + $this->setError($errstr); + return false; + } + // free the parser + xml_parser_free($this->parser); + $this->debug('Parsing WSDL done'); + // catch wsdl parse errors + if($this->getError()){ + return false; + } + return true; + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) + { + if ($this->status == 'schema') { + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + } elseif (preg_match('/schema$/', $name)) { + $this->debug('Parsing WSDL schema'); + // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")"); + $this->status = 'schema'; + $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces); + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + } else { + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + // process attributes + if (count($attrs) > 0) { + // register namespace declarations + foreach($attrs as $k => $v) { + if (preg_match('/^xmlns/',$k)) { + if ($ns_prefix = substr(strrchr($k, ':'), 1)) { + $this->namespaces[$ns_prefix] = $v; + } else { + $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v; + } + if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') { + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v . '-instance'; + } + } + } + // expand each attribute prefix to its namespace + foreach($attrs as $k => $v) { + $k = strpos($k, ':') ? $this->expandQname($k) : $k; + if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') { + $v = strpos($v, ':') ? $this->expandQname($v) : $v; + } + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // get element prefix, namespace and name + if (preg_match('/:/', $name)) { + // get ns prefix + $prefix = substr($name, 0, strpos($name, ':')); + // get ns + $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; + // get unqualified name + $name = substr(strstr($name, ':'), 1); + } + // process attributes, expanding any prefixes to namespaces + // find status, register data + switch ($this->status) { + case 'message': + if ($name == 'part') { + if (isset($attrs['type'])) { + $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs)); + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type']; + } + if (isset($attrs['element'])) { + $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs)); + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^'; + } + } + break; + case 'portType': + switch ($name) { + case 'operation': + $this->currentPortOperation = $attrs['name']; + $this->debug("portType $this->currentPortType operation: $this->currentPortOperation"); + if (isset($attrs['parameterOrder'])) { + $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder']; + } + break; + case 'documentation': + $this->documentation = true; + break; + // merge input/output data + default: + $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : ''; + $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m; + break; + } + break; + case 'binding': + switch ($name) { + case 'binding': + // get ns prefix + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['prefix'] = $prefix; + } + $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs); + break; + case 'header': + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; + break; + case 'operation': + if (isset($attrs['soapAction'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction']; + } + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style']; + } + if (isset($attrs['name'])) { + $this->currentOperation = $attrs['name']; + $this->debug("current binding operation: $this->currentOperation"); + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name']; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : ''; + } + break; + case 'input': + $this->opStatus = 'input'; + break; + case 'output': + $this->opStatus = 'output'; + break; + case 'body': + if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs); + } else { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs; + } + break; + } + break; + case 'service': + switch ($name) { + case 'port': + $this->currentPort = $attrs['name']; + $this->debug('current port: ' . $this->currentPort); + $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']); + + break; + case 'address': + $this->ports[$this->currentPort]['location'] = $attrs['location']; + $this->ports[$this->currentPort]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location']; + break; + } + break; + } + // set status + switch ($name) { + case 'import': + if (isset($attrs['location'])) { + $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false); + $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')'); + } else { + $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')'); + } + break; + //wait for schema + //case 'types': + // $this->status = 'schema'; + // break; + case 'message': + $this->status = 'message'; + $this->messages[$attrs['name']] = array(); + $this->currentMessage = $attrs['name']; + break; + case 'portType': + $this->status = 'portType'; + $this->portTypes[$attrs['name']] = array(); + $this->currentPortType = $attrs['name']; + break; + case "binding": + if (isset($attrs['name'])) { + // get binding name + if (strpos($attrs['name'], ':')) { + $this->currentBinding = $this->getLocalPart($attrs['name']); + } else { + $this->currentBinding = $attrs['name']; + } + $this->status = 'binding'; + $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']); + $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']); + } + break; + case 'service': + $this->serviceName = $attrs['name']; + $this->status = 'service'; + $this->debug('current service: ' . $this->serviceName); + break; + case 'definitions': + foreach ($attrs as $name => $value) { + $this->wsdl_info[$name] = $value; + } + break; + } + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name){ + // unset schema status + if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) { + $this->status = ""; + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema; + $this->debug('Parsing WSDL schema done'); + } + if ($this->status == 'schema') { + $this->currentSchema->schemaEndElement($parser, $name); + } else { + // bring depth down a notch + $this->depth--; + } + // end documentation + if ($this->documentation) { + //TODO: track the node to which documentation should be assigned; it can be a part, message, etc. + //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation; + $this->documentation = false; + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data) + { + $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0; + if (isset($this->message[$pos]['cdata'])) { + $this->message[$pos]['cdata'] .= $data; + } + if ($this->documentation) { + $this->documentation .= $data; + } + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype certRequest="); + $this->appendDebug($this->varDump($certRequest)); + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->certRequest = $certRequest; + } + + function getBindingData($binding) + { + if (is_array($this->bindings[$binding])) { + return $this->bindings[$binding]; + } + } + + /** + * returns an assoc array of operation names => operation data + * + * @param string $portName WSDL port name + * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported) + * @return array + * @access public + */ + function getOperations($portName = '', $bindingType = 'soap') { + $ops = array(); + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } else { + $this->debug("getOperations bindingType $bindingType may not be supported"); + } + $this->debug("getOperations for port '$portName' bindingType $bindingType"); + // loop thru ports + foreach($this->ports as $port => $portData) { + $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']); + if ($portName == '' || $port == $portName) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + $this->debug("getOperations found port $port bindingType $bindingType"); + //$this->debug("port data: " . $this->varDump($portData)); + //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ])); + // merge bindings + if (isset($this->bindings[ $portData['binding'] ]['operations'])) { + $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']); + } + } + } + } + if (count($ops) == 0) { + $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType"); + } + return $ops; + } + + /** + * returns an associative array of data necessary for calling an operation + * + * @param string $operation name of operation + * @param string $bindingType type of binding eg: soap, soap12 + * @return array + * @access public + */ + function getOperationData($operation, $bindingType = 'soap') + { + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + // get binding + //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { + foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) { + // note that we could/should also check the namespace here + if ($operation == $bOperation) { + $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation]; + return $opData; + } + } + } + } + } + + /** + * returns an associative array of data necessary for calling an operation + * + * @param string $soapAction soapAction for operation + * @param string $bindingType type of binding eg: soap, soap12 + * @return array + * @access public + */ + function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') { + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + // loop through operations for the binding + foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { + if ($opData['soapAction'] == $soapAction) { + return $opData; + } + } + } + } + } + + /** + * returns an array of information about a given type + * returns false if no type exists by the given name + * + * typeDef = array( + * 'elements' => array(), // refs to elements array + * 'restrictionBase' => '', + * 'phpType' => '', + * 'order' => '(sequence|all)', + * 'attrs' => array() // refs to attributes array + * ) + * + * @param string $type the type + * @param string $ns namespace (not prefix) of the type + * @return mixed + * @access public + * @see nusoap_xmlschema + */ + function getTypeDef($type, $ns) { + $this->debug("in getTypeDef: type=$type, ns=$ns"); + if ((! $ns) && isset($this->namespaces['tns'])) { + $ns = $this->namespaces['tns']; + $this->debug("in getTypeDef: type namespace forced to $ns"); + } + if (!isset($this->schemas[$ns])) { + foreach ($this->schemas as $ns0 => $schema0) { + if (strcasecmp($ns, $ns0) == 0) { + $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0"); + $ns = $ns0; + break; + } + } + } + if (isset($this->schemas[$ns])) { + $this->debug("in getTypeDef: have schema for namespace $ns"); + for ($i = 0; $i < count($this->schemas[$ns]); $i++) { + $xs = &$this->schemas[$ns][$i]; + $t = $xs->getTypeDef($type); + $this->appendDebug($xs->getDebug()); + $xs->clearDebug(); + if ($t) { + $this->debug("in getTypeDef: found type $type"); + if (!isset($t['phpType'])) { + // get info for type to tack onto the element + $uqType = substr($t['type'], strrpos($t['type'], ':') + 1); + $ns = substr($t['type'], 0, strrpos($t['type'], ':')); + $etype = $this->getTypeDef($uqType, $ns); + if ($etype) { + $this->debug("found type for [element] $type:"); + $this->debug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $t['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $t['elements'] = $etype['elements']; + } + if (isset($etype['attrs'])) { + $t['attrs'] = $etype['attrs']; + } + } else { + $this->debug("did not find type for [element] $type"); + } + } + return $t; + } + } + $this->debug("in getTypeDef: did not find type $type"); + } else { + $this->debug("in getTypeDef: do not have schema for namespace $ns"); + } + return false; + } + + /** + * prints html description of services + * + * @access private + */ + function webDescription(){ + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $PHP_SELF = $_SERVER['PHP_SELF']; + } elseif (isset($HTTP_SERVER_VARS)) { + $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF']; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + + $b = ' + NuSOAP: '.$this->serviceName.' + + + + +
+

+
'.$this->serviceName.'
+ +
'; + return $b; + } + + /** + * serialize the parsed wsdl + * + * @param mixed $debug whether to put debug=1 in endpoint URL + * @return string serialization of WSDL + * @access public + */ + function serialize($debug = 0) + { + $xml = ''; + $xml .= "\nnamespaces as $k => $v) { + $xml .= " xmlns:$k=\"$v\""; + } + // 10.9.02 - add poulter fix for wsdl and tns declarations + if (isset($this->namespaces['wsdl'])) { + $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\""; + } + if (isset($this->namespaces['tns'])) { + $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\""; + } + $xml .= '>'; + // imports + if (sizeof($this->import) > 0) { + foreach($this->import as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= ''; + } else { + $xml .= ''; + } + } + } + } + // types + if (count($this->schemas)>=1) { + $xml .= "\n\n"; + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $xml .= $xs->serializeSchema(); + } + } + $xml .= ''; + } + // messages + if (count($this->messages) >= 1) { + foreach($this->messages as $msgName => $msgParts) { + $xml .= "\n'; + if(is_array($msgParts)){ + foreach($msgParts as $partName => $partType) { + // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'
'; + if (strpos($partType, ':')) { + $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType)); + } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) { + // print 'checking typemap: '.$this->XMLSchemaVersion.'
'; + $typePrefix = 'xsd'; + } else { + foreach($this->typemap as $ns => $types) { + if (isset($types[$partType])) { + $typePrefix = $this->getPrefixFromNamespace($ns); + } + } + if (!isset($typePrefix)) { + die("$partType has no namespace!"); + } + } + $ns = $this->getNamespaceFromPrefix($typePrefix); + $localPart = $this->getLocalPart($partType); + $typeDef = $this->getTypeDef($localPart, $ns); + if ($typeDef['typeClass'] == 'element') { + $elementortype = 'element'; + if (substr($localPart, -1) == '^') { + $localPart = substr($localPart, 0, -1); + } + } else { + $elementortype = 'type'; + } + $xml .= "\n" . ' '; + } + } + $xml .= '
'; + } + } + // bindings & porttypes + if (count($this->bindings) >= 1) { + $binding_xml = ''; + $portType_xml = ''; + foreach($this->bindings as $bindingName => $attrs) { + $binding_xml .= "\n'; + $binding_xml .= "\n" . ' '; + $portType_xml .= "\n'; + foreach($attrs['operations'] as $opName => $opParts) { + $binding_xml .= "\n" . ' '; + $binding_xml .= "\n" . ' '; + if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') { + $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"'; + } else { + $enc_style = ''; + } + $binding_xml .= "\n" . ' '; + if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') { + $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"'; + } else { + $enc_style = ''; + } + $binding_xml .= "\n" . ' '; + $binding_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' ' . htmlspecialchars($opParts['documentation']) . ''; + } + $portType_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' '; + } + $portType_xml .= "\n" . ''; + $binding_xml .= "\n" . ''; + } + $xml .= $portType_xml . $binding_xml; + } + // services + $xml .= "\nserviceName . '">'; + if (count($this->ports) >= 1) { + foreach($this->ports as $pName => $attrs) { + $xml .= "\n" . ' '; + $xml .= "\n" . ' '; + $xml .= "\n" . ' '; + } + } + $xml .= "\n" . ''; + return $xml . "\n"; + } + + /** + * determine whether a set of parameters are unwrapped + * when they are expect to be wrapped, Microsoft-style. + * + * @param string $type the type (element name) of the wrapper + * @param array $parameters the parameter values for the SOAP call + * @return boolean whether they parameters are unwrapped (and should be wrapped) + * @access private + */ + function parametersMatchWrapped($type, &$parameters) { + $this->debug("in parametersMatchWrapped type=$type, parameters="); + $this->appendDebug($this->varDump($parameters)); + + // split type into namespace:unqualified-type + if (strpos($type, ':')) { + $uqType = substr($type, strrpos($type, ':') + 1); + $ns = substr($type, 0, strrpos($type, ':')); + $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns"); + if ($this->getNamespaceFromPrefix($ns)) { + $ns = $this->getNamespaceFromPrefix($ns); + $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns"); + } + } else { + // TODO: should the type be compared to types in XSD, and the namespace + // set to XSD if the type matches? + $this->debug("in parametersMatchWrapped: No namespace for type $type"); + $ns = ''; + $uqType = $type; + } + + // get the type information + if (!$typeDef = $this->getTypeDef($uqType, $ns)) { + $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type."); + return false; + } + $this->debug("in parametersMatchWrapped: found typeDef="); + $this->appendDebug($this->varDump($typeDef)); + if (substr($uqType, -1) == '^') { + $uqType = substr($uqType, 0, -1); + } + $phpType = $typeDef['phpType']; + $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''); + $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType"); + + // we expect a complexType or element of complexType + if ($phpType != 'struct') { + $this->debug("in parametersMatchWrapped: not a struct"); + return false; + } + + // see whether the parameter names match the elements + if (isset($typeDef['elements']) && is_array($typeDef['elements'])) { + $elements = 0; + $matches = 0; + foreach ($typeDef['elements'] as $name => $attrs) { + if (isset($parameters[$name])) { + $this->debug("in parametersMatchWrapped: have parameter named $name"); + $matches++; + } else { + $this->debug("in parametersMatchWrapped: do not have parameter named $name"); + } + $elements++; + } + + $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names"); + if ($matches == 0) { + return false; + } + return true; + } + + // since there are no elements for the type, if the user passed no + // parameters, the parameters match wrapped. + $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType"); + return count($parameters) == 0; + } + + /** + * serialize PHP values according to a WSDL message definition + * contrary to the method name, this is not limited to RPC + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $operation operation name + * @param string $direction (input|output) + * @param mixed $parameters parameter value(s) + * @param string $bindingType (soap|soap12) + * @return mixed parameters serialized as XML or false on error (e.g. operation not found) + * @access public + */ + function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') { + $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType"); + $this->appendDebug('parameters=' . $this->varDump($parameters)); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation, $bindingType)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType); + return false; + } + $this->debug('in serializeRPCParameters: opData:'); + $this->appendDebug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + $parts = &$opData[$direction]['parts']; + $part_count = sizeof($parts); + $style = $opData['style']; + $use = $opData[$direction]['use']; + $this->debug("have $part_count part(s) to serialize using $style/$use"); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $parameter_count = count($parameters); + $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize"); + // check for Microsoft-style wrapped parameters + if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) { + $this->debug('check whether the caller has wrapped the parameters'); + if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) { + // TODO: consider checking here for double-wrapping, when + // service function wraps, then NuSOAP wraps again + $this->debug("change simple array to associative with 'parameters' element"); + $parameters['parameters'] = $parameters[0]; + unset($parameters[0]); + } + if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) { + $this->debug('check whether caller\'s parameters match the wrapped ones'); + if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) { + $this->debug('wrap the parameters for the caller'); + $parameters = array('parameters' => $parameters); + $parameter_count = 1; + } + } + } + foreach ($parts as $name => $type) { + $this->debug("serializing part $name of type $type"); + // Track encoding style + if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + $this->debug("serializeRPCParameters returning: $xml"); + return $xml; + } + + /** + * serialize a PHP value according to a WSDL message definition + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $operation operation name + * @param string $direction (input|output) + * @param mixed $parameters parameter value(s) + * @return mixed parameters serialized as XML or false on error (e.g. operation not found) + * @access public + * @deprecated + */ + function serializeParameters($operation, $direction, $parameters) + { + $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion"); + $this->appendDebug('parameters=' . $this->varDump($parameters)); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation); + return false; + } + $this->debug('opData:'); + $this->appendDebug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + + $use = $opData[$direction]['use']; + $this->debug("use=$use"); + $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)'); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $this->debug('have ' . $parametersArrayType . ' parameters'); + foreach($opData[$direction]['parts'] as $name => $type) { + $this->debug('serializing part "'.$name.'" of type "'.$type.'"'); + // Track encoding style + if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + $this->debug("serializeParameters returning: $xml"); + return $xml; + } + + /** + * serializes a PHP value according a given type definition + * + * @param string $name name of value (part or element) + * @param string $type XML schema type of value (type or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $use use for part (encoded|literal) + * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style) + * @param boolean $unqualified a kludge for what should be XML namespace form handling + * @return string value serialized as an XML string + * @access private + */ + function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false) + { + $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified")); + $this->appendDebug("value=" . $this->varDump($value)); + if($use == 'encoded' && $encodingStyle) { + $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"'; + } + + // if a soapval has been supplied, let its type override the WSDL + if (is_object($value) && get_class($value) == 'soapval') { + if ($value->type_ns) { + $type = $value->type_ns . ':' . $value->type; + $forceType = true; + $this->debug("in serializeType: soapval overrides type to $type"); + } elseif ($value->type) { + $type = $value->type; + $forceType = true; + $this->debug("in serializeType: soapval overrides type to $type"); + } else { + $forceType = false; + $this->debug("in serializeType: soapval does not override type"); + } + $attrs = $value->attributes; + $value = $value->value; + $this->debug("in serializeType: soapval overrides value to $value"); + if ($attrs) { + if (!is_array($value)) { + $value['!'] = $value; + } + foreach ($attrs as $n => $v) { + $value['!' . $n] = $v; + } + $this->debug("in serializeType: soapval provides attributes"); + } + } else { + $forceType = false; + } + + $xml = ''; + if (strpos($type, ':')) { + $uqType = substr($type, strrpos($type, ':') + 1); + $ns = substr($type, 0, strrpos($type, ':')); + $this->debug("in serializeType: got a prefixed type: $uqType, $ns"); + if ($this->getNamespaceFromPrefix($ns)) { + $ns = $this->getNamespaceFromPrefix($ns); + $this->debug("in serializeType: expanded prefixed type: $uqType, $ns"); + } + + if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){ + $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type'); + if ($unqualified && $use == 'literal') { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$elementNS/>"; + } else { + // TODO: depends on nillable, which should be checked before calling this method + $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if ($uqType == 'Array') { + // JBoss/Axis does this sometimes + return $this->serialize_val($value, $name, false, false, false, false, $use); + } + if ($uqType == 'boolean') { + if ((is_string($value) && $value == 'false') || (! $value)) { + $value = 'false'; + } else { + $value = 'true'; + } + } + if ($uqType == 'string' && gettype($value) == 'string') { + $value = $this->expandEntities($value); + } + if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') { + $value = sprintf("%.0lf", $value); + } + // it's a scalar + // TODO: what about null/nil values? + // check type isn't a custom type extending xmlschema namespace + if (!$this->getTypeDef($uqType, $ns)) { + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value"; + } else { + $xml = "<$name$elementNS>$value"; + } + } else { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)'); + } else if ($ns == 'http://xml.apache.org/xml-soap') { + $this->debug('in serializeType: appears to be Apache SOAP type'); + if ($uqType == 'Map') { + $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap'); + if (! $tt_prefix) { + $this->debug('in serializeType: Add namespace for Apache SOAP type'); + $tt_prefix = 'ns' . rand(1000, 9999); + $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap'; + // force this to be added to usedNamespaces + $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap'); + } + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing map element: key $k, value $v"); + $contents .= ''; + $contents .= $this->serialize_val($k,'key',false,false,false,false,$use); + $contents .= $this->serialize_val($v,'value',false,false,false,false,$use); + $contents .= ''; + } + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents"; + } else { + $xml = "<$name>$contents"; + } + } else { + $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + $this->debug('in serializeType: Apache SOAP type, but only support Map'); + } + } else { + // TODO: should the type be compared to types in XSD, and the namespace + // set to XSD if the type matches? + $this->debug("in serializeType: No namespace for type $type"); + $ns = ''; + $uqType = $type; + } + if(!$typeDef = $this->getTypeDef($uqType, $ns)){ + $this->setError("$type ($uqType) is not a supported type."); + $this->debug("in serializeType: $type ($uqType) is not a supported type."); + return false; + } else { + $this->debug("in serializeType: found typeDef"); + $this->appendDebug('typeDef=' . $this->varDump($typeDef)); + if (substr($uqType, -1) == '^') { + $uqType = substr($uqType, 0, -1); + } + } + if (!isset($typeDef['phpType'])) { + $this->setError("$type ($uqType) has no phpType."); + $this->debug("in serializeType: $type ($uqType) has no phpType."); + return false; + } + $phpType = $typeDef['phpType']; + $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); + // if php type == struct, map value to the element names + if ($phpType == 'struct') { + if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') { + $elementName = $uqType; + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + $elementNS = " xmlns=\"\""; + } + } else { + $elementName = $name; + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs and nillable + $xml = "<$elementName$elementNS/>"; + } else { + $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if (is_object($value)) { + $value = get_object_vars($value); + } + if (is_array($value)) { + $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType); + if ($use == 'literal') { + if ($forceType) { + $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">"; + } else { + $xml = "<$elementName$elementNS$elementAttrs>"; + } + } else { + $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>"; + } + + if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') { + if (isset($value['!'])) { + $xml .= $value['!']; + $this->debug("in serializeType: serialized simpleContent for type $type"); + } else { + $this->debug("in serializeType: no simpleContent to serialize for type $type"); + } + } else { + // complexContent + $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle); + } + $xml .= ""; + } else { + $this->debug("in serializeType: phpType is struct, but value is not an array"); + $this->setError("phpType is struct, but value is not an array: see debug output for details"); + $xml = ''; + } + } elseif ($phpType == 'array') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$elementNS/>"; + } else { + $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . + ":Array\" " . + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . + ':arrayType="' . + $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) . + ':' . + $this->getLocalPart($typeDef['arrayType'])."[0]\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if (isset($typeDef['multidimensional'])) { + $nv = array(); + foreach($value as $v) { + $cols = ',' . sizeof($v); + $nv = array_merge($nv, $v); + } + $value = $nv; + } else { + $cols = ''; + } + if (is_array($value) && sizeof($value) >= 1) { + $rows = sizeof($value); + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]"); + //if (strpos($typeDef['arrayType'], ':') ) { + if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) { + $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use); + } else { + $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use); + } + } + } else { + $rows = 0; + $contents = null; + } + // TODO: for now, an empty value will be serialized as a zero element + // array. Revisit this when coding the handling of null/nil values. + if ($use == 'literal') { + $xml = "<$name$elementNS>" + .$contents + .""; + } else { + $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '. + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') + .':arrayType="' + .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) + .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">" + .$contents + .""; + } + } elseif ($phpType == 'scalar') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value"; + } else { + $xml = "<$name$elementNS>$value"; + } + } else { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value"; + } + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + + /** + * serializes the attributes for a complexType + * + * @param array $typeDef our internal representation of an XML schema type (or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $ns the namespace of the type + * @param string $uqType the local part of the type + * @return string value serialized as an XML string + * @access private + */ + function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) { + $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType"); + $xml = ''; + if (isset($typeDef['extensionBase'])) { + $nsx = $this->getPrefix($typeDef['extensionBase']); + $uqTypex = $this->getLocalPart($typeDef['extensionBase']); + if ($this->getNamespaceFromPrefix($nsx)) { + $nsx = $this->getNamespaceFromPrefix($nsx); + } + if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) { + $this->debug("serialize attributes for extension base $nsx:$uqTypex"); + $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex); + } else { + $this->debug("extension base $nsx:$uqTypex is not a supported type"); + } + } + if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) { + $this->debug("serialize attributes for XML Schema type $ns:$uqType"); + if (is_array($value)) { + $xvalue = $value; + } elseif (is_object($value)) { + $xvalue = get_object_vars($value); + } else { + $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType"); + $xvalue = array(); + } + foreach ($typeDef['attrs'] as $aName => $attrs) { + if (isset($xvalue['!' . $aName])) { + $xname = '!' . $aName; + $this->debug("value provided for attribute $aName with key $xname"); + } elseif (isset($xvalue[$aName])) { + $xname = $aName; + $this->debug("value provided for attribute $aName with key $xname"); + } elseif (isset($attrs['default'])) { + $xname = '!' . $aName; + $xvalue[$xname] = $attrs['default']; + $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName); + } else { + $xname = ''; + $this->debug("no value provided for attribute $aName"); + } + if ($xname) { + $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\""; + } + } + } else { + $this->debug("no attributes to serialize for XML Schema type $ns:$uqType"); + } + return $xml; + } + + /** + * serializes the elements for a complexType + * + * @param array $typeDef our internal representation of an XML schema type (or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $ns the namespace of the type + * @param string $uqType the local part of the type + * @param string $use use for part (encoded|literal) + * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style) + * @return string value serialized as an XML string + * @access private + */ + function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) { + $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType"); + $xml = ''; + if (isset($typeDef['extensionBase'])) { + $nsx = $this->getPrefix($typeDef['extensionBase']); + $uqTypex = $this->getLocalPart($typeDef['extensionBase']); + if ($this->getNamespaceFromPrefix($nsx)) { + $nsx = $this->getNamespaceFromPrefix($nsx); + } + if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) { + $this->debug("serialize elements for extension base $nsx:$uqTypex"); + $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle); + } else { + $this->debug("extension base $nsx:$uqTypex is not a supported type"); + } + } + if (isset($typeDef['elements']) && is_array($typeDef['elements'])) { + $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType"); + if (is_array($value)) { + $xvalue = $value; + } elseif (is_object($value)) { + $xvalue = get_object_vars($value); + } else { + $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType"); + $xvalue = array(); + } + // toggle whether all elements are present - ideally should validate against schema + if (count($typeDef['elements']) != count($xvalue)){ + $optionals = true; + } + foreach ($typeDef['elements'] as $eName => $attrs) { + if (!isset($xvalue[$eName])) { + if (isset($attrs['default'])) { + $xvalue[$eName] = $attrs['default']; + $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName); + } + } + // if user took advantage of a minOccurs=0, then only serialize named parameters + if (isset($optionals) + && (!isset($xvalue[$eName])) + && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true') + ){ + if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') { + $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']); + } + // do nothing + $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing"); + } else { + // get value + if (isset($xvalue[$eName])) { + $v = $xvalue[$eName]; + } else { + $v = null; + } + if (isset($attrs['form'])) { + $unqualified = ($attrs['form'] == 'unqualified'); + } else { + $unqualified = false; + } + if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') { + $vv = $v; + foreach ($vv as $k => $v) { + if (isset($attrs['type']) || isset($attrs['ref'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } else { + // serialize generic type (can this ever really happen?) + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } else { + if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') { + // do nothing + } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') { + // TODO: serialize a nil correctly, but for now serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } elseif (isset($attrs['type']) || isset($attrs['ref'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } else { + // serialize generic type (can this ever really happen?) + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } + } + } else { + $this->debug("no elements to serialize for XML Schema type $ns:$uqType"); + } + return $xml; + } + + /** + * adds an XML Schema complex type to the WSDL types + * + * @param string $name + * @param string $typeClass (complexType|simpleType|attribute) + * @param string $phpType currently supported are array and struct (php assoc array) + * @param string $compositor (all|sequence|choice) + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param array $elements e.g. array ( name => array(name=>'',type=>'') ) + * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]')) + * @param string $arrayType as namespace:name (xsd:string) + * @see nusoap_xmlschema + * @access public + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') { + if (count($elements) > 0) { + $eElements = array(); + foreach($elements as $n => $e){ + // expand each element + $ee = array(); + foreach ($e as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $ee[$k] = $v; + } + $eElements[$n] = $ee; + } + $elements = $eElements; + } + + if (count($attrs) > 0) { + foreach($attrs as $n => $a){ + // expand each attribute + foreach ($a as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $aa[$k] = $v; + } + $eAttrs[$n] = $aa; + } + $attrs = $eAttrs; + } + + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType); + } + + /** + * adds an XML Schema simple type to the WSDL types + * + * @param string $name + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param string $typeClass (should always be simpleType) + * @param string $phpType (should always be scalar) + * @param array $enumeration array of values + * @see nusoap_xmlschema + * @access public + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration); + } + + /** + * adds an element to the WSDL types + * + * @param array $attrs attributes that must include name and type + * @see nusoap_xmlschema + * @access public + */ + function addElement($attrs) { + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addElement($attrs); + } + + /** + * register an operation with the server + * + * @param string $name operation (method) name + * @param array $in assoc array of input values: key = param name, value = param type + * @param array $out assoc array of output values: key = param name, value = param type + * @param string $namespace optional The namespace for the operation + * @param string $soapaction optional The soapaction for the operation + * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically + * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now) + * @param string $documentation optional The description to include in the WSDL + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @access public + */ + function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){ + if ($use == 'encoded' && $encodingStyle == '') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } + + if ($style == 'document') { + $elements = array(); + foreach ($in as $n => $t) { + $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified'); + } + $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements); + $this->addElement(array('name' => $name, 'type' => $name . 'RequestType')); + $in = array('parameters' => 'tns:' . $name . '^'); + + $elements = array(); + foreach ($out as $n => $t) { + $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified'); + } + $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements); + $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified')); + $out = array('parameters' => 'tns:' . $name . 'Response' . '^'); + } + + // get binding + $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] = + array( + 'name' => $name, + 'binding' => $this->serviceName . 'Binding', + 'endpoint' => $this->endpoint, + 'soapAction' => $soapaction, + 'style' => $style, + 'input' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Request', + 'parts' => $in), + 'output' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Response', + 'parts' => $out), + 'namespace' => $namespace, + 'transport' => 'http://schemas.xmlsoap.org/soap/http', + 'documentation' => $documentation); + // add portTypes + // add messages + if($in) + { + foreach($in as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Request'][$pName] = $pType; + } + } else { + $this->messages[$name.'Request']= '0'; + } + if($out) + { + foreach($out as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Response'][$pName] = $pType; + } + } else { + $this->messages[$name.'Response']= '0'; + } + return true; + } +} + +?> \ No newline at end of file diff --git a/classes/lib/class.wsdlcache.php b/classes/lib/class.wsdlcache.php new file mode 100644 index 0000000..9824bb3 --- /dev/null +++ b/classes/lib/class.wsdlcache.php @@ -0,0 +1,209 @@ + +* @author Ingo Fischer +* @version $Id: class.wsdlcache.php,v 1.7 2007/04/17 16:34:03 snichol Exp $ +* @access public +*/ +class nusoap_wsdlcache { + /** + * @var resource + * @access private + */ + var $fplock; + /** + * @var integer + * @access private + */ + var $cache_lifetime; + /** + * @var string + * @access private + */ + var $cache_dir; + /** + * @var string + * @access public + */ + var $debug_str = ''; + + /** + * constructor + * + * @param string $cache_dir directory for cache-files + * @param integer $cache_lifetime lifetime for caching-files in seconds or 0 for unlimited + * @access public + */ + function nusoap_wsdlcache($cache_dir='.', $cache_lifetime=0) { + $this->fplock = array(); + $this->cache_dir = $cache_dir != '' ? $cache_dir : '.'; + $this->cache_lifetime = $cache_lifetime; + } + + /** + * creates the filename used to cache a wsdl instance + * + * @param string $wsdl The URL of the wsdl instance + * @return string The filename used to cache the instance + * @access private + */ + function createFilename($wsdl) { + return $this->cache_dir.'/wsdlcache-' . md5($wsdl); + } + + /** + * adds debug data to the class level debug string + * + * @param string $string debug data + * @access private + */ + function debug($string){ + $this->debug_str .= get_class($this).": $string\n"; + } + + /** + * gets a wsdl instance from the cache + * + * @param string $wsdl The URL of the wsdl instance + * @return object wsdl The cached wsdl instance, null if the instance is not in the cache + * @access public + */ + function get($wsdl) { + $filename = $this->createFilename($wsdl); + if ($this->obtainMutex($filename, "r")) { + // check for expired WSDL that must be removed from the cache + if ($this->cache_lifetime > 0) { + if (file_exists($filename) && (time() - filemtime($filename) > $this->cache_lifetime)) { + unlink($filename); + $this->debug("Expired $wsdl ($filename) from cache"); + $this->releaseMutex($filename); + return null; + } + } + // see what there is to return + if (!file_exists($filename)) { + $this->debug("$wsdl ($filename) not in cache (1)"); + $this->releaseMutex($filename); + return null; + } + $fp = @fopen($filename, "r"); + if ($fp) { + $s = implode("", @file($filename)); + fclose($fp); + $this->debug("Got $wsdl ($filename) from cache"); + } else { + $s = null; + $this->debug("$wsdl ($filename) not in cache (2)"); + } + $this->releaseMutex($filename); + return (!is_null($s)) ? unserialize($s) : null; + } else { + $this->debug("Unable to obtain mutex for $filename in get"); + } + return null; + } + + /** + * obtains the local mutex + * + * @param string $filename The Filename of the Cache to lock + * @param string $mode The open-mode ("r" or "w") or the file - affects lock-mode + * @return boolean Lock successfully obtained ?! + * @access private + */ + function obtainMutex($filename, $mode) { + if (isset($this->fplock[md5($filename)])) { + $this->debug("Lock for $filename already exists"); + return false; + } + $this->fplock[md5($filename)] = fopen($filename.".lock", "w"); + if ($mode == "r") { + return flock($this->fplock[md5($filename)], LOCK_SH); + } else { + return flock($this->fplock[md5($filename)], LOCK_EX); + } + } + + /** + * adds a wsdl instance to the cache + * + * @param object wsdl $wsdl_instance The wsdl instance to add + * @return boolean WSDL successfully cached + * @access public + */ + function put($wsdl_instance) { + $filename = $this->createFilename($wsdl_instance->wsdl); + $s = serialize($wsdl_instance); + if ($this->obtainMutex($filename, "w")) { + $fp = fopen($filename, "w"); + if (! $fp) { + $this->debug("Cannot write $wsdl_instance->wsdl ($filename) in cache"); + $this->releaseMutex($filename); + return false; + } + fputs($fp, $s); + fclose($fp); + $this->debug("Put $wsdl_instance->wsdl ($filename) in cache"); + $this->releaseMutex($filename); + return true; + } else { + $this->debug("Unable to obtain mutex for $filename in put"); + } + return false; + } + + /** + * releases the local mutex + * + * @param string $filename The Filename of the Cache to lock + * @return boolean Lock successfully released + * @access private + */ + function releaseMutex($filename) { + $ret = flock($this->fplock[md5($filename)], LOCK_UN); + fclose($this->fplock[md5($filename)]); + unset($this->fplock[md5($filename)]); + if (! $ret) { + $this->debug("Not able to release lock for $filename"); + } + return $ret; + } + + /** + * removes a wsdl instance from the cache + * + * @param string $wsdl The URL of the wsdl instance + * @return boolean Whether there was an instance to remove + * @access public + */ + function remove($wsdl) { + $filename = $this->createFilename($wsdl); + if (!file_exists($filename)) { + $this->debug("$wsdl ($filename) not in cache to be removed"); + return false; + } + // ignore errors obtaining mutex + $this->obtainMutex($filename, "w"); + $ret = unlink($filename); + $this->debug("Removed ($ret) $wsdl ($filename) from cache"); + $this->releaseMutex($filename); + return $ret; + } +} + +/** + * For backward compatibility + */ +class wsdlcache extends nusoap_wsdlcache { +} +?> diff --git a/classes/lib/class.xmlschema.php b/classes/lib/class.xmlschema.php new file mode 100644 index 0000000..02866bf --- /dev/null +++ b/classes/lib/class.xmlschema.php @@ -0,0 +1,973 @@ + +* @author Scott Nichol +* @version $Id: class.xmlschema.php,v 1.53 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_xmlschema extends nusoap_base { + + // files + var $schema = ''; + var $xml = ''; + // namespaces + var $enclosingNamespaces; + // schema info + var $schemaInfo = array(); + var $schemaTargetNamespace = ''; + // types, elements, attributes defined by the schema + var $attributes = array(); + var $complexTypes = array(); + var $complexTypeStack = array(); + var $currentComplexType = null; + var $elements = array(); + var $elementStack = array(); + var $currentElement = null; + var $simpleTypes = array(); + var $simpleTypeStack = array(); + var $currentSimpleType = null; + // imports + var $imports = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + var $message = array(); + var $defaultNamespace = array(); + + /** + * constructor + * + * @param string $schema schema document URI + * @param string $xml xml document URI + * @param string $namespaces namespaces defined in enclosing XML + * @access public + */ + function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){ + parent::nusoap_base(); + $this->debug('nusoap_xmlschema class instantiated, inside constructor'); + // files + $this->schema = $schema; + $this->xml = $xml; + + // namespaces + $this->enclosingNamespaces = $namespaces; + $this->namespaces = array_merge($this->namespaces, $namespaces); + + // parse schema file + if($schema != ''){ + $this->debug('initial schema file: '.$schema); + $this->parseFile($schema, 'schema'); + } + + // parse xml file + if($xml != ''){ + $this->debug('initial xml file: '.$xml); + $this->parseFile($xml, 'xml'); + } + + } + + /** + * parse an XML file + * + * @param string $xml path/URL to XML file + * @param string $type (schema | xml) + * @return boolean + * @access public + */ + function parseFile($xml,$type){ + // parse xml file + if($xml != ""){ + $xmlStr = @join("",@file($xml)); + if($xmlStr == ""){ + $msg = 'Error reading XML from '.$xml; + $this->setError($msg); + $this->debug($msg); + return false; + } else { + $this->debug("parsing $xml"); + $this->parseString($xmlStr,$type); + $this->debug("done parsing $xml"); + return true; + } + } + return false; + } + + /** + * parse an XML string + * + * @param string $xml path or URL + * @param string $type (schema|xml) + * @access private + */ + function parseString($xml,$type){ + // parse xml string + if($xml != ""){ + + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + + // Set the object for the parser. + xml_set_object($this->parser, $this); + + // Set the element handlers for the parser. + if($type == "schema"){ + xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); + xml_set_character_data_handler($this->parser,'schemaCharacterData'); + } elseif($type == "xml"){ + xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); + xml_set_character_data_handler($this->parser,'xmlCharacterData'); + } + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $errstr = sprintf('XML error parsing XML schema on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $xml); + $this->setError($errstr); + } + + xml_parser_free($this->parser); + } else{ + $this->debug('no xml passed to parseString()!!'); + $this->setError('no xml passed to parseString()!!'); + } + } + + /** + * gets a type name for an unnamed type + * + * @param string Element name + * @return string A type name for an unnamed type + * @access private + */ + function CreateTypeName($ename) { + $scope = ''; + for ($i = 0; $i < count($this->complexTypeStack); $i++) { + $scope .= $this->complexTypeStack[$i] . '_'; + } + return $scope . $ename . '_ContainedType'; + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function schemaStartElement($parser, $name, $attrs) { + + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + if ($depth > 0) { + $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]]; + } else { + $this->defaultNamespace[$pos] = false; + } + + // get element prefix + if($prefix = $this->getPrefix($name)){ + // get unqualified name + $name = $this->getLocalPart($name); + } else { + $prefix = ''; + } + + // loop thru attributes, expanding, and registering namespace declarations + if(count($attrs) > 0){ + foreach($attrs as $k => $v){ + // if ns declarations, add to class level array of valid namespaces + if(preg_match('/^xmlns/',$k)){ + //$this->xdebug("$k: $v"); + //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); + if($ns_prefix = substr(strrchr($k,':'),1)){ + //$this->xdebug("Add namespace[$ns_prefix] = $v"); + $this->namespaces[$ns_prefix] = $v; + } else { + $this->defaultNamespace[$pos] = $v; + if (! $this->getPrefixFromNamespace($v)) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; + } + } + if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){ + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v.'-instance'; + } + } + } + foreach($attrs as $k => $v){ + // expand each attribute + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // find status, register data + switch($name){ + case 'all': // (optional) compositor content for a complexType + case 'choice': + case 'group': + case 'sequence': + //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); + $this->complexTypes[$this->currentComplexType]['compositor'] = $name; + //if($name == 'all' || $name == 'sequence'){ + // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + //} + break; + case 'attribute': // complexType attribute + //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); + $this->xdebug("parsing attribute:"); + $this->appendDebug($this->varDump($attrs)); + if (!isset($attrs['form'])) { + // TODO: handle globals + $attrs['form'] = $this->schemaInfo['attributeFormDefault']; + } + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + if (!strpos($v, ':')) { + // no namespace in arrayType attribute value... + if ($this->defaultNamespace[$pos]) { + // ...so use the default + $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } + } + } + if(isset($attrs['name'])){ + $this->attributes[$attrs['name']] = $attrs; + $aname = $attrs['name']; + } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $aname = ''; + } + } elseif(isset($attrs['ref'])){ + $aname = $attrs['ref']; + $this->attributes[$attrs['ref']] = $attrs; + } + + if($this->currentComplexType){ // This should *always* be + $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; + } + // arrayType attribute + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + $prefix = $this->getPrefix($aname); + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $v = ''; + } + if(strpos($v,'[,]')){ + $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; + } + $v = substr($v,0,strpos($v,'[')); // clip the [] + if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ + $v = $this->XMLSchemaVersion.':'.$v; + } + $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; + } + break; + case 'complexContent': // (optional) content for a complexType + $this->xdebug("do nothing for element $name"); + break; + case 'complexType': + array_push($this->complexTypeStack, $this->currentComplexType); + if(isset($attrs['name'])){ + // TODO: what is the scope of named complexTypes that appear + // nested within other c complexTypes? + $this->xdebug('processing named complexType '.$attrs['name']); + //$this->currentElement = false; + $this->currentComplexType = $attrs['name']; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + // This is for constructs like + // + // + // + // + // + if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ + $this->xdebug('complexType is unusual array'); + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + } else { + $name = $this->CreateTypeName($this->currentElement); + $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name); + $this->currentComplexType = $name; + //$this->currentElement = false; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + // This is for constructs like + // + // + // + // + // + if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ + $this->xdebug('complexType is unusual array'); + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + } + $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false'; + break; + case 'element': + array_push($this->elementStack, $this->currentElement); + if (!isset($attrs['form'])) { + if ($this->currentComplexType) { + $attrs['form'] = $this->schemaInfo['elementFormDefault']; + } else { + // global + $attrs['form'] = 'qualified'; + } + } + if(isset($attrs['type'])){ + $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']); + if (! $this->getPrefix($attrs['type'])) { + if ($this->defaultNamespace[$pos]) { + $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type']; + $this->xdebug('used default namespace to make type ' . $attrs['type']); + } + } + // This is for constructs like + // + // + // + // + // + if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') { + $this->xdebug('arrayType for unusual array is ' . $attrs['type']); + $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type']; + } + $this->currentElement = $attrs['name']; + $ename = $attrs['name']; + } elseif(isset($attrs['ref'])){ + $this->xdebug("processing element as ref to ".$attrs['ref']); + $this->currentElement = "ref to ".$attrs['ref']; + $ename = $this->getLocalPart($attrs['ref']); + } else { + $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']); + $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type); + $this->currentElement = $attrs['name']; + $attrs['type'] = $this->schemaTargetNamespace . ':' . $type; + $ename = $attrs['name']; + } + if (isset($ename) && $this->currentComplexType) { + $this->xdebug("add element $ename to complexType $this->currentComplexType"); + $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; + } elseif (!isset($attrs['ref'])) { + $this->xdebug("add element $ename to elements array"); + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + } + break; + case 'enumeration': // restriction value list member + $this->xdebug('enumeration ' . $attrs['value']); + if ($this->currentSimpleType) { + $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value']; + } elseif ($this->currentComplexType) { + $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value']; + } + break; + case 'extension': // simpleContent or complexContent type extension + $this->xdebug('extension ' . $attrs['base']); + if ($this->currentComplexType) { + $ns = $this->getPrefix($attrs['base']); + if ($ns == '') { + $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base']; + } else { + $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base']; + } + } else { + $this->xdebug('no current complexType to set extensionBase'); + } + break; + case 'import': + if (isset($attrs['schemaLocation'])) { + $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']); + $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); + } else { + $this->xdebug('import namespace ' . $attrs['namespace']); + $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + } + break; + case 'include': + if (isset($attrs['schemaLocation'])) { + $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']); + $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); + } else { + $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute'); + } + break; + case 'list': // simpleType value list + $this->xdebug("do nothing for element $name"); + break; + case 'restriction': // simpleType, simpleContent or complexContent value restriction + $this->xdebug('restriction ' . $attrs['base']); + if($this->currentSimpleType){ + $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base']; + } elseif($this->currentComplexType){ + $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; + if(strstr($attrs['base'],':') == ':Array'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } + } + break; + case 'schema': + $this->schemaInfo = $attrs; + $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); + if (isset($attrs['targetNamespace'])) { + $this->schemaTargetNamespace = $attrs['targetNamespace']; + } + if (!isset($attrs['elementFormDefault'])) { + $this->schemaInfo['elementFormDefault'] = 'unqualified'; + } + if (!isset($attrs['attributeFormDefault'])) { + $this->schemaInfo['attributeFormDefault'] = 'unqualified'; + } + break; + case 'simpleContent': // (optional) content for a complexType + if ($this->currentComplexType) { // This should *always* be + $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true'; + } else { + $this->xdebug("do nothing for element $name because there is no current complexType"); + } + break; + case 'simpleType': + array_push($this->simpleTypeStack, $this->currentSimpleType); + if(isset($attrs['name'])){ + $this->xdebug("processing simpleType for name " . $attrs['name']); + $this->currentSimpleType = $attrs['name']; + $this->simpleTypes[ $attrs['name'] ] = $attrs; + $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType'; + $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar'; + } else { + $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement); + $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name); + $this->currentSimpleType = $name; + //$this->currentElement = false; + $this->simpleTypes[$this->currentSimpleType] = $attrs; + $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar'; + } + break; + case 'union': // simpleType type list + $this->xdebug("do nothing for element $name"); + break; + default: + $this->xdebug("do not have any logic to process element $name"); + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function schemaEndElement($parser, $name) { + // bring depth down a notch + $this->depth--; + // position of current element is equal to the last value left in depth_array for my depth + if(isset($this->depth_array[$this->depth])){ + $pos = $this->depth_array[$this->depth]; + } + // get element prefix + if ($prefix = $this->getPrefix($name)){ + // get unqualified name + $name = $this->getLocalPart($name); + } else { + $prefix = ''; + } + // move on... + if($name == 'complexType'){ + $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)')); + $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType])); + $this->currentComplexType = array_pop($this->complexTypeStack); + //$this->currentElement = false; + } + if($name == 'element'){ + $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)')); + $this->currentElement = array_pop($this->elementStack); + } + if($name == 'simpleType'){ + $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)')); + $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType])); + $this->currentSimpleType = array_pop($this->simpleTypeStack); + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function schemaCharacterData($parser, $data){ + $pos = $this->depth_array[$this->depth - 1]; + $this->message[$pos]['cdata'] .= $data; + } + + /** + * serialize the schema + * + * @access public + */ + function serializeSchema(){ + + $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); + $xml = ''; + // imports + if (sizeof($this->imports) > 0) { + foreach($this->imports as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n"; + } else { + $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n"; + } + } + } + } + // complex types + foreach($this->complexTypes as $typeName => $attrs){ + $contentStr = ''; + // serialize child elements + if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){ + foreach($attrs['elements'] as $element => $eParts){ + if(isset($eParts['ref'])){ + $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n"; + } else { + $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\""; + foreach ($eParts as $aName => $aValue) { + // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable + if ($aName != 'name' && $aName != 'type') { + $contentStr .= " $aName=\"$aValue\""; + } + } + $contentStr .= "/>\n"; + } + } + // compositor wraps elements + if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) { + $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." \n"; + } + } + // attributes + if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){ + foreach($attrs['attrs'] as $attr => $aParts){ + $contentStr .= " <$schemaPrefix:attribute"; + foreach ($aParts as $a => $v) { + if ($a == 'ref' || $a == 'type') { + $contentStr .= " $a=\"".$this->contractQName($v).'"'; + } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') { + $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl']; + $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"'; + } else { + $contentStr .= " $a=\"$v\""; + } + } + $contentStr .= "/>\n"; + } + } + // if restriction + if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){ + $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." \n"; + // complex or simple content + if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){ + $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." \n"; + } + } + // finalize complex type + if($contentStr != ''){ + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." \n"; + } else { + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n"; + } + $xml .= $contentStr; + } + // simple types + if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){ + foreach($this->simpleTypes as $typeName => $eParts){ + $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n"; + if (isset($eParts['enumeration'])) { + foreach ($eParts['enumeration'] as $e) { + $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n"; + } + } + $xml .= " \n "; + } + } + // elements + if(isset($this->elements) && count($this->elements) > 0){ + foreach($this->elements as $element => $eParts){ + $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n"; + } + } + // attributes + if(isset($this->attributes) && count($this->attributes) > 0){ + foreach($this->attributes as $attr => $aParts){ + $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>"; + } + } + // finish 'er up + $attr = ''; + foreach ($this->schemaInfo as $k => $v) { + if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') { + $attr .= " $k=\"$v\""; + } + } + $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n"; + foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) { + $el .= " xmlns:$nsp=\"$ns\""; + } + $xml = $el . ">\n".$xml."\n"; + return $xml; + } + + /** + * adds debug data to the clas level debug string + * + * @param string $string debug data + * @access private + */ + function xdebug($string){ + $this->debug('<' . $this->schemaTargetNamespace . '> '.$string); + } + + /** + * get the PHP type of a user defined type in the schema + * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays + * returns false if no type exists, or not w/ the given namespace + * else returns a string that is either a native php type, or 'struct' + * + * @param string $type name of defined type + * @param string $ns namespace of type + * @return mixed + * @access public + * @deprecated + */ + function getPHPType($type,$ns){ + if(isset($this->typemap[$ns][$type])){ + //print "found type '$type' and ns $ns in typemap
"; + return $this->typemap[$ns][$type]; + } elseif(isset($this->complexTypes[$type])){ + //print "getting type '$type' and ns $ns from complexTypes array
"; + return $this->complexTypes[$type]['phpType']; + } + return false; + } + + /** + * returns an associative array of information about a given type + * returns false if no type exists by the given name + * + * For a complexType typeDef = array( + * 'restrictionBase' => '', + * 'phpType' => '', + * 'compositor' => '(sequence|all)', + * 'elements' => array(), // refs to elements array + * 'attrs' => array() // refs to attributes array + * ... and so on (see addComplexType) + * ) + * + * For simpleType or element, the array has different keys. + * + * @param string $type + * @return mixed + * @access public + * @see addComplexType + * @see addSimpleType + * @see addElement + */ + function getTypeDef($type){ + //$this->debug("in getTypeDef for type $type"); + if (substr($type, -1) == '^') { + $is_element = 1; + $type = substr($type, 0, -1); + } else { + $is_element = 0; + } + + if((! $is_element) && isset($this->complexTypes[$type])){ + $this->xdebug("in getTypeDef, found complexType $type"); + return $this->complexTypes[$type]; + } elseif((! $is_element) && isset($this->simpleTypes[$type])){ + $this->xdebug("in getTypeDef, found simpleType $type"); + if (!isset($this->simpleTypes[$type]['phpType'])) { + // get info for type to tack onto the simple type + // TODO: can this ever really apply (i.e. what is a simpleType really?) + $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1); + $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + $this->xdebug("in getTypeDef, found type for simpleType $type:"); + $this->xdebug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $this->simpleTypes[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->simpleTypes[$type]['elements'] = $etype['elements']; + } + } + } + return $this->simpleTypes[$type]; + } elseif(isset($this->elements[$type])){ + $this->xdebug("in getTypeDef, found element $type"); + if (!isset($this->elements[$type]['phpType'])) { + // get info for type to tack onto the element + $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1); + $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + $this->xdebug("in getTypeDef, found type for element $type:"); + $this->xdebug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $this->elements[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->elements[$type]['elements'] = $etype['elements']; + } + if (isset($etype['extensionBase'])) { + $this->elements[$type]['extensionBase'] = $etype['extensionBase']; + } + } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') { + $this->xdebug("in getTypeDef, element $type is an XSD type"); + $this->elements[$type]['phpType'] = 'scalar'; + } + } + return $this->elements[$type]; + } elseif(isset($this->attributes[$type])){ + $this->xdebug("in getTypeDef, found attribute $type"); + return $this->attributes[$type]; + } elseif (preg_match('/_ContainedType$/', $type)) { + $this->xdebug("in getTypeDef, have an untyped element $type"); + $typeDef['typeClass'] = 'simpleType'; + $typeDef['phpType'] = 'scalar'; + $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string'; + return $typeDef; + } + $this->xdebug("in getTypeDef, did not find $type"); + return false; + } + + /** + * returns a sample serialization of a given type, or false if no type by the given name + * + * @param string $type name of type + * @return mixed + * @access public + * @deprecated + */ + function serializeTypeDef($type){ + //print "in sTD() for type $type
"; + if($typeDef = $this->getTypeDef($type)){ + $str .= '<'.$type; + if(is_array($typeDef['attrs'])){ + foreach($typeDef['attrs'] as $attName => $data){ + $str .= " $attName=\"{type = ".$data['type']."}\""; + } + } + $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; + if(count($typeDef['elements']) > 0){ + $str .= ">"; + foreach($typeDef['elements'] as $element => $eData){ + $str .= $this->serializeTypeDef($element); + } + $str .= ""; + } elseif($typeDef['typeClass'] == 'element') { + $str .= ">"; + } else { + $str .= "/>"; + } + return $str; + } + return false; + } + + /** + * returns HTML form elements that allow a user + * to enter values for creating an instance of the given type. + * + * @param string $name name for type instance + * @param string $type name of type + * @return string + * @access public + * @deprecated + */ + function typeToForm($name,$type){ + // get typedef + if($typeDef = $this->getTypeDef($type)){ + // if struct + if($typeDef['phpType'] == 'struct'){ + $buffer .= ''; + foreach($typeDef['elements'] as $child => $childDef){ + $buffer .= " + + "; + } + $buffer .= '
$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):
'; + // if array + } elseif($typeDef['phpType'] == 'array'){ + $buffer .= ''; + for($i=0;$i < 3; $i++){ + $buffer .= " + + "; + } + $buffer .= '
array item (type: $typeDef[arrayType]):
'; + // if scalar + } else { + $buffer .= ""; + } + } else { + $buffer .= ""; + } + return $buffer; + } + + /** + * adds a complex type to the schema + * + * example: array + * + * addType( + * 'ArrayOfstring', + * 'complexType', + * 'array', + * '', + * 'SOAP-ENC:Array', + * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), + * 'xsd:string' + * ); + * + * example: PHP associative array ( SOAP Struct ) + * + * addType( + * 'SOAPStruct', + * 'complexType', + * 'struct', + * 'all', + * array('myVar'=> array('name'=>'myVar','type'=>'string') + * ); + * + * @param name + * @param typeClass (complexType|simpleType|attribute) + * @param phpType: currently supported are array and struct (php assoc array) + * @param compositor (all|sequence|choice) + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param elements = array ( name = array(name=>'',type=>'') ) + * @param attrs = array( + * array( + * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", + * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" + * ) + * ) + * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) + * @access public + * @see getTypeDef + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ + $this->complexTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'compositor'=> $compositor, + 'restrictionBase' => $restrictionBase, + 'elements' => $elements, + 'attrs' => $attrs, + 'arrayType' => $arrayType + ); + + $this->xdebug("addComplexType $name:"); + $this->appendDebug($this->varDump($this->complexTypes[$name])); + } + + /** + * adds a simple type to the schema + * + * @param string $name + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param string $typeClass (should always be simpleType) + * @param string $phpType (should always be scalar) + * @param array $enumeration array of values + * @access public + * @see nusoap_xmlschema + * @see getTypeDef + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { + $this->simpleTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'type' => $restrictionBase, + 'enumeration' => $enumeration + ); + + $this->xdebug("addSimpleType $name:"); + $this->appendDebug($this->varDump($this->simpleTypes[$name])); + } + + /** + * adds an element to the schema + * + * @param array $attrs attributes that must include name and type + * @see nusoap_xmlschema + * @access public + */ + function addElement($attrs) { + if (! $this->getPrefix($attrs['type'])) { + $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type']; + } + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + + $this->xdebug("addElement " . $attrs['name']); + $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ])); + } +} + +/** + * Backward compatibility + */ +class XMLSchema extends nusoap_xmlschema { +} + + +?> \ No newline at end of file diff --git a/classes/lib/nusoap.php b/classes/lib/nusoap.php new file mode 100644 index 0000000..4973532 --- /dev/null +++ b/classes/lib/nusoap.php @@ -0,0 +1,8148 @@ + +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_base { + /** + * Identification for HTTP headers. + * + * @var string + * @access private + */ + var $title = 'NuSOAP'; + /** + * Version for HTTP headers. + * + * @var string + * @access private + */ + var $version = '0.9.5'; + /** + * CVS revision for HTTP headers. + * + * @var string + * @access private + */ + var $revision = '$Revision: 1.123 $'; + /** + * Current error string (manipulated by getError/setError) + * + * @var string + * @access private + */ + var $error_str = ''; + /** + * Current debug string (manipulated by debug/appendDebug/clearDebug/getDebug/getDebugAsXMLComment) + * + * @var string + * @access private + */ + var $debug_str = ''; + /** + * toggles automatic encoding of special characters as entities + * (should always be true, I think) + * + * @var boolean + * @access private + */ + var $charencoding = true; + /** + * the debug level for this instance + * + * @var integer + * @access private + */ + var $debugLevel; + + /** + * set schema version + * + * @var string + * @access public + */ + var $XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema'; + + /** + * charset encoding for outgoing messages + * + * @var string + * @access public + */ + var $soap_defencoding = 'ISO-8859-1'; + //var $soap_defencoding = 'UTF-8'; + + /** + * namespaces in an array of prefix => uri + * + * this is "seeded" by a set of constants, but it may be altered by code + * + * @var array + * @access public + */ + var $namespaces = array( + 'SOAP-ENV' => 'http://schemas.xmlsoap.org/soap/envelope/', + 'xsd' => 'http://www.w3.org/2001/XMLSchema', + 'xsi' => 'http://www.w3.org/2001/XMLSchema-instance', + 'SOAP-ENC' => 'http://schemas.xmlsoap.org/soap/encoding/' + ); + + /** + * namespaces used in the current context, e.g. during serialization + * + * @var array + * @access private + */ + var $usedNamespaces = array(); + + /** + * XML Schema types in an array of uri => (array of xml type => php type) + * is this legacy yet? + * no, this is used by the nusoap_xmlschema class to verify type => namespace mappings. + * @var array + * @access public + */ + var $typemap = array( + 'http://www.w3.org/2001/XMLSchema' => array( + 'string'=>'string','boolean'=>'boolean','float'=>'double','double'=>'double','decimal'=>'double', + 'duration'=>'','dateTime'=>'string','time'=>'string','date'=>'string','gYearMonth'=>'', + 'gYear'=>'','gMonthDay'=>'','gDay'=>'','gMonth'=>'','hexBinary'=>'string','base64Binary'=>'string', + // abstract "any" types + 'anyType'=>'string','anySimpleType'=>'string', + // derived datatypes + 'normalizedString'=>'string','token'=>'string','language'=>'','NMTOKEN'=>'','NMTOKENS'=>'','Name'=>'','NCName'=>'','ID'=>'', + 'IDREF'=>'','IDREFS'=>'','ENTITY'=>'','ENTITIES'=>'','integer'=>'integer','nonPositiveInteger'=>'integer', + 'negativeInteger'=>'integer','long'=>'integer','int'=>'integer','short'=>'integer','byte'=>'integer','nonNegativeInteger'=>'integer', + 'unsignedLong'=>'','unsignedInt'=>'','unsignedShort'=>'','unsignedByte'=>'','positiveInteger'=>''), + 'http://www.w3.org/2000/10/XMLSchema' => array( + 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', + 'float'=>'double','dateTime'=>'string', + 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), + 'http://www.w3.org/1999/XMLSchema' => array( + 'i4'=>'','int'=>'integer','boolean'=>'boolean','string'=>'string','double'=>'double', + 'float'=>'double','dateTime'=>'string', + 'timeInstant'=>'string','base64Binary'=>'string','base64'=>'string','ur-type'=>'array'), + 'http://soapinterop.org/xsd' => array('SOAPStruct'=>'struct'), + 'http://schemas.xmlsoap.org/soap/encoding/' => array('base64'=>'string','array'=>'array','Array'=>'array'), + 'http://xml.apache.org/xml-soap' => array('Map') + ); + + /** + * XML entities to convert + * + * @var array + * @access public + * @deprecated + * @see expandEntities + */ + var $xmlEntities = array('quot' => '"','amp' => '&', + 'lt' => '<','gt' => '>','apos' => "'"); + + /** + * constructor + * + * @access public + */ + function nusoap_base() { + $this->debugLevel = $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel']; + } + + /** + * gets the global debug level, which applies to future instances + * + * @return integer Debug level 0-9, where 0 turns off + * @access public + */ + function getGlobalDebugLevel() { + return $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel']; + } + + /** + * sets the global debug level, which applies to future instances + * + * @param int $level Debug level 0-9, where 0 turns off + * @access public + */ + function setGlobalDebugLevel($level) { + $GLOBALS['_transient']['static']['nusoap_base']['globalDebugLevel'] = $level; + } + + /** + * gets the debug level for this instance + * + * @return int Debug level 0-9, where 0 turns off + * @access public + */ + function getDebugLevel() { + return $this->debugLevel; + } + + /** + * sets the debug level for this instance + * + * @param int $level Debug level 0-9, where 0 turns off + * @access public + */ + function setDebugLevel($level) { + $this->debugLevel = $level; + } + + /** + * adds debug data to the instance debug string with formatting + * + * @param string $string debug data + * @access private + */ + function debug($string){ + if ($this->debugLevel > 0) { + $this->appendDebug($this->getmicrotime().' '.get_class($this).": $string\n"); + } + } + + /** + * adds debug data to the instance debug string without formatting + * + * @param string $string debug data + * @access public + */ + function appendDebug($string){ + if ($this->debugLevel > 0) { + // it would be nice to use a memory stream here to use + // memory more efficiently + $this->debug_str .= $string; + } + } + + /** + * clears the current debug data for this instance + * + * @access public + */ + function clearDebug() { + // it would be nice to use a memory stream here to use + // memory more efficiently + $this->debug_str = ''; + } + + /** + * gets the current debug data for this instance + * + * @return debug data + * @access public + */ + function &getDebug() { + // it would be nice to use a memory stream here to use + // memory more efficiently + return $this->debug_str; + } + + /** + * gets the current debug data for this instance as an XML comment + * this may change the contents of the debug data + * + * @return debug data as an XML comment + * @access public + */ + function &getDebugAsXMLComment() { + // it would be nice to use a memory stream here to use + // memory more efficiently + while (strpos($this->debug_str, '--')) { + $this->debug_str = str_replace('--', '- -', $this->debug_str); + } + $ret = ""; + return $ret; + } + + /** + * expands entities, e.g. changes '<' to '<'. + * + * @param string $val The string in which to expand entities. + * @access private + */ + function expandEntities($val) { + if ($this->charencoding) { + $val = str_replace('&', '&', $val); + $val = str_replace("'", ''', $val); + $val = str_replace('"', '"', $val); + $val = str_replace('<', '<', $val); + $val = str_replace('>', '>', $val); + } + return $val; + } + + /** + * returns error string if present + * + * @return mixed error string or false + * @access public + */ + function getError(){ + if($this->error_str != ''){ + return $this->error_str; + } + return false; + } + + /** + * sets error string + * + * @return boolean $string error string + * @access private + */ + function setError($str){ + $this->error_str = $str; + } + + /** + * detect if array is a simple array or a struct (associative array) + * + * @param mixed $val The PHP array + * @return string (arraySimple|arrayStruct) + * @access private + */ + function isArraySimpleOrStruct($val) { + $keyList = array_keys($val); + foreach ($keyList as $keyListValue) { + if (!is_int($keyListValue)) { + return 'arrayStruct'; + } + } + return 'arraySimple'; + } + + /** + * serializes PHP values in accordance w/ section 5. Type information is + * not serialized if $use == 'literal'. + * + * @param mixed $val The value to serialize + * @param string $name The name (local part) of the XML element + * @param string $type The XML schema type (local part) for the element + * @param string $name_ns The namespace for the name of the XML element + * @param string $type_ns The namespace for the type of the element + * @param array $attributes The attributes to serialize as name=>value pairs + * @param string $use The WSDL "use" (encoded|literal) + * @param boolean $soapval Whether this is called from soapval. + * @return string The serialized element, possibly with child elements + * @access public + */ + function serialize_val($val,$name=false,$type=false,$name_ns=false,$type_ns=false,$attributes=false,$use='encoded',$soapval=false) { + $this->debug("in serialize_val: name=$name, type=$type, name_ns=$name_ns, type_ns=$type_ns, use=$use, soapval=$soapval"); + $this->appendDebug('value=' . $this->varDump($val)); + $this->appendDebug('attributes=' . $this->varDump($attributes)); + + if (is_object($val) && get_class($val) == 'soapval' && (! $soapval)) { + $this->debug("serialize_val: serialize soapval"); + $xml = $val->serialize($use); + $this->appendDebug($val->getDebug()); + $val->clearDebug(); + $this->debug("serialize_val of soapval returning $xml"); + return $xml; + } + // force valid name if necessary + if (is_numeric($name)) { + $name = '__numeric_' . $name; + } elseif (! $name) { + $name = 'noname'; + } + // if name has ns, add ns prefix to name + $xmlns = ''; + if($name_ns){ + $prefix = 'nu'.rand(1000,9999); + $name = $prefix.':'.$name; + $xmlns .= " xmlns:$prefix=\"$name_ns\""; + } + // if type is prefixed, create type prefix + if($type_ns != '' && $type_ns == $this->namespaces['xsd']){ + // need to fix this. shouldn't default to xsd if no ns specified + // w/o checking against typemap + $type_prefix = 'xsd'; + } elseif($type_ns){ + $type_prefix = 'ns'.rand(1000,9999); + $xmlns .= " xmlns:$type_prefix=\"$type_ns\""; + } + // serialize attributes if present + $atts = ''; + if($attributes){ + foreach($attributes as $k => $v){ + $atts .= " $k=\"".$this->expandEntities($v).'"'; + } + } + // serialize null value + if (is_null($val)) { + $this->debug("serialize_val: serialize null"); + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$xmlns$atts/>"; + $this->debug("serialize_val returning $xml"); + return $xml; + } else { + if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + $xml = "<$name$xmlns$type_str$atts xsi:nil=\"true\"/>"; + $this->debug("serialize_val returning $xml"); + return $xml; + } + } + // serialize if an xsd built-in primitive type + if($type != '' && isset($this->typemap[$this->XMLSchemaVersion][$type])){ + $this->debug("serialize_val: serialize xsd built-in primitive type"); + if (is_bool($val)) { + if ($type == 'boolean') { + $val = $val ? 'true' : 'false'; + } elseif (! $val) { + $val = 0; + } + } else if (is_string($val)) { + $val = $this->expandEntities($val); + } + if ($use == 'literal') { + $xml = "<$name$xmlns$atts>$val"; + $this->debug("serialize_val returning $xml"); + return $xml; + } else { + $xml = "<$name$xmlns xsi:type=\"xsd:$type\"$atts>$val"; + $this->debug("serialize_val returning $xml"); + return $xml; + } + } + // detect type and serialize + $xml = ''; + switch(true) { + case (is_bool($val) || $type == 'boolean'): + $this->debug("serialize_val: serialize boolean"); + if ($type == 'boolean') { + $val = $val ? 'true' : 'false'; + } elseif (! $val) { + $val = 0; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:boolean\"$atts>$val"; + } + break; + case (is_int($val) || is_long($val) || $type == 'int'): + $this->debug("serialize_val: serialize int"); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:int\"$atts>$val"; + } + break; + case (is_float($val)|| is_double($val) || $type == 'float'): + $this->debug("serialize_val: serialize float"); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:float\"$atts>$val"; + } + break; + case (is_string($val) || $type == 'string'): + $this->debug("serialize_val: serialize string"); + $val = $this->expandEntities($val); + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$val"; + } else { + $xml .= "<$name$xmlns xsi:type=\"xsd:string\"$atts>$val"; + } + break; + case is_object($val): + $this->debug("serialize_val: serialize object"); + if (get_class($val) == 'soapval') { + $this->debug("serialize_val: serialize soapval object"); + $pXml = $val->serialize($use); + $this->appendDebug($val->getDebug()); + $val->clearDebug(); + } else { + if (! $name) { + $name = get_class($val); + $this->debug("In serialize_val, used class name $name as element name"); + } else { + $this->debug("In serialize_val, do not override name $name for element name for class " . get_class($val)); + } + foreach(get_object_vars($val) as $k => $v){ + $pXml = isset($pXml) ? $pXml.$this->serialize_val($v,$k,false,false,false,false,$use) : $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + if(isset($type) && isset($type_prefix)){ + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>$pXml"; + } else { + $xml .= "<$name$xmlns$type_str$atts>$pXml"; + } + break; + break; + case (is_array($val) || $type): + // detect if struct or array + $valueType = $this->isArraySimpleOrStruct($val); + if($valueType=='arraySimple' || preg_match('/^ArrayOf/',$type)){ + $this->debug("serialize_val: serialize array"); + $i = 0; + if(is_array($val) && count($val)> 0){ + foreach($val as $v){ + if(is_object($v) && get_class($v) == 'soapval'){ + $tt_ns = $v->type_ns; + $tt = $v->type; + } elseif (is_array($v)) { + $tt = $this->isArraySimpleOrStruct($v); + } else { + $tt = gettype($v); + } + $array_types[$tt] = 1; + // TODO: for literal, the name should be $name + $xml .= $this->serialize_val($v,'item',false,false,false,false,$use); + ++$i; + } + if(count($array_types) > 1){ + $array_typename = 'xsd:anyType'; + } elseif(isset($tt) && isset($this->typemap[$this->XMLSchemaVersion][$tt])) { + if ($tt == 'integer') { + $tt = 'int'; + } + $array_typename = 'xsd:'.$tt; + } elseif(isset($tt) && $tt == 'arraySimple'){ + $array_typename = 'SOAP-ENC:Array'; + } elseif(isset($tt) && $tt == 'arrayStruct'){ + $array_typename = 'unnamed_struct_use_soapval'; + } else { + // if type is prefixed, create type prefix + if ($tt_ns != '' && $tt_ns == $this->namespaces['xsd']){ + $array_typename = 'xsd:' . $tt; + } elseif ($tt_ns) { + $tt_prefix = 'ns' . rand(1000, 9999); + $array_typename = "$tt_prefix:$tt"; + $xmlns .= " xmlns:$tt_prefix=\"$tt_ns\""; + } else { + $array_typename = $tt; + } + } + $array_type = $i; + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"".$array_typename."[$array_type]\""; + } + // empty array + } else { + if ($use == 'literal') { + $type_str = ''; + } else if (isset($type) && isset($type_prefix)) { + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = " xsi:type=\"SOAP-ENC:Array\" SOAP-ENC:arrayType=\"xsd:anyType[0]\""; + } + } + // TODO: for array in literal, there is no wrapper here + $xml = "<$name$xmlns$type_str$atts>".$xml.""; + } else { + // got a struct + $this->debug("serialize_val: serialize struct"); + if(isset($type) && isset($type_prefix)){ + $type_str = " xsi:type=\"$type_prefix:$type\""; + } else { + $type_str = ''; + } + if ($use == 'literal') { + $xml .= "<$name$xmlns$atts>"; + } else { + $xml .= "<$name$xmlns$type_str$atts>"; + } + foreach($val as $k => $v){ + // Apache Map + if ($type == 'Map' && $type_ns == 'http://xml.apache.org/xml-soap') { + $xml .= ''; + $xml .= $this->serialize_val($k,'key',false,false,false,false,$use); + $xml .= $this->serialize_val($v,'value',false,false,false,false,$use); + $xml .= ''; + } else { + $xml .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } + $xml .= ""; + } + break; + default: + $this->debug("serialize_val: serialize unknown"); + $xml .= 'not detected, got '.gettype($val).' for '.$val; + break; + } + $this->debug("serialize_val returning $xml"); + return $xml; + } + + /** + * serializes a message + * + * @param string $body the XML of the SOAP body + * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array + * @param array $namespaces optional the namespaces used in generating the body and headers + * @param string $style optional (rpc|document) + * @param string $use optional (encoded|literal) + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @return string the message + * @access public + */ + function serializeEnvelope($body,$headers=false,$namespaces=array(),$style='rpc',$use='encoded',$encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'){ + // TODO: add an option to automatically run utf8_encode on $body and $headers + // if $this->soap_defencoding is UTF-8. Not doing this automatically allows + // one to send arbitrary UTF-8 characters, not just characters that map to ISO-8859-1 + + $this->debug("In serializeEnvelope length=" . strlen($body) . " body (max 1000 characters)=" . substr($body, 0, 1000) . " style=$style use=$use encodingStyle=$encodingStyle"); + $this->debug("headers:"); + $this->appendDebug($this->varDump($headers)); + $this->debug("namespaces:"); + $this->appendDebug($this->varDump($namespaces)); + + // serialize namespaces + $ns_string = ''; + foreach(array_merge($this->namespaces,$namespaces) as $k => $v){ + $ns_string .= " xmlns:$k=\"$v\""; + } + if($encodingStyle) { + $ns_string = " SOAP-ENV:encodingStyle=\"$encodingStyle\"$ns_string"; + } + + // serialize headers + if($headers){ + if (is_array($headers)) { + $xml = ''; + foreach ($headers as $k => $v) { + if (is_object($v) && get_class($v) == 'soapval') { + $xml .= $this->serialize_val($v, false, false, false, false, false, $use); + } else { + $xml .= $this->serialize_val($v, $k, false, false, false, false, $use); + } + } + $headers = $xml; + $this->debug("In serializeEnvelope, serialized array of headers to $headers"); + } + $headers = "".$headers.""; + } + // serialize envelope + return + 'soap_defencoding .'"?'.">". + '". + $headers. + "". + $body. + "". + ""; + } + + /** + * formats a string to be inserted into an HTML stream + * + * @param string $str The string to format + * @return string The formatted string + * @access public + * @deprecated + */ + function formatDump($str){ + $str = htmlspecialchars($str); + return nl2br($str); + } + + /** + * contracts (changes namespace to prefix) a qualified name + * + * @param string $qname qname + * @return string contracted qname + * @access private + */ + function contractQname($qname){ + // get element namespace + //$this->xdebug("Contract $qname"); + if (strrpos($qname, ':')) { + // get unqualified name + $name = substr($qname, strrpos($qname, ':') + 1); + // get ns + $ns = substr($qname, 0, strrpos($qname, ':')); + $p = $this->getPrefixFromNamespace($ns); + if ($p) { + return $p . ':' . $name; + } + return $qname; + } else { + return $qname; + } + } + + /** + * expands (changes prefix to namespace) a qualified name + * + * @param string $qname qname + * @return string expanded qname + * @access private + */ + function expandQname($qname){ + // get element prefix + if(strpos($qname,':') && !preg_match('/^http:\/\//',$qname)){ + // get unqualified name + $name = substr(strstr($qname,':'),1); + // get ns prefix + $prefix = substr($qname,0,strpos($qname,':')); + if(isset($this->namespaces[$prefix])){ + return $this->namespaces[$prefix].':'.$name; + } else { + return $qname; + } + } else { + return $qname; + } + } + + /** + * returns the local part of a prefixed string + * returns the original string, if not prefixed + * + * @param string $str The prefixed string + * @return string The local part + * @access public + */ + function getLocalPart($str){ + if($sstr = strrchr($str,':')){ + // get unqualified name + return substr( $sstr, 1 ); + } else { + return $str; + } + } + + /** + * returns the prefix part of a prefixed string + * returns false, if not prefixed + * + * @param string $str The prefixed string + * @return mixed The prefix or false if there is no prefix + * @access public + */ + function getPrefix($str){ + if($pos = strrpos($str,':')){ + // get prefix + return substr($str,0,$pos); + } + return false; + } + + /** + * pass it a prefix, it returns a namespace + * + * @param string $prefix The prefix + * @return mixed The namespace, false if no namespace has the specified prefix + * @access public + */ + function getNamespaceFromPrefix($prefix){ + if (isset($this->namespaces[$prefix])) { + return $this->namespaces[$prefix]; + } + //$this->setError("No namespace registered for prefix '$prefix'"); + return false; + } + + /** + * returns the prefix for a given namespace (or prefix) + * or false if no prefixes registered for the given namespace + * + * @param string $ns The namespace + * @return mixed The prefix, false if the namespace has no prefixes + * @access public + */ + function getPrefixFromNamespace($ns) { + foreach ($this->namespaces as $p => $n) { + if ($ns == $n || $ns == $p) { + $this->usedNamespaces[$p] = $n; + return $p; + } + } + return false; + } + + /** + * returns the time in ODBC canonical form with microseconds + * + * @return string The time in ODBC canonical form with microseconds + * @access public + */ + function getmicrotime() { + if (function_exists('gettimeofday')) { + $tod = gettimeofday(); + $sec = $tod['sec']; + $usec = $tod['usec']; + } else { + $sec = time(); + $usec = 0; + } + return strftime('%Y-%m-%d %H:%M:%S', $sec) . '.' . sprintf('%06d', $usec); + } + + /** + * Returns a string with the output of var_dump + * + * @param mixed $data The variable to var_dump + * @return string The output of var_dump + * @access public + */ + function varDump($data) { + ob_start(); + var_dump($data); + $ret_val = ob_get_contents(); + ob_end_clean(); + return $ret_val; + } + + /** + * represents the object as a string + * + * @return string + * @access public + */ + function __toString() { + return $this->varDump($this); + } +} + +// XML Schema Datatype Helper Functions + +//xsd:dateTime helpers + +/** +* convert unix timestamp to ISO 8601 compliant date string +* +* @param int $timestamp Unix time stamp +* @param boolean $utc Whether the time stamp is UTC or local +* @return mixed ISO 8601 date string or false +* @access public +*/ +function timestamp_to_iso8601($timestamp,$utc=true){ + $datestr = date('Y-m-d\TH:i:sO',$timestamp); + $pos = strrpos($datestr, "+"); + if ($pos === FALSE) { + $pos = strrpos($datestr, "-"); + } + if ($pos !== FALSE) { + if (strlen($datestr) == $pos + 5) { + $datestr = substr($datestr, 0, $pos + 3) . ':' . substr($datestr, -2); + } + } + if($utc){ + $pattern = '/'. + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]*)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + '/'; + + if(preg_match($pattern,$datestr,$regs)){ + return sprintf('%04d-%02d-%02dT%02d:%02d:%02dZ',$regs[1],$regs[2],$regs[3],$regs[4],$regs[5],$regs[6]); + } + return false; + } else { + return $datestr; + } +} + +/** +* convert ISO 8601 compliant date string to unix timestamp +* +* @param string $datestr ISO 8601 compliant date string +* @return mixed Unix timestamp (int) or false +* @access public +*/ +function iso8601_to_timestamp($datestr){ + $pattern = '/'. + '([0-9]{4})-'. // centuries & years CCYY- + '([0-9]{2})-'. // months MM- + '([0-9]{2})'. // days DD + 'T'. // separator T + '([0-9]{2}):'. // hours hh: + '([0-9]{2}):'. // minutes mm: + '([0-9]{2})(\.[0-9]+)?'. // seconds ss.ss... + '(Z|[+\-][0-9]{2}:?[0-9]{2})?'. // Z to indicate UTC, -/+HH:MM:SS.SS... for local tz's + '/'; + if(preg_match($pattern,$datestr,$regs)){ + // not utc + if($regs[8] != 'Z'){ + $op = substr($regs[8],0,1); + $h = substr($regs[8],1,2); + $m = substr($regs[8],strlen($regs[8])-2,2); + if($op == '-'){ + $regs[4] = $regs[4] + $h; + $regs[5] = $regs[5] + $m; + } elseif($op == '+'){ + $regs[4] = $regs[4] - $h; + $regs[5] = $regs[5] - $m; + } + } + return gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); +// return strtotime("$regs[1]-$regs[2]-$regs[3] $regs[4]:$regs[5]:$regs[6]Z"); + } else { + return false; + } +} + +/** +* sleeps some number of microseconds +* +* @param string $usec the number of microseconds to sleep +* @access public +* @deprecated +*/ +function usleepWindows($usec) +{ + $start = gettimeofday(); + + do + { + $stop = gettimeofday(); + $timePassed = 1000000 * ($stop['sec'] - $start['sec']) + + $stop['usec'] - $start['usec']; + } + while ($timePassed < $usec); +} + +?> +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_fault extends nusoap_base { + /** + * The fault code (client|server) + * @var string + * @access private + */ + var $faultcode; + /** + * The fault actor + * @var string + * @access private + */ + var $faultactor; + /** + * The fault string, a description of the fault + * @var string + * @access private + */ + var $faultstring; + /** + * The fault detail, typically a string or array of string + * @var mixed + * @access private + */ + var $faultdetail; + + /** + * constructor + * + * @param string $faultcode (SOAP-ENV:Client | SOAP-ENV:Server) + * @param string $faultactor only used when msg routed between multiple actors + * @param string $faultstring human readable error message + * @param mixed $faultdetail detail, typically a string or array of string + */ + function nusoap_fault($faultcode,$faultactor='',$faultstring='',$faultdetail=''){ + parent::nusoap_base(); + $this->faultcode = $faultcode; + $this->faultactor = $faultactor; + $this->faultstring = $faultstring; + $this->faultdetail = $faultdetail; + } + + /** + * serialize a fault + * + * @return string The serialization of the fault instance. + * @access public + */ + function serialize(){ + $ns_string = ''; + foreach($this->namespaces as $k => $v){ + $ns_string .= "\n xmlns:$k=\"$v\""; + } + $return_msg = + 'soap_defencoding.'"?>'. + '\n". + ''. + ''. + $this->serialize_val($this->faultcode, 'faultcode'). + $this->serialize_val($this->faultactor, 'faultactor'). + $this->serialize_val($this->faultstring, 'faultstring'). + $this->serialize_val($this->faultdetail, 'detail'). + ''. + ''. + ''; + return $return_msg; + } +} + +/** + * Backward compatibility + */ +class soap_fault extends nusoap_fault { +} + +?> +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_xmlschema extends nusoap_base { + + // files + var $schema = ''; + var $xml = ''; + // namespaces + var $enclosingNamespaces; + // schema info + var $schemaInfo = array(); + var $schemaTargetNamespace = ''; + // types, elements, attributes defined by the schema + var $attributes = array(); + var $complexTypes = array(); + var $complexTypeStack = array(); + var $currentComplexType = null; + var $elements = array(); + var $elementStack = array(); + var $currentElement = null; + var $simpleTypes = array(); + var $simpleTypeStack = array(); + var $currentSimpleType = null; + // imports + var $imports = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + var $message = array(); + var $defaultNamespace = array(); + + /** + * constructor + * + * @param string $schema schema document URI + * @param string $xml xml document URI + * @param string $namespaces namespaces defined in enclosing XML + * @access public + */ + function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){ + parent::nusoap_base(); + $this->debug('nusoap_xmlschema class instantiated, inside constructor'); + // files + $this->schema = $schema; + $this->xml = $xml; + + // namespaces + $this->enclosingNamespaces = $namespaces; + $this->namespaces = array_merge($this->namespaces, $namespaces); + + // parse schema file + if($schema != ''){ + $this->debug('initial schema file: '.$schema); + $this->parseFile($schema, 'schema'); + } + + // parse xml file + if($xml != ''){ + $this->debug('initial xml file: '.$xml); + $this->parseFile($xml, 'xml'); + } + + } + + /** + * parse an XML file + * + * @param string $xml path/URL to XML file + * @param string $type (schema | xml) + * @return boolean + * @access public + */ + function parseFile($xml,$type){ + // parse xml file + if($xml != ""){ + $xmlStr = @join("",@file($xml)); + if($xmlStr == ""){ + $msg = 'Error reading XML from '.$xml; + $this->setError($msg); + $this->debug($msg); + return false; + } else { + $this->debug("parsing $xml"); + $this->parseString($xmlStr,$type); + $this->debug("done parsing $xml"); + return true; + } + } + return false; + } + + /** + * parse an XML string + * + * @param string $xml path or URL + * @param string $type (schema|xml) + * @access private + */ + function parseString($xml,$type){ + // parse xml string + if($xml != ""){ + + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + + // Set the object for the parser. + xml_set_object($this->parser, $this); + + // Set the element handlers for the parser. + if($type == "schema"){ + xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); + xml_set_character_data_handler($this->parser,'schemaCharacterData'); + } elseif($type == "xml"){ + xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); + xml_set_character_data_handler($this->parser,'xmlCharacterData'); + } + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $errstr = sprintf('XML error parsing XML schema on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $xml); + $this->setError($errstr); + } + + xml_parser_free($this->parser); + } else{ + $this->debug('no xml passed to parseString()!!'); + $this->setError('no xml passed to parseString()!!'); + } + } + + /** + * gets a type name for an unnamed type + * + * @param string Element name + * @return string A type name for an unnamed type + * @access private + */ + function CreateTypeName($ename) { + $scope = ''; + for ($i = 0; $i < count($this->complexTypeStack); $i++) { + $scope .= $this->complexTypeStack[$i] . '_'; + } + return $scope . $ename . '_ContainedType'; + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function schemaStartElement($parser, $name, $attrs) { + + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + if ($depth > 0) { + $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]]; + } else { + $this->defaultNamespace[$pos] = false; + } + + // get element prefix + if($prefix = $this->getPrefix($name)){ + // get unqualified name + $name = $this->getLocalPart($name); + } else { + $prefix = ''; + } + + // loop thru attributes, expanding, and registering namespace declarations + if(count($attrs) > 0){ + foreach($attrs as $k => $v){ + // if ns declarations, add to class level array of valid namespaces + if(preg_match('/^xmlns/',$k)){ + //$this->xdebug("$k: $v"); + //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); + if($ns_prefix = substr(strrchr($k,':'),1)){ + //$this->xdebug("Add namespace[$ns_prefix] = $v"); + $this->namespaces[$ns_prefix] = $v; + } else { + $this->defaultNamespace[$pos] = $v; + if (! $this->getPrefixFromNamespace($v)) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; + } + } + if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){ + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v.'-instance'; + } + } + } + foreach($attrs as $k => $v){ + // expand each attribute + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // find status, register data + switch($name){ + case 'all': // (optional) compositor content for a complexType + case 'choice': + case 'group': + case 'sequence': + //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); + $this->complexTypes[$this->currentComplexType]['compositor'] = $name; + //if($name == 'all' || $name == 'sequence'){ + // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + //} + break; + case 'attribute': // complexType attribute + //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); + $this->xdebug("parsing attribute:"); + $this->appendDebug($this->varDump($attrs)); + if (!isset($attrs['form'])) { + // TODO: handle globals + $attrs['form'] = $this->schemaInfo['attributeFormDefault']; + } + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + if (!strpos($v, ':')) { + // no namespace in arrayType attribute value... + if ($this->defaultNamespace[$pos]) { + // ...so use the default + $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } + } + } + if(isset($attrs['name'])){ + $this->attributes[$attrs['name']] = $attrs; + $aname = $attrs['name']; + } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ + if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { + $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $aname = ''; + } + } elseif(isset($attrs['ref'])){ + $aname = $attrs['ref']; + $this->attributes[$attrs['ref']] = $attrs; + } + + if($this->currentComplexType){ // This should *always* be + $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; + } + // arrayType attribute + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + $prefix = $this->getPrefix($aname); + if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ + $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; + } else { + $v = ''; + } + if(strpos($v,'[,]')){ + $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; + } + $v = substr($v,0,strpos($v,'[')); // clip the [] + if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ + $v = $this->XMLSchemaVersion.':'.$v; + } + $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; + } + break; + case 'complexContent': // (optional) content for a complexType + $this->xdebug("do nothing for element $name"); + break; + case 'complexType': + array_push($this->complexTypeStack, $this->currentComplexType); + if(isset($attrs['name'])){ + // TODO: what is the scope of named complexTypes that appear + // nested within other c complexTypes? + $this->xdebug('processing named complexType '.$attrs['name']); + //$this->currentElement = false; + $this->currentComplexType = $attrs['name']; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + // This is for constructs like + // + // + // + // + // + if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ + $this->xdebug('complexType is unusual array'); + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + } else { + $name = $this->CreateTypeName($this->currentElement); + $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name); + $this->currentComplexType = $name; + //$this->currentElement = false; + $this->complexTypes[$this->currentComplexType] = $attrs; + $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; + // This is for constructs like + // + // + // + // + // + if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ + $this->xdebug('complexType is unusual array'); + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } else { + $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; + } + } + $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false'; + break; + case 'element': + array_push($this->elementStack, $this->currentElement); + if (!isset($attrs['form'])) { + if ($this->currentComplexType) { + $attrs['form'] = $this->schemaInfo['elementFormDefault']; + } else { + // global + $attrs['form'] = 'qualified'; + } + } + if(isset($attrs['type'])){ + $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']); + if (! $this->getPrefix($attrs['type'])) { + if ($this->defaultNamespace[$pos]) { + $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type']; + $this->xdebug('used default namespace to make type ' . $attrs['type']); + } + } + // This is for constructs like + // + // + // + // + // + if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') { + $this->xdebug('arrayType for unusual array is ' . $attrs['type']); + $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type']; + } + $this->currentElement = $attrs['name']; + $ename = $attrs['name']; + } elseif(isset($attrs['ref'])){ + $this->xdebug("processing element as ref to ".$attrs['ref']); + $this->currentElement = "ref to ".$attrs['ref']; + $ename = $this->getLocalPart($attrs['ref']); + } else { + $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']); + $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type); + $this->currentElement = $attrs['name']; + $attrs['type'] = $this->schemaTargetNamespace . ':' . $type; + $ename = $attrs['name']; + } + if (isset($ename) && $this->currentComplexType) { + $this->xdebug("add element $ename to complexType $this->currentComplexType"); + $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; + } elseif (!isset($attrs['ref'])) { + $this->xdebug("add element $ename to elements array"); + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + } + break; + case 'enumeration': // restriction value list member + $this->xdebug('enumeration ' . $attrs['value']); + if ($this->currentSimpleType) { + $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value']; + } elseif ($this->currentComplexType) { + $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value']; + } + break; + case 'extension': // simpleContent or complexContent type extension + $this->xdebug('extension ' . $attrs['base']); + if ($this->currentComplexType) { + $ns = $this->getPrefix($attrs['base']); + if ($ns == '') { + $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base']; + } else { + $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base']; + } + } else { + $this->xdebug('no current complexType to set extensionBase'); + } + break; + case 'import': + if (isset($attrs['schemaLocation'])) { + $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']); + $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); + } else { + $this->xdebug('import namespace ' . $attrs['namespace']); + $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + } + break; + case 'include': + if (isset($attrs['schemaLocation'])) { + $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']); + $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); + } else { + $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute'); + } + break; + case 'list': // simpleType value list + $this->xdebug("do nothing for element $name"); + break; + case 'restriction': // simpleType, simpleContent or complexContent value restriction + $this->xdebug('restriction ' . $attrs['base']); + if($this->currentSimpleType){ + $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base']; + } elseif($this->currentComplexType){ + $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; + if(strstr($attrs['base'],':') == ':Array'){ + $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; + } + } + break; + case 'schema': + $this->schemaInfo = $attrs; + $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); + if (isset($attrs['targetNamespace'])) { + $this->schemaTargetNamespace = $attrs['targetNamespace']; + } + if (!isset($attrs['elementFormDefault'])) { + $this->schemaInfo['elementFormDefault'] = 'unqualified'; + } + if (!isset($attrs['attributeFormDefault'])) { + $this->schemaInfo['attributeFormDefault'] = 'unqualified'; + } + break; + case 'simpleContent': // (optional) content for a complexType + if ($this->currentComplexType) { // This should *always* be + $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true'; + } else { + $this->xdebug("do nothing for element $name because there is no current complexType"); + } + break; + case 'simpleType': + array_push($this->simpleTypeStack, $this->currentSimpleType); + if(isset($attrs['name'])){ + $this->xdebug("processing simpleType for name " . $attrs['name']); + $this->currentSimpleType = $attrs['name']; + $this->simpleTypes[ $attrs['name'] ] = $attrs; + $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType'; + $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar'; + } else { + $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement); + $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name); + $this->currentSimpleType = $name; + //$this->currentElement = false; + $this->simpleTypes[$this->currentSimpleType] = $attrs; + $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar'; + } + break; + case 'union': // simpleType type list + $this->xdebug("do nothing for element $name"); + break; + default: + $this->xdebug("do not have any logic to process element $name"); + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function schemaEndElement($parser, $name) { + // bring depth down a notch + $this->depth--; + // position of current element is equal to the last value left in depth_array for my depth + if(isset($this->depth_array[$this->depth])){ + $pos = $this->depth_array[$this->depth]; + } + // get element prefix + if ($prefix = $this->getPrefix($name)){ + // get unqualified name + $name = $this->getLocalPart($name); + } else { + $prefix = ''; + } + // move on... + if($name == 'complexType'){ + $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)')); + $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType])); + $this->currentComplexType = array_pop($this->complexTypeStack); + //$this->currentElement = false; + } + if($name == 'element'){ + $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)')); + $this->currentElement = array_pop($this->elementStack); + } + if($name == 'simpleType'){ + $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)')); + $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType])); + $this->currentSimpleType = array_pop($this->simpleTypeStack); + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function schemaCharacterData($parser, $data){ + $pos = $this->depth_array[$this->depth - 1]; + $this->message[$pos]['cdata'] .= $data; + } + + /** + * serialize the schema + * + * @access public + */ + function serializeSchema(){ + + $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); + $xml = ''; + // imports + if (sizeof($this->imports) > 0) { + foreach($this->imports as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n"; + } else { + $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n"; + } + } + } + } + // complex types + foreach($this->complexTypes as $typeName => $attrs){ + $contentStr = ''; + // serialize child elements + if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){ + foreach($attrs['elements'] as $element => $eParts){ + if(isset($eParts['ref'])){ + $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n"; + } else { + $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\""; + foreach ($eParts as $aName => $aValue) { + // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable + if ($aName != 'name' && $aName != 'type') { + $contentStr .= " $aName=\"$aValue\""; + } + } + $contentStr .= "/>\n"; + } + } + // compositor wraps elements + if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) { + $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." \n"; + } + } + // attributes + if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){ + foreach($attrs['attrs'] as $attr => $aParts){ + $contentStr .= " <$schemaPrefix:attribute"; + foreach ($aParts as $a => $v) { + if ($a == 'ref' || $a == 'type') { + $contentStr .= " $a=\"".$this->contractQName($v).'"'; + } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') { + $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl']; + $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"'; + } else { + $contentStr .= " $a=\"$v\""; + } + } + $contentStr .= "/>\n"; + } + } + // if restriction + if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){ + $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." \n"; + // complex or simple content + if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){ + $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." \n"; + } + } + // finalize complex type + if($contentStr != ''){ + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." \n"; + } else { + $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n"; + } + $xml .= $contentStr; + } + // simple types + if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){ + foreach($this->simpleTypes as $typeName => $eParts){ + $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n"; + if (isset($eParts['enumeration'])) { + foreach ($eParts['enumeration'] as $e) { + $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n"; + } + } + $xml .= " \n "; + } + } + // elements + if(isset($this->elements) && count($this->elements) > 0){ + foreach($this->elements as $element => $eParts){ + $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n"; + } + } + // attributes + if(isset($this->attributes) && count($this->attributes) > 0){ + foreach($this->attributes as $attr => $aParts){ + $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>"; + } + } + // finish 'er up + $attr = ''; + foreach ($this->schemaInfo as $k => $v) { + if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') { + $attr .= " $k=\"$v\""; + } + } + $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n"; + foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) { + $el .= " xmlns:$nsp=\"$ns\""; + } + $xml = $el . ">\n".$xml."\n"; + return $xml; + } + + /** + * adds debug data to the clas level debug string + * + * @param string $string debug data + * @access private + */ + function xdebug($string){ + $this->debug('<' . $this->schemaTargetNamespace . '> '.$string); + } + + /** + * get the PHP type of a user defined type in the schema + * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays + * returns false if no type exists, or not w/ the given namespace + * else returns a string that is either a native php type, or 'struct' + * + * @param string $type name of defined type + * @param string $ns namespace of type + * @return mixed + * @access public + * @deprecated + */ + function getPHPType($type,$ns){ + if(isset($this->typemap[$ns][$type])){ + //print "found type '$type' and ns $ns in typemap
"; + return $this->typemap[$ns][$type]; + } elseif(isset($this->complexTypes[$type])){ + //print "getting type '$type' and ns $ns from complexTypes array
"; + return $this->complexTypes[$type]['phpType']; + } + return false; + } + + /** + * returns an associative array of information about a given type + * returns false if no type exists by the given name + * + * For a complexType typeDef = array( + * 'restrictionBase' => '', + * 'phpType' => '', + * 'compositor' => '(sequence|all)', + * 'elements' => array(), // refs to elements array + * 'attrs' => array() // refs to attributes array + * ... and so on (see addComplexType) + * ) + * + * For simpleType or element, the array has different keys. + * + * @param string $type + * @return mixed + * @access public + * @see addComplexType + * @see addSimpleType + * @see addElement + */ + function getTypeDef($type){ + //$this->debug("in getTypeDef for type $type"); + if (substr($type, -1) == '^') { + $is_element = 1; + $type = substr($type, 0, -1); + } else { + $is_element = 0; + } + + if((! $is_element) && isset($this->complexTypes[$type])){ + $this->xdebug("in getTypeDef, found complexType $type"); + return $this->complexTypes[$type]; + } elseif((! $is_element) && isset($this->simpleTypes[$type])){ + $this->xdebug("in getTypeDef, found simpleType $type"); + if (!isset($this->simpleTypes[$type]['phpType'])) { + // get info for type to tack onto the simple type + // TODO: can this ever really apply (i.e. what is a simpleType really?) + $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1); + $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + $this->xdebug("in getTypeDef, found type for simpleType $type:"); + $this->xdebug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $this->simpleTypes[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->simpleTypes[$type]['elements'] = $etype['elements']; + } + } + } + return $this->simpleTypes[$type]; + } elseif(isset($this->elements[$type])){ + $this->xdebug("in getTypeDef, found element $type"); + if (!isset($this->elements[$type]['phpType'])) { + // get info for type to tack onto the element + $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1); + $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':')); + $etype = $this->getTypeDef($uqType); + if ($etype) { + $this->xdebug("in getTypeDef, found type for element $type:"); + $this->xdebug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $this->elements[$type]['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $this->elements[$type]['elements'] = $etype['elements']; + } + if (isset($etype['extensionBase'])) { + $this->elements[$type]['extensionBase'] = $etype['extensionBase']; + } + } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') { + $this->xdebug("in getTypeDef, element $type is an XSD type"); + $this->elements[$type]['phpType'] = 'scalar'; + } + } + return $this->elements[$type]; + } elseif(isset($this->attributes[$type])){ + $this->xdebug("in getTypeDef, found attribute $type"); + return $this->attributes[$type]; + } elseif (preg_match('/_ContainedType$/', $type)) { + $this->xdebug("in getTypeDef, have an untyped element $type"); + $typeDef['typeClass'] = 'simpleType'; + $typeDef['phpType'] = 'scalar'; + $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string'; + return $typeDef; + } + $this->xdebug("in getTypeDef, did not find $type"); + return false; + } + + /** + * returns a sample serialization of a given type, or false if no type by the given name + * + * @param string $type name of type + * @return mixed + * @access public + * @deprecated + */ + function serializeTypeDef($type){ + //print "in sTD() for type $type
"; + if($typeDef = $this->getTypeDef($type)){ + $str .= '<'.$type; + if(is_array($typeDef['attrs'])){ + foreach($typeDef['attrs'] as $attName => $data){ + $str .= " $attName=\"{type = ".$data['type']."}\""; + } + } + $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; + if(count($typeDef['elements']) > 0){ + $str .= ">"; + foreach($typeDef['elements'] as $element => $eData){ + $str .= $this->serializeTypeDef($element); + } + $str .= ""; + } elseif($typeDef['typeClass'] == 'element') { + $str .= ">"; + } else { + $str .= "/>"; + } + return $str; + } + return false; + } + + /** + * returns HTML form elements that allow a user + * to enter values for creating an instance of the given type. + * + * @param string $name name for type instance + * @param string $type name of type + * @return string + * @access public + * @deprecated + */ + function typeToForm($name,$type){ + // get typedef + if($typeDef = $this->getTypeDef($type)){ + // if struct + if($typeDef['phpType'] == 'struct'){ + $buffer .= ''; + foreach($typeDef['elements'] as $child => $childDef){ + $buffer .= " + + "; + } + $buffer .= '
$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):
'; + // if array + } elseif($typeDef['phpType'] == 'array'){ + $buffer .= ''; + for($i=0;$i < 3; $i++){ + $buffer .= " + + "; + } + $buffer .= '
array item (type: $typeDef[arrayType]):
'; + // if scalar + } else { + $buffer .= ""; + } + } else { + $buffer .= ""; + } + return $buffer; + } + + /** + * adds a complex type to the schema + * + * example: array + * + * addType( + * 'ArrayOfstring', + * 'complexType', + * 'array', + * '', + * 'SOAP-ENC:Array', + * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), + * 'xsd:string' + * ); + * + * example: PHP associative array ( SOAP Struct ) + * + * addType( + * 'SOAPStruct', + * 'complexType', + * 'struct', + * 'all', + * array('myVar'=> array('name'=>'myVar','type'=>'string') + * ); + * + * @param name + * @param typeClass (complexType|simpleType|attribute) + * @param phpType: currently supported are array and struct (php assoc array) + * @param compositor (all|sequence|choice) + * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param elements = array ( name = array(name=>'',type=>'') ) + * @param attrs = array( + * array( + * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", + * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" + * ) + * ) + * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) + * @access public + * @see getTypeDef + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ + $this->complexTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'compositor'=> $compositor, + 'restrictionBase' => $restrictionBase, + 'elements' => $elements, + 'attrs' => $attrs, + 'arrayType' => $arrayType + ); + + $this->xdebug("addComplexType $name:"); + $this->appendDebug($this->varDump($this->complexTypes[$name])); + } + + /** + * adds a simple type to the schema + * + * @param string $name + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param string $typeClass (should always be simpleType) + * @param string $phpType (should always be scalar) + * @param array $enumeration array of values + * @access public + * @see nusoap_xmlschema + * @see getTypeDef + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { + $this->simpleTypes[$name] = array( + 'name' => $name, + 'typeClass' => $typeClass, + 'phpType' => $phpType, + 'type' => $restrictionBase, + 'enumeration' => $enumeration + ); + + $this->xdebug("addSimpleType $name:"); + $this->appendDebug($this->varDump($this->simpleTypes[$name])); + } + + /** + * adds an element to the schema + * + * @param array $attrs attributes that must include name and type + * @see nusoap_xmlschema + * @access public + */ + function addElement($attrs) { + if (! $this->getPrefix($attrs['type'])) { + $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type']; + } + $this->elements[ $attrs['name'] ] = $attrs; + $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; + + $this->xdebug("addElement " . $attrs['name']); + $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ])); + } +} + +/** + * Backward compatibility + */ +class XMLSchema extends nusoap_xmlschema { +} + +?> +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class soapval extends nusoap_base { + /** + * The XML element name + * + * @var string + * @access private + */ + var $name; + /** + * The XML type name (string or false) + * + * @var mixed + * @access private + */ + var $type; + /** + * The PHP value + * + * @var mixed + * @access private + */ + var $value; + /** + * The XML element namespace (string or false) + * + * @var mixed + * @access private + */ + var $element_ns; + /** + * The XML type namespace (string or false) + * + * @var mixed + * @access private + */ + var $type_ns; + /** + * The XML element attributes (array or false) + * + * @var mixed + * @access private + */ + var $attributes; + + /** + * constructor + * + * @param string $name optional name + * @param mixed $type optional type name + * @param mixed $value optional value + * @param mixed $element_ns optional namespace of value + * @param mixed $type_ns optional namespace of type + * @param mixed $attributes associative array of attributes to add to element serialization + * @access public + */ + function soapval($name='soapval',$type=false,$value=-1,$element_ns=false,$type_ns=false,$attributes=false) { + parent::nusoap_base(); + $this->name = $name; + $this->type = $type; + $this->value = $value; + $this->element_ns = $element_ns; + $this->type_ns = $type_ns; + $this->attributes = $attributes; + } + + /** + * return serialized value + * + * @param string $use The WSDL use value (encoded|literal) + * @return string XML data + * @access public + */ + function serialize($use='encoded') { + return $this->serialize_val($this->value, $this->name, $this->type, $this->element_ns, $this->type_ns, $this->attributes, $use, true); + } + + /** + * decodes a soapval object into a PHP native type + * + * @return mixed + * @access public + */ + function decode(){ + return $this->value; + } +} + + + +?> +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class soap_transport_http extends nusoap_base { + + var $url = ''; + var $uri = ''; + var $digest_uri = ''; + var $scheme = ''; + var $host = ''; + var $port = ''; + var $path = ''; + var $request_method = 'POST'; + var $protocol_version = '1.0'; + var $encoding = ''; + var $outgoing_headers = array(); + var $incoming_headers = array(); + var $incoming_cookies = array(); + var $outgoing_payload = ''; + var $incoming_payload = ''; + var $response_status_line; // HTTP response status line + var $useSOAPAction = true; + var $persistentConnection = false; + var $ch = false; // cURL handle + var $ch_options = array(); // cURL custom options + var $use_curl = false; // force cURL use + var $proxy = null; // proxy information (associative array) + var $username = ''; + var $password = ''; + var $authtype = ''; + var $digestRequest = array(); + var $certRequest = array(); // keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional) + // cainfofile: certificate authority file, e.g. '$pathToPemFiles/rootca.pem' + // sslcertfile: SSL certificate file, e.g. '$pathToPemFiles/mycert.pem' + // sslkeyfile: SSL key file, e.g. '$pathToPemFiles/mykey.pem' + // passphrase: SSL key password/passphrase + // certpassword: SSL certificate password + // verifypeer: default is 1 + // verifyhost: default is 1 + + /** + * constructor + * + * @param string $url The URL to which to connect + * @param array $curl_options User-specified cURL options + * @param boolean $use_curl Whether to try to force cURL use + * @access public + */ + function soap_transport_http($url, $curl_options = NULL, $use_curl = false){ + parent::nusoap_base(); + $this->debug("ctor url=$url use_curl=$use_curl curl_options:"); + $this->appendDebug($this->varDump($curl_options)); + $this->setURL($url); + if (is_array($curl_options)) { + $this->ch_options = $curl_options; + } + $this->use_curl = $use_curl; + preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); + $this->setHeader('User-Agent', $this->title.'/'.$this->version.' ('.$rev[1].')'); + } + + /** + * sets a cURL option + * + * @param mixed $option The cURL option (always integer?) + * @param mixed $value The cURL option value + * @access private + */ + function setCurlOption($option, $value) { + $this->debug("setCurlOption option=$option, value="); + $this->appendDebug($this->varDump($value)); + curl_setopt($this->ch, $option, $value); + } + + /** + * sets an HTTP header + * + * @param string $name The name of the header + * @param string $value The value of the header + * @access private + */ + function setHeader($name, $value) { + $this->outgoing_headers[$name] = $value; + $this->debug("set header $name: $value"); + } + + /** + * unsets an HTTP header + * + * @param string $name The name of the header + * @access private + */ + function unsetHeader($name) { + if (isset($this->outgoing_headers[$name])) { + $this->debug("unset header $name"); + unset($this->outgoing_headers[$name]); + } + } + + /** + * sets the URL to which to connect + * + * @param string $url The URL to which to connect + * @access private + */ + function setURL($url) { + $this->url = $url; + + $u = parse_url($url); + foreach($u as $k => $v){ + $this->debug("parsed URL $k = $v"); + $this->$k = $v; + } + + // add any GET params to path + if(isset($u['query']) && $u['query'] != ''){ + $this->path .= '?' . $u['query']; + } + + // set default port + if(!isset($u['port'])){ + if($u['scheme'] == 'https'){ + $this->port = 443; + } else { + $this->port = 80; + } + } + + $this->uri = $this->path; + $this->digest_uri = $this->uri; + + // build headers + if (!isset($u['port'])) { + $this->setHeader('Host', $this->host); + } else { + $this->setHeader('Host', $this->host.':'.$this->port); + } + + if (isset($u['user']) && $u['user'] != '') { + $this->setCredentials(urldecode($u['user']), isset($u['pass']) ? urldecode($u['pass']) : ''); + } + } + + /** + * gets the I/O method to use + * + * @return string I/O method to use (socket|curl|unknown) + * @access private + */ + function io_method() { + if ($this->use_curl || ($this->scheme == 'https') || ($this->scheme == 'http' && $this->authtype == 'ntlm') || ($this->scheme == 'http' && is_array($this->proxy) && $this->proxy['authtype'] == 'ntlm')) + return 'curl'; + if (($this->scheme == 'http' || $this->scheme == 'ssl') && $this->authtype != 'ntlm' && (!is_array($this->proxy) || $this->proxy['authtype'] != 'ntlm')) + return 'socket'; + return 'unknown'; + } + + /** + * establish an HTTP connection + * + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return boolean true if connected, false if not + * @access private + */ + function connect($connection_timeout=0,$response_timeout=30){ + // For PHP 4.3 with OpenSSL, change https scheme to ssl, then treat like + // "regular" socket. + // TODO: disabled for now because OpenSSL must be *compiled* in (not just + // loaded), and until PHP5 stream_get_wrappers is not available. +// if ($this->scheme == 'https') { +// if (version_compare(phpversion(), '4.3.0') >= 0) { +// if (extension_loaded('openssl')) { +// $this->scheme = 'ssl'; +// $this->debug('Using SSL over OpenSSL'); +// } +// } +// } + $this->debug("connect connection_timeout $connection_timeout, response_timeout $response_timeout, scheme $this->scheme, host $this->host, port $this->port"); + if ($this->io_method() == 'socket') { + if (!is_array($this->proxy)) { + $host = $this->host; + $port = $this->port; + } else { + $host = $this->proxy['host']; + $port = $this->proxy['port']; + } + + // use persistent connection + if($this->persistentConnection && isset($this->fp) && is_resource($this->fp)){ + if (!feof($this->fp)) { + $this->debug('Re-use persistent connection'); + return true; + } + fclose($this->fp); + $this->debug('Closed persistent connection at EOF'); + } + + // munge host if using OpenSSL + if ($this->scheme == 'ssl') { + $host = 'ssl://' . $host; + } + $this->debug('calling fsockopen with host ' . $host . ' connection_timeout ' . $connection_timeout); + + // open socket + if($connection_timeout > 0){ + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str, $connection_timeout); + } else { + $this->fp = @fsockopen( $host, $this->port, $this->errno, $this->error_str); + } + + // test pointer + if(!$this->fp) { + $msg = 'Couldn\'t open socket connection to server ' . $this->url; + if ($this->errno) { + $msg .= ', Error ('.$this->errno.'): '.$this->error_str; + } else { + $msg .= ' prior to connect(). This is often a problem looking up the host name.'; + } + $this->debug($msg); + $this->setError($msg); + return false; + } + + // set response timeout + $this->debug('set response timeout to ' . $response_timeout); + socket_set_timeout( $this->fp, $response_timeout); + + $this->debug('socket connected'); + return true; + } else if ($this->io_method() == 'curl') { + if (!extension_loaded('curl')) { +// $this->setError('cURL Extension, or OpenSSL extension w/ PHP version >= 4.3 is required for HTTPS'); + $this->setError('The PHP cURL Extension is required for HTTPS or NLTM. You will need to re-build or update your PHP to include cURL or change php.ini to load the PHP cURL extension.'); + return false; + } + // Avoid warnings when PHP does not have these options + if (defined('CURLOPT_CONNECTIONTIMEOUT')) + $CURLOPT_CONNECTIONTIMEOUT = CURLOPT_CONNECTIONTIMEOUT; + else + $CURLOPT_CONNECTIONTIMEOUT = 78; + if (defined('CURLOPT_HTTPAUTH')) + $CURLOPT_HTTPAUTH = CURLOPT_HTTPAUTH; + else + $CURLOPT_HTTPAUTH = 107; + if (defined('CURLOPT_PROXYAUTH')) + $CURLOPT_PROXYAUTH = CURLOPT_PROXYAUTH; + else + $CURLOPT_PROXYAUTH = 111; + if (defined('CURLAUTH_BASIC')) + $CURLAUTH_BASIC = CURLAUTH_BASIC; + else + $CURLAUTH_BASIC = 1; + if (defined('CURLAUTH_DIGEST')) + $CURLAUTH_DIGEST = CURLAUTH_DIGEST; + else + $CURLAUTH_DIGEST = 2; + if (defined('CURLAUTH_NTLM')) + $CURLAUTH_NTLM = CURLAUTH_NTLM; + else + $CURLAUTH_NTLM = 8; + + $this->debug('connect using cURL'); + // init CURL + $this->ch = curl_init(); + // set url + $hostURL = ($this->port != '') ? "$this->scheme://$this->host:$this->port" : "$this->scheme://$this->host"; + // add path + $hostURL .= $this->path; + $this->setCurlOption(CURLOPT_URL, $hostURL); + // follow location headers (re-directs) + if (ini_get('safe_mode') || ini_get('open_basedir')) { + $this->debug('safe_mode or open_basedir set, so do not set CURLOPT_FOLLOWLOCATION'); + $this->debug('safe_mode = '); + $this->appendDebug($this->varDump(ini_get('safe_mode'))); + $this->debug('open_basedir = '); + $this->appendDebug($this->varDump(ini_get('open_basedir'))); + } else { + $this->setCurlOption(CURLOPT_FOLLOWLOCATION, 1); + } + // ask for headers in the response output + $this->setCurlOption(CURLOPT_HEADER, 1); + // ask for the response output as the return value + $this->setCurlOption(CURLOPT_RETURNTRANSFER, 1); + // encode + // We manage this ourselves through headers and encoding +// if(function_exists('gzuncompress')){ +// $this->setCurlOption(CURLOPT_ENCODING, 'deflate'); +// } + // persistent connection + if ($this->persistentConnection) { + // I believe the following comment is now bogus, having applied to + // the code when it used CURLOPT_CUSTOMREQUEST to send the request. + // The way we send data, we cannot use persistent connections, since + // there will be some "junk" at the end of our request. + //$this->setCurlOption(CURL_HTTP_VERSION_1_1, true); + $this->persistentConnection = false; + $this->setHeader('Connection', 'close'); + } + // set timeouts + if ($connection_timeout != 0) { + $this->setCurlOption($CURLOPT_CONNECTIONTIMEOUT, $connection_timeout); + } + if ($response_timeout != 0) { + $this->setCurlOption(CURLOPT_TIMEOUT, $response_timeout); + } + + if ($this->scheme == 'https') { + $this->debug('set cURL SSL verify options'); + // recent versions of cURL turn on peer/host checking by default, + // while PHP binaries are not compiled with a default location for the + // CA cert bundle, so disable peer/host checking. + //$this->setCurlOption(CURLOPT_CAINFO, 'f:\php-4.3.2-win32\extensions\curl-ca-bundle.crt'); + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 0); + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 0); + + // support client certificates (thanks Tobias Boes, Doug Anarino, Eryan Ariobowo) + if ($this->authtype == 'certificate') { + $this->debug('set cURL certificate options'); + if (isset($this->certRequest['cainfofile'])) { + $this->setCurlOption(CURLOPT_CAINFO, $this->certRequest['cainfofile']); + } + if (isset($this->certRequest['verifypeer'])) { + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, $this->certRequest['verifypeer']); + } else { + $this->setCurlOption(CURLOPT_SSL_VERIFYPEER, 1); + } + if (isset($this->certRequest['verifyhost'])) { + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, $this->certRequest['verifyhost']); + } else { + $this->setCurlOption(CURLOPT_SSL_VERIFYHOST, 1); + } + if (isset($this->certRequest['sslcertfile'])) { + $this->setCurlOption(CURLOPT_SSLCERT, $this->certRequest['sslcertfile']); + } + if (isset($this->certRequest['sslkeyfile'])) { + $this->setCurlOption(CURLOPT_SSLKEY, $this->certRequest['sslkeyfile']); + } + if (isset($this->certRequest['passphrase'])) { + $this->setCurlOption(CURLOPT_SSLKEYPASSWD, $this->certRequest['passphrase']); + } + if (isset($this->certRequest['certpassword'])) { + $this->setCurlOption(CURLOPT_SSLCERTPASSWD, $this->certRequest['certpassword']); + } + } + } + if ($this->authtype && ($this->authtype != 'certificate')) { + if ($this->username) { + $this->debug('set cURL username/password'); + $this->setCurlOption(CURLOPT_USERPWD, "$this->username:$this->password"); + } + if ($this->authtype == 'basic') { + $this->debug('set cURL for Basic authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_BASIC); + } + if ($this->authtype == 'digest') { + $this->debug('set cURL for digest authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_DIGEST); + } + if ($this->authtype == 'ntlm') { + $this->debug('set cURL for NTLM authentication'); + $this->setCurlOption($CURLOPT_HTTPAUTH, $CURLAUTH_NTLM); + } + } + if (is_array($this->proxy)) { + $this->debug('set cURL proxy options'); + if ($this->proxy['port'] != '') { + $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host'].':'.$this->proxy['port']); + } else { + $this->setCurlOption(CURLOPT_PROXY, $this->proxy['host']); + } + if ($this->proxy['username'] || $this->proxy['password']) { + $this->debug('set cURL proxy authentication options'); + $this->setCurlOption(CURLOPT_PROXYUSERPWD, $this->proxy['username'].':'.$this->proxy['password']); + if ($this->proxy['authtype'] == 'basic') { + $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_BASIC); + } + if ($this->proxy['authtype'] == 'ntlm') { + $this->setCurlOption($CURLOPT_PROXYAUTH, $CURLAUTH_NTLM); + } + } + } + $this->debug('cURL connection set up'); + return true; + } else { + $this->setError('Unknown scheme ' . $this->scheme); + $this->debug('Unknown scheme ' . $this->scheme); + return false; + } + } + + /** + * sends the SOAP request and gets the SOAP response via HTTP[S] + * + * @param string $data message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @param array $cookies cookies to send + * @return string data + * @access public + */ + function send($data, $timeout=0, $response_timeout=30, $cookies=NULL) { + + $this->debug('entered send() with data of length: '.strlen($data)); + + $this->tryagain = true; + $tries = 0; + while ($this->tryagain) { + $this->tryagain = false; + if ($tries++ < 2) { + // make connnection + if (!$this->connect($timeout, $response_timeout)){ + return false; + } + + // send request + if (!$this->sendRequest($data, $cookies)){ + return false; + } + + // get response + $respdata = $this->getResponse(); + } else { + $this->setError("Too many tries to get an OK response ($this->response_status_line)"); + } + } + $this->debug('end of send()'); + return $respdata; + } + + + /** + * sends the SOAP request and gets the SOAP response via HTTPS using CURL + * + * @param string $data message data + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @param array $cookies cookies to send + * @return string data + * @access public + * @deprecated + */ + function sendHTTPS($data, $timeout=0, $response_timeout=30, $cookies) { + return $this->send($data, $timeout, $response_timeout, $cookies); + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $digestRequest (keys must be nonce, nc, realm, qop) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $digestRequest = array(), $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype digestRequest="); + $this->appendDebug($this->varDump($digestRequest)); + $this->debug("certRequest="); + $this->appendDebug($this->varDump($certRequest)); + // cf. RFC 2617 + if ($authtype == 'basic') { + $this->setHeader('Authorization', 'Basic '.base64_encode(str_replace(':','',$username).':'.$password)); + } elseif ($authtype == 'digest') { + if (isset($digestRequest['nonce'])) { + $digestRequest['nc'] = isset($digestRequest['nc']) ? $digestRequest['nc']++ : 1; + + // calculate the Digest hashes (calculate code based on digest implementation found at: http://www.rassoc.com/gregr/weblog/stories/2002/07/09/webServicesSecurityHttpDigestAuthenticationWithoutActiveDirectory.html) + + // A1 = unq(username-value) ":" unq(realm-value) ":" passwd + $A1 = $username. ':' . (isset($digestRequest['realm']) ? $digestRequest['realm'] : '') . ':' . $password; + + // H(A1) = MD5(A1) + $HA1 = md5($A1); + + // A2 = Method ":" digest-uri-value + $A2 = $this->request_method . ':' . $this->digest_uri; + + // H(A2) + $HA2 = md5($A2); + + // KD(secret, data) = H(concat(secret, ":", data)) + // if qop == auth: + // request-digest = <"> < KD ( H(A1), unq(nonce-value) + // ":" nc-value + // ":" unq(cnonce-value) + // ":" unq(qop-value) + // ":" H(A2) + // ) <"> + // if qop is missing, + // request-digest = <"> < KD ( H(A1), unq(nonce-value) ":" H(A2) ) > <"> + + $unhashedDigest = ''; + $nonce = isset($digestRequest['nonce']) ? $digestRequest['nonce'] : ''; + $cnonce = $nonce; + if ($digestRequest['qop'] != '') { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . sprintf("%08d", $digestRequest['nc']) . ':' . $cnonce . ':' . $digestRequest['qop'] . ':' . $HA2; + } else { + $unhashedDigest = $HA1 . ':' . $nonce . ':' . $HA2; + } + + $hashedDigest = md5($unhashedDigest); + + $opaque = ''; + if (isset($digestRequest['opaque'])) { + $opaque = ', opaque="' . $digestRequest['opaque'] . '"'; + } + + $this->setHeader('Authorization', 'Digest username="' . $username . '", realm="' . $digestRequest['realm'] . '", nonce="' . $nonce . '", uri="' . $this->digest_uri . $opaque . '", cnonce="' . $cnonce . '", nc=' . sprintf("%08x", $digestRequest['nc']) . ', qop="' . $digestRequest['qop'] . '", response="' . $hashedDigest . '"'); + } + } elseif ($authtype == 'certificate') { + $this->certRequest = $certRequest; + $this->debug('Authorization header not set for certificate'); + } elseif ($authtype == 'ntlm') { + // do nothing + $this->debug('Authorization header not set for ntlm'); + } + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->digestRequest = $digestRequest; + } + + /** + * set the soapaction value + * + * @param string $soapaction + * @access public + */ + function setSOAPAction($soapaction) { + $this->setHeader('SOAPAction', '"' . $soapaction . '"'); + } + + /** + * use http encoding + * + * @param string $enc encoding style. supported values: gzip, deflate, or both + * @access public + */ + function setEncoding($enc='gzip, deflate') { + if (function_exists('gzdeflate')) { + $this->protocol_version = '1.1'; + $this->setHeader('Accept-Encoding', $enc); + if (!isset($this->outgoing_headers['Connection'])) { + $this->setHeader('Connection', 'close'); + $this->persistentConnection = false; + } + // deprecated as of PHP 5.3.0 + //set_magic_quotes_runtime(0); + $this->encoding = $enc; + } + } + + /** + * set proxy info here + * + * @param string $proxyhost use an empty string to remove proxy + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param string $proxyauthtype (basic|ntlm) + * @access public + */ + function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 'basic') { + if ($proxyhost) { + $this->proxy = array( + 'host' => $proxyhost, + 'port' => $proxyport, + 'username' => $proxyusername, + 'password' => $proxypassword, + 'authtype' => $proxyauthtype + ); + if ($proxyusername != '' && $proxypassword != '' && $proxyauthtype = 'basic') { + $this->setHeader('Proxy-Authorization', ' Basic '.base64_encode($proxyusername.':'.$proxypassword)); + } + } else { + $this->debug('remove proxy'); + $proxy = null; + unsetHeader('Proxy-Authorization'); + } + } + + + /** + * Test if the given string starts with a header that is to be skipped. + * Skippable headers result from chunked transfer and proxy requests. + * + * @param string $data The string to check. + * @returns boolean Whether a skippable header was found. + * @access private + */ + function isSkippableCurlHeader(&$data) { + $skipHeaders = array( 'HTTP/1.1 100', + 'HTTP/1.0 301', + 'HTTP/1.1 301', + 'HTTP/1.0 302', + 'HTTP/1.1 302', + 'HTTP/1.0 401', + 'HTTP/1.1 401', + 'HTTP/1.0 200 Connection established'); + foreach ($skipHeaders as $hd) { + $prefix = substr($data, 0, strlen($hd)); + if ($prefix == $hd) return true; + } + + return false; + } + + /** + * decode a string that is encoded w/ "chunked' transfer encoding + * as defined in RFC2068 19.4.6 + * + * @param string $buffer + * @param string $lb + * @returns string + * @access public + * @deprecated + */ + function decodeChunked($buffer, $lb){ + // length := 0 + $length = 0; + $new = ''; + + // read chunk-size, chunk-extension (if any) and CRLF + // get the position of the linebreak + $chunkend = strpos($buffer, $lb); + if ($chunkend == FALSE) { + $this->debug('no linebreak found in decodeChunked'); + return $new; + } + $temp = substr($buffer,0,$chunkend); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend + strlen($lb); + // while (chunk-size > 0) { + while ($chunk_size > 0) { + $this->debug("chunkstart: $chunkstart chunk_size: $chunk_size"); + $chunkend = strpos( $buffer, $lb, $chunkstart + $chunk_size); + + // Just in case we got a broken connection + if ($chunkend == FALSE) { + $chunk = substr($buffer,$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + $length += strlen($chunk); + break; + } + + // read chunk-data and CRLF + $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart); + // append chunk-data to entity-body + $new .= $chunk; + // length := length + chunk-size + $length += strlen($chunk); + // read chunk-size and CRLF + $chunkstart = $chunkend + strlen($lb); + + $chunkend = strpos($buffer, $lb, $chunkstart) + strlen($lb); + if ($chunkend == FALSE) { + break; //Just in case we got a broken connection + } + $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart); + $chunk_size = hexdec( trim($temp) ); + $chunkstart = $chunkend; + } + return $new; + } + + /** + * Writes the payload, including HTTP headers, to $this->outgoing_payload. + * + * @param string $data HTTP body + * @param string $cookie_str data for HTTP Cookie header + * @return void + * @access private + */ + function buildPayload($data, $cookie_str = '') { + // Note: for cURL connections, $this->outgoing_payload is ignored, + // as is the Content-Length header, but these are still created as + // debugging guides. + + // add content-length header + if ($this->request_method != 'GET') { + $this->setHeader('Content-Length', strlen($data)); + } + + // start building outgoing payload: + if ($this->proxy) { + $uri = $this->url; + } else { + $uri = $this->uri; + } + $req = "$this->request_method $uri HTTP/$this->protocol_version"; + $this->debug("HTTP request: $req"); + $this->outgoing_payload = "$req\r\n"; + + // loop thru headers, serializing + foreach($this->outgoing_headers as $k => $v){ + $hdr = $k.': '.$v; + $this->debug("HTTP header: $hdr"); + $this->outgoing_payload .= "$hdr\r\n"; + } + + // add any cookies + if ($cookie_str != '') { + $hdr = 'Cookie: '.$cookie_str; + $this->debug("HTTP header: $hdr"); + $this->outgoing_payload .= "$hdr\r\n"; + } + + // header/body separator + $this->outgoing_payload .= "\r\n"; + + // add data + $this->outgoing_payload .= $data; + } + + /** + * sends the SOAP request via HTTP[S] + * + * @param string $data message data + * @param array $cookies cookies to send + * @return boolean true if OK, false if problem + * @access private + */ + function sendRequest($data, $cookies = NULL) { + // build cookie string + $cookie_str = $this->getCookiesForRequest($cookies, (($this->scheme == 'ssl') || ($this->scheme == 'https'))); + + // build payload + $this->buildPayload($data, $cookie_str); + + if ($this->io_method() == 'socket') { + // send payload + if(!fputs($this->fp, $this->outgoing_payload, strlen($this->outgoing_payload))) { + $this->setError('couldn\'t write message data to socket'); + $this->debug('couldn\'t write message data to socket'); + return false; + } + $this->debug('wrote data to socket, length = ' . strlen($this->outgoing_payload)); + return true; + } else if ($this->io_method() == 'curl') { + // set payload + // cURL does say this should only be the verb, and in fact it + // turns out that the URI and HTTP version are appended to this, which + // some servers refuse to work with (so we no longer use this method!) + //$this->setCurlOption(CURLOPT_CUSTOMREQUEST, $this->outgoing_payload); + $curl_headers = array(); + foreach($this->outgoing_headers as $k => $v){ + if ($k == 'Connection' || $k == 'Content-Length' || $k == 'Host' || $k == 'Authorization' || $k == 'Proxy-Authorization') { + $this->debug("Skip cURL header $k: $v"); + } else { + $curl_headers[] = "$k: $v"; + } + } + if ($cookie_str != '') { + $curl_headers[] = 'Cookie: ' . $cookie_str; + } + $this->setCurlOption(CURLOPT_HTTPHEADER, $curl_headers); + $this->debug('set cURL HTTP headers'); + if ($this->request_method == "POST") { + $this->setCurlOption(CURLOPT_POST, 1); + $this->setCurlOption(CURLOPT_POSTFIELDS, $data); + $this->debug('set cURL POST data'); + } else { + } + // insert custom user-set cURL options + foreach ($this->ch_options as $key => $val) { + $this->setCurlOption($key, $val); + } + + $this->debug('set cURL payload'); + return true; + } + } + + /** + * gets the SOAP response via HTTP[S] + * + * @return string the response (also sets member variables like incoming_payload) + * @access private + */ + function getResponse(){ + $this->incoming_payload = ''; + + if ($this->io_method() == 'socket') { + // loop until headers have been retrieved + $data = ''; + while (!isset($lb)){ + + // We might EOF during header read. + if(feof($this->fp)) { + $this->incoming_payload = $data; + $this->debug('found no headers before EOF after length ' . strlen($data)); + $this->debug("received before EOF:\n" . $data); + $this->setError('server failed to send headers'); + return false; + } + + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read line of $tmplen bytes: " . trim($tmp)); + + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of headers timed out after length ' . strlen($data)); + $this->debug("read before timeout: " . $data); + $this->setError('socket read of headers timed out'); + return false; + } + + $data .= $tmp; + $pos = strpos($data,"\r\n\r\n"); + if($pos > 1){ + $lb = "\r\n"; + } else { + $pos = strpos($data,"\n\n"); + if($pos > 1){ + $lb = "\n"; + } + } + // remove 100 headers + if (isset($lb) && preg_match('/^HTTP\/1.1 100/',$data)) { + unset($lb); + $data = ''; + }// + } + // store header data + $this->incoming_payload .= $data; + $this->debug('found end of headers after length ' . strlen($data)); + // process headers + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + $this->incoming_headers = array(); + $this->incoming_cookies = array(); + foreach($header_array as $header_line){ + $arr = explode(':',$header_line, 2); + if(count($arr) > 1){ + $header_name = strtolower(trim($arr[0])); + $this->incoming_headers[$header_name] = trim($arr[1]); + if ($header_name == 'set-cookie') { + // TODO: allow multiple cookies from parseCookie + $cookie = $this->parseCookie(trim($arr[1])); + if ($cookie) { + $this->incoming_cookies[] = $cookie; + $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); + } else { + $this->debug('did not find cookie in ' . trim($arr[1])); + } + } + } else if (isset($header_name)) { + // append continuation line to previous header + $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; + } + } + + // loop until msg has been received + if (isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked') { + $content_length = 2147483647; // ignore any content-length header + $chunked = true; + $this->debug("want to read chunked content"); + } elseif (isset($this->incoming_headers['content-length'])) { + $content_length = $this->incoming_headers['content-length']; + $chunked = false; + $this->debug("want to read content of length $content_length"); + } else { + $content_length = 2147483647; + $chunked = false; + $this->debug("want to read content to EOF"); + } + $data = ''; + do { + if ($chunked) { + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read chunk line of $tmplen bytes"); + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of chunk length timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of chunk length timed out'); + return false; + } + $content_length = hexdec(trim($tmp)); + $this->debug("chunk length $content_length"); + } + $strlen = 0; + while (($strlen < $content_length) && (!feof($this->fp))) { + $readlen = min(8192, $content_length - $strlen); + $tmp = fread($this->fp, $readlen); + $tmplen = strlen($tmp); + $this->debug("read buffer of $tmplen bytes"); + if (($tmplen == 0) && (!feof($this->fp))) { + $this->incoming_payload = $data; + $this->debug('socket read of body timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of body timed out'); + return false; + } + $strlen += $tmplen; + $data .= $tmp; + } + if ($chunked && ($content_length > 0)) { + $tmp = fgets($this->fp, 256); + $tmplen = strlen($tmp); + $this->debug("read chunk terminator of $tmplen bytes"); + if ($tmplen == 0) { + $this->incoming_payload = $data; + $this->debug('socket read of chunk terminator timed out after length ' . strlen($data)); + $this->debug("read before timeout:\n" . $data); + $this->setError('socket read of chunk terminator timed out'); + return false; + } + } + } while ($chunked && ($content_length > 0) && (!feof($this->fp))); + if (feof($this->fp)) { + $this->debug('read to EOF'); + } + $this->debug('read body of length ' . strlen($data)); + $this->incoming_payload .= $data; + $this->debug('received a total of '.strlen($this->incoming_payload).' bytes of data from server'); + + // close filepointer + if( + (isset($this->incoming_headers['connection']) && strtolower($this->incoming_headers['connection']) == 'close') || + (! $this->persistentConnection) || feof($this->fp)){ + fclose($this->fp); + $this->fp = false; + $this->debug('closed socket'); + } + + // connection was closed unexpectedly + if($this->incoming_payload == ''){ + $this->setError('no response from server'); + return false; + } + + // decode transfer-encoding +// if(isset($this->incoming_headers['transfer-encoding']) && strtolower($this->incoming_headers['transfer-encoding']) == 'chunked'){ +// if(!$data = $this->decodeChunked($data, $lb)){ +// $this->setError('Decoding of chunked data failed'); +// return false; +// } + //print "
\nde-chunked:\n---------------\n$data\n\n---------------\n
"; + // set decoded payload +// $this->incoming_payload = $header_data.$lb.$lb.$data; +// } + + } else if ($this->io_method() == 'curl') { + // send and receive + $this->debug('send and receive with cURL'); + $this->incoming_payload = curl_exec($this->ch); + $data = $this->incoming_payload; + + $cErr = curl_error($this->ch); + if ($cErr != '') { + $err = 'cURL ERROR: '.curl_errno($this->ch).': '.$cErr.'
'; + // TODO: there is a PHP bug that can cause this to SEGV for CURLINFO_CONTENT_TYPE + foreach(curl_getinfo($this->ch) as $k => $v){ + $err .= "$k: $v
"; + } + $this->debug($err); + $this->setError($err); + curl_close($this->ch); + return false; + } else { + //echo '
';
+			//var_dump(curl_getinfo($this->ch));
+			//echo '
'; + } + // close curl + $this->debug('No cURL error, closing cURL'); + curl_close($this->ch); + + // try removing skippable headers + $savedata = $data; + while ($this->isSkippableCurlHeader($data)) { + $this->debug("Found HTTP header to skip"); + if ($pos = strpos($data,"\r\n\r\n")) { + $data = ltrim(substr($data,$pos)); + } elseif($pos = strpos($data,"\n\n") ) { + $data = ltrim(substr($data,$pos)); + } + } + + if ($data == '') { + // have nothing left; just remove 100 header(s) + $data = $savedata; + while (preg_match('/^HTTP\/1.1 100/',$data)) { + if ($pos = strpos($data,"\r\n\r\n")) { + $data = ltrim(substr($data,$pos)); + } elseif($pos = strpos($data,"\n\n") ) { + $data = ltrim(substr($data,$pos)); + } + } + } + + // separate content from HTTP headers + if ($pos = strpos($data,"\r\n\r\n")) { + $lb = "\r\n"; + } elseif( $pos = strpos($data,"\n\n")) { + $lb = "\n"; + } else { + $this->debug('no proper separation of headers and document'); + $this->setError('no proper separation of headers and document'); + return false; + } + $header_data = trim(substr($data,0,$pos)); + $header_array = explode($lb,$header_data); + $data = ltrim(substr($data,$pos)); + $this->debug('found proper separation of headers and document'); + $this->debug('cleaned data, stringlen: '.strlen($data)); + // clean headers + foreach ($header_array as $header_line) { + $arr = explode(':',$header_line,2); + if(count($arr) > 1){ + $header_name = strtolower(trim($arr[0])); + $this->incoming_headers[$header_name] = trim($arr[1]); + if ($header_name == 'set-cookie') { + // TODO: allow multiple cookies from parseCookie + $cookie = $this->parseCookie(trim($arr[1])); + if ($cookie) { + $this->incoming_cookies[] = $cookie; + $this->debug('found cookie: ' . $cookie['name'] . ' = ' . $cookie['value']); + } else { + $this->debug('did not find cookie in ' . trim($arr[1])); + } + } + } else if (isset($header_name)) { + // append continuation line to previous header + $this->incoming_headers[$header_name] .= $lb . ' ' . $header_line; + } + } + } + + $this->response_status_line = $header_array[0]; + $arr = explode(' ', $this->response_status_line, 3); + $http_version = $arr[0]; + $http_status = intval($arr[1]); + $http_reason = count($arr) > 2 ? $arr[2] : ''; + + // see if we need to resend the request with http digest authentication + if (isset($this->incoming_headers['location']) && ($http_status == 301 || $http_status == 302)) { + $this->debug("Got $http_status $http_reason with Location: " . $this->incoming_headers['location']); + $this->setURL($this->incoming_headers['location']); + $this->tryagain = true; + return false; + } + + // see if we need to resend the request with http digest authentication + if (isset($this->incoming_headers['www-authenticate']) && $http_status == 401) { + $this->debug("Got 401 $http_reason with WWW-Authenticate: " . $this->incoming_headers['www-authenticate']); + if (strstr($this->incoming_headers['www-authenticate'], "Digest ")) { + $this->debug('Server wants digest authentication'); + // remove "Digest " from our elements + $digestString = str_replace('Digest ', '', $this->incoming_headers['www-authenticate']); + + // parse elements into array + $digestElements = explode(',', $digestString); + foreach ($digestElements as $val) { + $tempElement = explode('=', trim($val), 2); + $digestRequest[$tempElement[0]] = str_replace("\"", '', $tempElement[1]); + } + + // should have (at least) qop, realm, nonce + if (isset($digestRequest['nonce'])) { + $this->setCredentials($this->username, $this->password, 'digest', $digestRequest); + $this->tryagain = true; + return false; + } + } + $this->debug('HTTP authentication failed'); + $this->setError('HTTP authentication failed'); + return false; + } + + if ( + ($http_status >= 300 && $http_status <= 307) || + ($http_status >= 400 && $http_status <= 417) || + ($http_status >= 501 && $http_status <= 505) + ) { + $this->setError("Unsupported HTTP response status $http_status $http_reason (soapclient->response has contents of the response)"); + return false; + } + + // decode content-encoding + if(isset($this->incoming_headers['content-encoding']) && $this->incoming_headers['content-encoding'] != ''){ + if(strtolower($this->incoming_headers['content-encoding']) == 'deflate' || strtolower($this->incoming_headers['content-encoding']) == 'gzip'){ + // if decoding works, use it. else assume data wasn't gzencoded + if(function_exists('gzinflate')){ + //$timer->setMarker('starting decoding of gzip/deflated content'); + // IIS 5 requires gzinflate instead of gzuncompress (similar to IE 5 and gzdeflate v. gzcompress) + // this means there are no Zlib headers, although there should be + $this->debug('The gzinflate function exists'); + $datalen = strlen($data); + if ($this->incoming_headers['content-encoding'] == 'deflate') { + if ($degzdata = @gzinflate($data)) { + $data = $degzdata; + $this->debug('The payload has been inflated to ' . strlen($data) . ' bytes'); + if (strlen($data) < $datalen) { + // test for the case that the payload has been compressed twice + $this->debug('The inflated payload is smaller than the gzipped one; try again'); + if ($degzdata = @gzinflate($data)) { + $data = $degzdata; + $this->debug('The payload has been inflated again to ' . strlen($data) . ' bytes'); + } + } + } else { + $this->debug('Error using gzinflate to inflate the payload'); + $this->setError('Error using gzinflate to inflate the payload'); + } + } elseif ($this->incoming_headers['content-encoding'] == 'gzip') { + if ($degzdata = @gzinflate(substr($data, 10))) { // do our best + $data = $degzdata; + $this->debug('The payload has been un-gzipped to ' . strlen($data) . ' bytes'); + if (strlen($data) < $datalen) { + // test for the case that the payload has been compressed twice + $this->debug('The un-gzipped payload is smaller than the gzipped one; try again'); + if ($degzdata = @gzinflate(substr($data, 10))) { + $data = $degzdata; + $this->debug('The payload has been un-gzipped again to ' . strlen($data) . ' bytes'); + } + } + } else { + $this->debug('Error using gzinflate to un-gzip the payload'); + $this->setError('Error using gzinflate to un-gzip the payload'); + } + } + //$timer->setMarker('finished decoding of gzip/deflated content'); + //print "\nde-inflated:\n---------------\n$data\n-------------\n"; + // set decoded payload + $this->incoming_payload = $header_data.$lb.$lb.$data; + } else { + $this->debug('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); + $this->setError('The server sent compressed data. Your php install must have the Zlib extension compiled in to support this.'); + } + } else { + $this->debug('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); + $this->setError('Unsupported Content-Encoding ' . $this->incoming_headers['content-encoding']); + } + } else { + $this->debug('No Content-Encoding header'); + } + + if(strlen($data) == 0){ + $this->debug('no data after headers!'); + $this->setError('no data present after HTTP headers'); + return false; + } + + return $data; + } + + /** + * sets the content-type for the SOAP message to be sent + * + * @param string $type the content type, MIME style + * @param mixed $charset character set used for encoding (or false) + * @access public + */ + function setContentType($type, $charset = false) { + $this->setHeader('Content-Type', $type . ($charset ? '; charset=' . $charset : '')); + } + + /** + * specifies that an HTTP persistent connection should be used + * + * @return boolean whether the request was honored by this method. + * @access public + */ + function usePersistentConnection(){ + if (isset($this->outgoing_headers['Accept-Encoding'])) { + return false; + } + $this->protocol_version = '1.1'; + $this->persistentConnection = true; + $this->setHeader('Connection', 'Keep-Alive'); + return true; + } + + /** + * parse an incoming Cookie into it's parts + * + * @param string $cookie_str content of cookie + * @return array with data of that cookie + * @access private + */ + /* + * TODO: allow a Set-Cookie string to be parsed into multiple cookies + */ + function parseCookie($cookie_str) { + $cookie_str = str_replace('; ', ';', $cookie_str) . ';'; + $data = preg_split('/;/', $cookie_str); + $value_str = $data[0]; + + $cookie_param = 'domain='; + $start = strpos($cookie_str, $cookie_param); + if ($start > 0) { + $domain = substr($cookie_str, $start + strlen($cookie_param)); + $domain = substr($domain, 0, strpos($domain, ';')); + } else { + $domain = ''; + } + + $cookie_param = 'expires='; + $start = strpos($cookie_str, $cookie_param); + if ($start > 0) { + $expires = substr($cookie_str, $start + strlen($cookie_param)); + $expires = substr($expires, 0, strpos($expires, ';')); + } else { + $expires = ''; + } + + $cookie_param = 'path='; + $start = strpos($cookie_str, $cookie_param); + if ( $start > 0 ) { + $path = substr($cookie_str, $start + strlen($cookie_param)); + $path = substr($path, 0, strpos($path, ';')); + } else { + $path = '/'; + } + + $cookie_param = ';secure;'; + if (strpos($cookie_str, $cookie_param) !== FALSE) { + $secure = true; + } else { + $secure = false; + } + + $sep_pos = strpos($value_str, '='); + + if ($sep_pos) { + $name = substr($value_str, 0, $sep_pos); + $value = substr($value_str, $sep_pos + 1); + $cookie= array( 'name' => $name, + 'value' => $value, + 'domain' => $domain, + 'path' => $path, + 'expires' => $expires, + 'secure' => $secure + ); + return $cookie; + } + return false; + } + + /** + * sort out cookies for the current request + * + * @param array $cookies array with all cookies + * @param boolean $secure is the send-content secure or not? + * @return string for Cookie-HTTP-Header + * @access private + */ + function getCookiesForRequest($cookies, $secure=false) { + $cookie_str = ''; + if ((! is_null($cookies)) && (is_array($cookies))) { + foreach ($cookies as $cookie) { + if (! is_array($cookie)) { + continue; + } + $this->debug("check cookie for validity: ".$cookie['name'].'='.$cookie['value']); + if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { + if (strtotime($cookie['expires']) <= time()) { + $this->debug('cookie has expired'); + continue; + } + } + if ((isset($cookie['domain'])) && (! empty($cookie['domain']))) { + $domain = preg_quote($cookie['domain']); + if (! preg_match("'.*$domain$'i", $this->host)) { + $this->debug('cookie has different domain'); + continue; + } + } + if ((isset($cookie['path'])) && (! empty($cookie['path']))) { + $path = preg_quote($cookie['path']); + if (! preg_match("'^$path.*'i", $this->path)) { + $this->debug('cookie is for a different path'); + continue; + } + } + if ((! $secure) && (isset($cookie['secure'])) && ($cookie['secure'])) { + $this->debug('cookie is secure, transport is not'); + continue; + } + $cookie_str .= $cookie['name'] . '=' . $cookie['value'] . '; '; + $this->debug('add cookie to Cookie-String: ' . $cookie['name'] . '=' . $cookie['value']); + } + } + return $cookie_str; + } +} + +?> +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_server extends nusoap_base { + /** + * HTTP headers of request + * @var array + * @access private + */ + var $headers = array(); + /** + * HTTP request + * @var string + * @access private + */ + var $request = ''; + /** + * SOAP headers from request (incomplete namespace resolution; special characters not escaped) (text) + * @var string + * @access public + */ + var $requestHeaders = ''; + /** + * SOAP Headers from request (parsed) + * @var mixed + * @access public + */ + var $requestHeader = NULL; + /** + * SOAP body request portion (incomplete namespace resolution; special characters not escaped) (text) + * @var string + * @access public + */ + var $document = ''; + /** + * SOAP payload for request (text) + * @var string + * @access public + */ + var $requestSOAP = ''; + /** + * requested method namespace URI + * @var string + * @access private + */ + var $methodURI = ''; + /** + * name of method requested + * @var string + * @access private + */ + var $methodname = ''; + /** + * method parameters from request + * @var array + * @access private + */ + var $methodparams = array(); + /** + * SOAP Action from request + * @var string + * @access private + */ + var $SOAPAction = ''; + /** + * character set encoding of incoming (request) messages + * @var string + * @access public + */ + var $xml_encoding = ''; + /** + * toggles whether the parser decodes element content w/ utf8_decode() + * @var boolean + * @access public + */ + var $decode_utf8 = true; + + /** + * HTTP headers of response + * @var array + * @access public + */ + var $outgoing_headers = array(); + /** + * HTTP response + * @var string + * @access private + */ + var $response = ''; + /** + * SOAP headers for response (text or array of soapval or associative array) + * @var mixed + * @access public + */ + var $responseHeaders = ''; + /** + * SOAP payload for response (text) + * @var string + * @access private + */ + var $responseSOAP = ''; + /** + * method return value to place in response + * @var mixed + * @access private + */ + var $methodreturn = false; + /** + * whether $methodreturn is a string of literal XML + * @var boolean + * @access public + */ + var $methodreturnisliteralxml = false; + /** + * SOAP fault for response (or false) + * @var mixed + * @access private + */ + var $fault = false; + /** + * text indication of result (for debugging) + * @var string + * @access private + */ + var $result = 'successful'; + + /** + * assoc array of operations => opData; operations are added by the register() + * method or by parsing an external WSDL definition + * @var array + * @access private + */ + var $operations = array(); + /** + * wsdl instance (if one) + * @var mixed + * @access private + */ + var $wsdl = false; + /** + * URL for WSDL (if one) + * @var mixed + * @access private + */ + var $externalWSDLURL = false; + /** + * whether to append debug to response as XML comment + * @var boolean + * @access public + */ + var $debug_flag = false; + + + /** + * constructor + * the optional parameter is a path to a WSDL file that you'd like to bind the server instance to. + * + * @param mixed $wsdl file path or URL (string), or wsdl instance (object) + * @access public + */ + function nusoap_server($wsdl=false){ + parent::nusoap_base(); + // turn on debugging? + global $debug; + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $this->debug("_SERVER is defined:"); + $this->appendDebug($this->varDump($_SERVER)); + } elseif (isset($HTTP_SERVER_VARS)) { + $this->debug("HTTP_SERVER_VARS is defined:"); + $this->appendDebug($this->varDump($HTTP_SERVER_VARS)); + } else { + $this->debug("Neither _SERVER nor HTTP_SERVER_VARS is defined."); + } + + if (isset($debug)) { + $this->debug("In nusoap_server, set debug_flag=$debug based on global flag"); + $this->debug_flag = $debug; + } elseif (isset($_SERVER['QUERY_STRING'])) { + $qs = explode('&', $_SERVER['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #1"); + $this->debug_flag = substr($v, 6); + } + } + } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { + $qs = explode('&', $HTTP_SERVER_VARS['QUERY_STRING']); + foreach ($qs as $v) { + if (substr($v, 0, 6) == 'debug=') { + $this->debug("In nusoap_server, set debug_flag=" . substr($v, 6) . " based on query string #2"); + $this->debug_flag = substr($v, 6); + } + } + } + + // wsdl + if($wsdl){ + $this->debug("In nusoap_server, WSDL is specified"); + if (is_object($wsdl) && (get_class($wsdl) == 'wsdl')) { + $this->wsdl = $wsdl; + $this->externalWSDLURL = $this->wsdl->wsdl; + $this->debug('Use existing wsdl instance from ' . $this->externalWSDLURL); + } else { + $this->debug('Create wsdl from ' . $wsdl); + $this->wsdl = new wsdl($wsdl); + $this->externalWSDLURL = $wsdl; + } + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if($err = $this->wsdl->getError()){ + die('WSDL ERROR: '.$err); + } + } + } + + /** + * processes request and returns response + * + * @param string $data usually is the value of $HTTP_RAW_POST_DATA + * @access public + */ + function service($data){ + global $HTTP_SERVER_VARS; + + if (isset($_SERVER['REQUEST_METHOD'])) { + $rm = $_SERVER['REQUEST_METHOD']; + } elseif (isset($HTTP_SERVER_VARS['REQUEST_METHOD'])) { + $rm = $HTTP_SERVER_VARS['REQUEST_METHOD']; + } else { + $rm = ''; + } + + if (isset($_SERVER['QUERY_STRING'])) { + $qs = $_SERVER['QUERY_STRING']; + } elseif (isset($HTTP_SERVER_VARS['QUERY_STRING'])) { + $qs = $HTTP_SERVER_VARS['QUERY_STRING']; + } else { + $qs = ''; + } + $this->debug("In service, request method=$rm query string=$qs strlen(\$data)=" . strlen($data)); + + if ($rm == 'POST') { + $this->debug("In service, invoke the request"); + $this->parse_request($data); + if (! $this->fault) { + $this->invoke_method(); + } + if (! $this->fault) { + $this->serialize_return(); + } + $this->send_response(); + } elseif (preg_match('/wsdl/', $qs) ){ + $this->debug("In service, this is a request for WSDL"); + if ($this->externalWSDLURL){ + if (strpos($this->externalWSDLURL, "http://") !== false) { // assume URL + $this->debug("In service, re-direct for WSDL"); + header('Location: '.$this->externalWSDLURL); + } else { // assume file + $this->debug("In service, use file passthru for WSDL"); + header("Content-Type: text/xml\r\n"); + $pos = strpos($this->externalWSDLURL, "file://"); + if ($pos === false) { + $filename = $this->externalWSDLURL; + } else { + $filename = substr($this->externalWSDLURL, $pos + 7); + } + $fp = fopen($this->externalWSDLURL, 'r'); + fpassthru($fp); + } + } elseif ($this->wsdl) { + $this->debug("In service, serialize WSDL"); + header("Content-Type: text/xml; charset=ISO-8859-1\r\n"); + print $this->wsdl->serialize($this->debug_flag); + if ($this->debug_flag) { + $this->debug('wsdl:'); + $this->appendDebug($this->varDump($this->wsdl)); + print $this->getDebugAsXMLComment(); + } + } else { + $this->debug("In service, there is no WSDL"); + header("Content-Type: text/html; charset=ISO-8859-1\r\n"); + print "This service does not provide WSDL"; + } + } elseif ($this->wsdl) { + $this->debug("In service, return Web description"); + print $this->wsdl->webDescription(); + } else { + $this->debug("In service, no Web description"); + header("Content-Type: text/html; charset=ISO-8859-1\r\n"); + print "This service does not provide a Web description"; + } + } + + /** + * parses HTTP request headers. + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * + * @access private + */ + function parse_http_headers() { + global $HTTP_SERVER_VARS; + + $this->request = ''; + $this->SOAPAction = ''; + if(function_exists('getallheaders')){ + $this->debug("In parse_http_headers, use getallheaders"); + $headers = getallheaders(); + foreach($headers as $k=>$v){ + $k = strtolower($k); + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + // get SOAPAction header + if(isset($this->headers['soapaction'])){ + $this->SOAPAction = str_replace('"','',$this->headers['soapaction']); + } + // get the character encoding of the incoming request + if(isset($this->headers['content-type']) && strpos($this->headers['content-type'],'=')){ + $enc = str_replace('"','',substr(strstr($this->headers["content-type"],'='),1)); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } elseif(isset($_SERVER) && is_array($_SERVER)){ + $this->debug("In parse_http_headers, use _SERVER"); + foreach ($_SERVER as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); + } else { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); + } + if ($k == 'soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'content-type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } elseif (is_array($HTTP_SERVER_VARS)) { + $this->debug("In parse_http_headers, use HTTP_SERVER_VARS"); + foreach ($HTTP_SERVER_VARS as $k => $v) { + if (substr($k, 0, 5) == 'HTTP_') { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', substr($k, 5)))); $k = strtolower(substr($k, 5)); + } else { + $k = str_replace(' ', '-', strtolower(str_replace('_', ' ', $k))); $k = strtolower($k); + } + if ($k == 'soapaction') { + // get SOAPAction header + $k = 'SOAPAction'; + $v = str_replace('"', '', $v); + $v = str_replace('\\', '', $v); + $this->SOAPAction = $v; + } else if ($k == 'content-type') { + // get the character encoding of the incoming request + if (strpos($v, '=')) { + $enc = substr(strstr($v, '='), 1); + $enc = str_replace('"', '', $enc); + $enc = str_replace('\\', '', $enc); + if (preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)) { + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + } + $this->headers[$k] = $v; + $this->request .= "$k: $v\r\n"; + $this->debug("$k: $v"); + } + } else { + $this->debug("In parse_http_headers, HTTP headers not accessible"); + $this->setError("HTTP headers not accessible"); + } + } + + /** + * parses a request + * + * The following fields are set by this function (when successful) + * + * headers + * request + * xml_encoding + * SOAPAction + * request + * requestSOAP + * methodURI + * methodname + * methodparams + * requestHeaders + * document + * + * This sets the fault field on error + * + * @param string $data XML string + * @access private + */ + function parse_request($data='') { + $this->debug('entering parse_request()'); + $this->parse_http_headers(); + $this->debug('got character encoding: '.$this->xml_encoding); + // uncompress if necessary + if (isset($this->headers['content-encoding']) && $this->headers['content-encoding'] != '') { + $this->debug('got content encoding: ' . $this->headers['content-encoding']); + if ($this->headers['content-encoding'] == 'deflate' || $this->headers['content-encoding'] == 'gzip') { + // if decoding works, use it. else assume data wasn't gzencoded + if (function_exists('gzuncompress')) { + if ($this->headers['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data)) { + $data = $degzdata; + } elseif ($this->headers['content-encoding'] == 'gzip' && $degzdata = gzinflate(substr($data, 10))) { + $data = $degzdata; + } else { + $this->fault('SOAP-ENV:Client', 'Errors occurred when trying to decode the data'); + return; + } + } else { + $this->fault('SOAP-ENV:Client', 'This Server does not support compressed data'); + return; + } + } + } + $this->request .= "\r\n".$data; + $data = $this->parseRequest($this->headers, $data); + $this->requestSOAP = $data; + $this->debug('leaving parse_request'); + } + + /** + * invokes a PHP function for the requested SOAP method + * + * The following fields are set by this function (when successful) + * + * methodreturn + * + * Note that the PHP function that is called may also set the following + * fields to affect the response sent to the client + * + * responseHeaders + * outgoing_headers + * + * This sets the fault field on error + * + * @access private + */ + function invoke_method() { + $this->debug('in invoke_method, methodname=' . $this->methodname . ' methodURI=' . $this->methodURI . ' SOAPAction=' . $this->SOAPAction); + + // + // if you are debugging in this area of the code, your service uses a class to implement methods, + // you use SOAP RPC, and the client is .NET, please be aware of the following... + // when the .NET wsdl.exe utility generates a proxy, it will remove the '.' or '..' from the + // method name. that is fine for naming the .NET methods. it is not fine for properly constructing + // the XML request and reading the XML response. you need to add the RequestElementName and + // ResponseElementName to the System.Web.Services.Protocols.SoapRpcMethodAttribute that wsdl.exe + // generates for the method. these parameters are used to specify the correct XML element names + // for .NET to use, i.e. the names with the '.' in them. + // + $orig_methodname = $this->methodname; + if ($this->wsdl) { + if ($this->opData = $this->wsdl->getOperationData($this->methodname)) { + $this->debug('in invoke_method, found WSDL operation=' . $this->methodname); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + } elseif ($this->opData = $this->wsdl->getOperationDataForSoapAction($this->SOAPAction)) { + // Note: hopefully this case will only be used for doc/lit, since rpc services should have wrapper element + $this->debug('in invoke_method, found WSDL soapAction=' . $this->SOAPAction . ' for operation=' . $this->opData['name']); + $this->appendDebug('opData=' . $this->varDump($this->opData)); + $this->methodname = $this->opData['name']; + } else { + $this->debug('in invoke_method, no WSDL for operation=' . $this->methodname); + $this->fault('SOAP-ENV:Client', "Operation '" . $this->methodname . "' is not defined in the WSDL for this service"); + return; + } + } else { + $this->debug('in invoke_method, no WSDL to validate method'); + } + + // if a . is present in $this->methodname, we see if there is a class in scope, + // which could be referred to. We will also distinguish between two deliminators, + // to allow methods to be called a the class or an instance + if (strpos($this->methodname, '..') > 0) { + $delim = '..'; + } else if (strpos($this->methodname, '.') > 0) { + $delim = '.'; + } else { + $delim = ''; + } + $this->debug("in invoke_method, delim=$delim"); + + $class = ''; + $method = ''; + if (strlen($delim) > 0 && substr_count($this->methodname, $delim) == 1) { + $try_class = substr($this->methodname, 0, strpos($this->methodname, $delim)); + if (class_exists($try_class)) { + // get the class and method name + $class = $try_class; + $method = substr($this->methodname, strpos($this->methodname, $delim) + strlen($delim)); + $this->debug("in invoke_method, class=$class method=$method delim=$delim"); + } else { + $this->debug("in invoke_method, class=$try_class not found"); + } + } else { + $try_class = ''; + $this->debug("in invoke_method, no class to try"); + } + + // does method exist? + if ($class == '') { + if (!function_exists($this->methodname)) { + $this->debug("in invoke_method, function '$this->methodname' not found!"); + $this->result = 'fault: method not found'; + $this->fault('SOAP-ENV:Client',"method '$this->methodname'('$orig_methodname') not defined in service('$try_class' '$delim')"); + return; + } + } else { + $method_to_compare = (substr(phpversion(), 0, 2) == '4.') ? strtolower($method) : $method; + if (!in_array($method_to_compare, get_class_methods($class))) { + $this->debug("in invoke_method, method '$this->methodname' not found in class '$class'!"); + $this->result = 'fault: method not found'; + $this->fault('SOAP-ENV:Client',"method '$this->methodname'/'$method_to_compare'('$orig_methodname') not defined in service/'$class'('$try_class' '$delim')"); + return; + } + } + + // evaluate message, getting back parameters + // verify that request parameters match the method's signature + if(! $this->verify_method($this->methodname,$this->methodparams)){ + // debug + $this->debug('ERROR: request not verified against method signature'); + $this->result = 'fault: request failed validation against method signature'; + // return fault + $this->fault('SOAP-ENV:Client',"Operation '$this->methodname' not defined in service."); + return; + } + + // if there are parameters to pass + $this->debug('in invoke_method, params:'); + $this->appendDebug($this->varDump($this->methodparams)); + $this->debug("in invoke_method, calling '$this->methodname'"); + if (!function_exists('call_user_func_array')) { + if ($class == '') { + $this->debug('in invoke_method, calling function using eval()'); + $funcCall = "\$this->methodreturn = $this->methodname("; + } else { + if ($delim == '..') { + $this->debug('in invoke_method, calling class method using eval()'); + $funcCall = "\$this->methodreturn = ".$class."::".$method."("; + } else { + $this->debug('in invoke_method, calling instance method using eval()'); + // generate unique instance name + $instname = "\$inst_".time(); + $funcCall = $instname." = new ".$class."(); "; + $funcCall .= "\$this->methodreturn = ".$instname."->".$method."("; + } + } + if ($this->methodparams) { + foreach ($this->methodparams as $param) { + if (is_array($param) || is_object($param)) { + $this->fault('SOAP-ENV:Client', 'NuSOAP does not handle complexType parameters correctly when using eval; call_user_func_array must be available'); + return; + } + $funcCall .= "\"$param\","; + } + $funcCall = substr($funcCall, 0, -1); + } + $funcCall .= ');'; + $this->debug('in invoke_method, function call: '.$funcCall); + @eval($funcCall); + } else { + if ($class == '') { + $this->debug('in invoke_method, calling function using call_user_func_array()'); + $call_arg = "$this->methodname"; // straight assignment changes $this->methodname to lower case after call_user_func_array() + } elseif ($delim == '..') { + $this->debug('in invoke_method, calling class method using call_user_func_array()'); + $call_arg = array ($class, $method); + } else { + $this->debug('in invoke_method, calling instance method using call_user_func_array()'); + $instance = new $class (); + $call_arg = array(&$instance, $method); + } + if (is_array($this->methodparams)) { + $this->methodreturn = call_user_func_array($call_arg, array_values($this->methodparams)); + } else { + $this->methodreturn = call_user_func_array($call_arg, array()); + } + } + $this->debug('in invoke_method, methodreturn:'); + $this->appendDebug($this->varDump($this->methodreturn)); + $this->debug("in invoke_method, called method $this->methodname, received data of type ".gettype($this->methodreturn)); + } + + /** + * serializes the return value from a PHP function into a full SOAP Envelope + * + * The following fields are set by this function (when successful) + * + * responseSOAP + * + * This sets the fault field on error + * + * @access private + */ + function serialize_return() { + $this->debug('Entering serialize_return methodname: ' . $this->methodname . ' methodURI: ' . $this->methodURI); + // if fault + if (isset($this->methodreturn) && is_object($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) { + $this->debug('got a fault object from method'); + $this->fault = $this->methodreturn; + return; + } elseif ($this->methodreturnisliteralxml) { + $return_val = $this->methodreturn; + // returned value(s) + } else { + $this->debug('got a(n) '.gettype($this->methodreturn).' from method'); + $this->debug('serializing return value'); + if($this->wsdl){ + if (sizeof($this->opData['output']['parts']) > 1) { + $this->debug('more than one output part, so use the method return unchanged'); + $opParams = $this->methodreturn; + } elseif (sizeof($this->opData['output']['parts']) == 1) { + $this->debug('exactly one output part, so wrap the method return in a simple array'); + // TODO: verify that it is not already wrapped! + //foreach ($this->opData['output']['parts'] as $name => $type) { + // $this->debug('wrap in element named ' . $name); + //} + $opParams = array($this->methodreturn); + } + $return_val = $this->wsdl->serializeRPCParameters($this->methodname,'output',$opParams); + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if($errstr = $this->wsdl->getError()){ + $this->debug('got wsdl error: '.$errstr); + $this->fault('SOAP-ENV:Server', 'unable to serialize result'); + return; + } + } else { + if (isset($this->methodreturn)) { + $return_val = $this->serialize_val($this->methodreturn, 'return'); + } else { + $return_val = ''; + $this->debug('in absence of WSDL, assume void return for backward compatibility'); + } + } + } + $this->debug('return value:'); + $this->appendDebug($this->varDump($return_val)); + + $this->debug('serializing response'); + if ($this->wsdl) { + $this->debug('have WSDL for serialization: style is ' . $this->opData['style']); + if ($this->opData['style'] == 'rpc') { + $this->debug('style is rpc for serialization: use is ' . $this->opData['output']['use']); + if ($this->opData['output']['use'] == 'literal') { + // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace + if ($this->methodURI) { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } else { + $payload = '<'.$this->methodname.'Response>'.$return_val.'methodname.'Response>'; + } + } else { + if ($this->methodURI) { + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } else { + $payload = '<'.$this->methodname.'Response>'.$return_val.'methodname.'Response>'; + } + } + } else { + $this->debug('style is not rpc for serialization: assume document'); + $payload = $return_val; + } + } else { + $this->debug('do not have WSDL for serialization: assume rpc/encoded'); + $payload = 'methodname.'Response xmlns:ns1="'.$this->methodURI.'">'.$return_val.'methodname."Response>"; + } + $this->result = 'successful'; + if($this->wsdl){ + //if($this->debug_flag){ + $this->appendDebug($this->wsdl->getDebug()); + // } + if (isset($this->opData['output']['encodingStyle'])) { + $encodingStyle = $this->opData['output']['encodingStyle']; + } else { + $encodingStyle = ''; + } + // Added: In case we use a WSDL, return a serialized env. WITH the usedNamespaces. + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders,$this->wsdl->usedNamespaces,$this->opData['style'],$this->opData['output']['use'],$encodingStyle); + } else { + $this->responseSOAP = $this->serializeEnvelope($payload,$this->responseHeaders); + } + $this->debug("Leaving serialize_return"); + } + + /** + * sends an HTTP response + * + * The following fields are set by this function (when successful) + * + * outgoing_headers + * response + * + * @access private + */ + function send_response() { + $this->debug('Enter send_response'); + if ($this->fault) { + $payload = $this->fault->serialize(); + $this->outgoing_headers[] = "HTTP/1.0 500 Internal Server Error"; + $this->outgoing_headers[] = "Status: 500 Internal Server Error"; + } else { + $payload = $this->responseSOAP; + // Some combinations of PHP+Web server allow the Status + // to come through as a header. Since OK is the default + // just do nothing. + // $this->outgoing_headers[] = "HTTP/1.0 200 OK"; + // $this->outgoing_headers[] = "Status: 200 OK"; + } + // add debug data if in debug mode + if(isset($this->debug_flag) && $this->debug_flag){ + $payload .= $this->getDebugAsXMLComment(); + } + $this->outgoing_headers[] = "Server: $this->title Server v$this->version"; + preg_match('/\$Revisio' . 'n: ([^ ]+)/', $this->revision, $rev); + $this->outgoing_headers[] = "X-SOAP-Server: $this->title/$this->version (".$rev[1].")"; + // Let the Web server decide about this + //$this->outgoing_headers[] = "Connection: Close\r\n"; + $payload = $this->getHTTPBody($payload); + $type = $this->getHTTPContentType(); + $charset = $this->getHTTPContentTypeCharset(); + $this->outgoing_headers[] = "Content-Type: $type" . ($charset ? '; charset=' . $charset : ''); + //begin code to compress payload - by John + // NOTE: there is no way to know whether the Web server will also compress + // this data. + if (strlen($payload) > 1024 && isset($this->headers) && isset($this->headers['accept-encoding'])) { + if (strstr($this->headers['accept-encoding'], 'gzip')) { + if (function_exists('gzencode')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: gzip"; + $payload = gzencode($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } elseif (strstr($this->headers['accept-encoding'], 'deflate')) { + // Note: MSIE requires gzdeflate output (no Zlib header and checksum), + // instead of gzcompress output, + // which conflicts with HTTP 1.1 spec (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.5) + if (function_exists('gzdeflate')) { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + $this->outgoing_headers[] = "Content-Encoding: deflate"; + $payload = gzdeflate($payload); + } else { + if (isset($this->debug_flag) && $this->debug_flag) { + $payload .= ""; + } + } + } + } + //end code + $this->outgoing_headers[] = "Content-Length: ".strlen($payload); + reset($this->outgoing_headers); + foreach($this->outgoing_headers as $hdr){ + header($hdr, false); + } + print $payload; + $this->response = join("\r\n",$this->outgoing_headers)."\r\n\r\n".$payload; + } + + /** + * takes the value that was created by parsing the request + * and compares to the method's signature, if available. + * + * @param string $operation The operation to be invoked + * @param array $request The array of parameter values + * @return boolean Whether the operation was found + * @access private + */ + function verify_method($operation,$request){ + if(isset($this->wsdl) && is_object($this->wsdl)){ + if($this->wsdl->getOperationData($operation)){ + return true; + } + } elseif(isset($this->operations[$operation])){ + return true; + } + return false; + } + + /** + * processes SOAP message received from client + * + * @param array $headers The HTTP headers + * @param string $data unprocessed request data from client + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseRequest($headers, $data) { + $this->debug('Entering parseRequest() for data of length ' . strlen($data) . ' headers:'); + $this->appendDebug($this->varDump($headers)); + if (!isset($headers['content-type'])) { + $this->setError('Request not of type text/xml (no content-type header)'); + return false; + } + if (!strstr($headers['content-type'], 'text/xml')) { + $this->setError('Request not of type text/xml'); + return false; + } + if (strpos($headers['content-type'], '=')) { + $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); + $this->debug('Got response encoding: ' . $enc); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); + // parse response, get soap parser obj + $parser = new nusoap_parser($data,$this->xml_encoding,'',$this->decode_utf8); + // parser debug + $this->debug("parser debug: \n".$parser->getDebug()); + // if fault occurred during message parsing + if($err = $parser->getError()){ + $this->result = 'fault: error in msg parsing: '.$err; + $this->fault('SOAP-ENV:Client',"error in msg parsing:\n".$err); + // else successfully parsed request into soapval object + } else { + // get/set methodname + $this->methodURI = $parser->root_struct_namespace; + $this->methodname = $parser->root_struct_name; + $this->debug('methodname: '.$this->methodname.' methodURI: '.$this->methodURI); + $this->debug('calling parser->get_soapbody()'); + $this->methodparams = $parser->get_soapbody(); + // get SOAP headers + $this->requestHeaders = $parser->getHeaders(); + // get SOAP Header + $this->requestHeader = $parser->get_soapheader(); + // add document for doclit support + $this->document = $parser->document; + } + } + + /** + * gets the HTTP body for the current response. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + return $soapmsg; + } + + /** + * gets the HTTP content type for the current response. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current response. + * @access private + */ + function getHTTPContentType() { + return 'text/xml'; + } + + /** + * gets the HTTP content type charset for the current response. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current response. + * @access private + */ + function getHTTPContentTypeCharset() { + return $this->soap_defencoding; + } + + /** + * add a method to the dispatch map (this has been replaced by the register method) + * + * @param string $methodname + * @param string $in array of input values + * @param string $out array of output values + * @access public + * @deprecated + */ + function add_to_map($methodname,$in,$out){ + $this->operations[$methodname] = array('name' => $methodname,'in' => $in,'out' => $out); + } + + /** + * register a service function with the server + * + * @param string $name the name of the PHP function, class.method or class..method + * @param array $in assoc array of input values: key = param name, value = param type + * @param array $out assoc array of output values: key = param name, value = param type + * @param mixed $namespace the element namespace for the method or false + * @param mixed $soapaction the soapaction for the method or false + * @param mixed $style optional (rpc|document) or false Note: when 'document' is specified, parameter and return wrappers are created for you automatically + * @param mixed $use optional (encoded|literal) or false + * @param string $documentation optional Description to include in WSDL + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @access public + */ + function register($name,$in=array(),$out=array(),$namespace=false,$soapaction=false,$style=false,$use=false,$documentation='',$encodingStyle=''){ + global $HTTP_SERVER_VARS; + + if($this->externalWSDLURL){ + die('You cannot bind to an external WSDL file, and register methods outside of it! Please choose either WSDL or no WSDL.'); + } + if (! $name) { + die('You must specify a name when you register an operation'); + } + if (!is_array($in)) { + die('You must provide an array for operation inputs'); + } + if (!is_array($out)) { + die('You must provide an array for operation outputs'); + } + if(false == $namespace) { + } + if(false == $soapaction) { + if (isset($_SERVER)) { + $SERVER_NAME = $_SERVER['SERVER_NAME']; + $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; + $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); + } elseif (isset($HTTP_SERVER_VARS)) { + $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; + $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; + $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + if ($HTTPS == '1' || $HTTPS == 'on') { + $SCHEME = 'https'; + } else { + $SCHEME = 'http'; + } + $soapaction = "$SCHEME://$SERVER_NAME$SCRIPT_NAME/$name"; + } + if(false == $style) { + $style = "rpc"; + } + if(false == $use) { + $use = "encoded"; + } + if ($use == 'encoded' && $encodingStyle == '') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } + + $this->operations[$name] = array( + 'name' => $name, + 'in' => $in, + 'out' => $out, + 'namespace' => $namespace, + 'soapaction' => $soapaction, + 'style' => $style); + if($this->wsdl){ + $this->wsdl->addOperation($name,$in,$out,$namespace,$soapaction,$style,$use,$documentation,$encodingStyle); + } + return true; + } + + /** + * Specify a fault to be returned to the client. + * This also acts as a flag to the server that a fault has occured. + * + * @param string $faultcode + * @param string $faultstring + * @param string $faultactor + * @param string $faultdetail + * @access public + */ + function fault($faultcode,$faultstring,$faultactor='',$faultdetail=''){ + if ($faultdetail == '' && $this->debug_flag) { + $faultdetail = $this->getDebug(); + } + $this->fault = new nusoap_fault($faultcode,$faultactor,$faultstring,$faultdetail); + $this->fault->soap_defencoding = $this->soap_defencoding; + } + + /** + * Sets up wsdl object. + * Acts as a flag to enable internal WSDL generation + * + * @param string $serviceName, name of the service + * @param mixed $namespace optional 'tns' service namespace or false + * @param mixed $endpoint optional URL of service endpoint or false + * @param string $style optional (rpc|document) WSDL style (also specified by operation) + * @param string $transport optional SOAP transport + * @param mixed $schemaTargetNamespace optional 'types' targetNamespace for service schema or false + */ + function configureWSDL($serviceName,$namespace = false,$endpoint = false,$style='rpc', $transport = 'http://schemas.xmlsoap.org/soap/http', $schemaTargetNamespace = false) + { + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $SERVER_NAME = $_SERVER['SERVER_NAME']; + $SERVER_PORT = $_SERVER['SERVER_PORT']; + $SCRIPT_NAME = isset($_SERVER['PHP_SELF']) ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; + $HTTPS = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : (isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'); + } elseif (isset($HTTP_SERVER_VARS)) { + $SERVER_NAME = $HTTP_SERVER_VARS['SERVER_NAME']; + $SERVER_PORT = $HTTP_SERVER_VARS['SERVER_PORT']; + $SCRIPT_NAME = isset($HTTP_SERVER_VARS['PHP_SELF']) ? $HTTP_SERVER_VARS['PHP_SELF'] : $HTTP_SERVER_VARS['SCRIPT_NAME']; + $HTTPS = isset($HTTP_SERVER_VARS['HTTPS']) ? $HTTP_SERVER_VARS['HTTPS'] : 'off'; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + // If server name has port number attached then strip it (else port number gets duplicated in WSDL output) (occurred using lighttpd and FastCGI) + $colon = strpos($SERVER_NAME,":"); + if ($colon) { + $SERVER_NAME = substr($SERVER_NAME, 0, $colon); + } + if ($SERVER_PORT == 80) { + $SERVER_PORT = ''; + } else { + $SERVER_PORT = ':' . $SERVER_PORT; + } + if(false == $namespace) { + $namespace = "http://$SERVER_NAME/soap/$serviceName"; + } + + if(false == $endpoint) { + if ($HTTPS == '1' || $HTTPS == 'on') { + $SCHEME = 'https'; + } else { + $SCHEME = 'http'; + } + $endpoint = "$SCHEME://$SERVER_NAME$SERVER_PORT$SCRIPT_NAME"; + } + + if(false == $schemaTargetNamespace) { + $schemaTargetNamespace = $namespace; + } + + $this->wsdl = new wsdl; + $this->wsdl->serviceName = $serviceName; + $this->wsdl->endpoint = $endpoint; + $this->wsdl->namespaces['tns'] = $namespace; + $this->wsdl->namespaces['soap'] = 'http://schemas.xmlsoap.org/wsdl/soap/'; + $this->wsdl->namespaces['wsdl'] = 'http://schemas.xmlsoap.org/wsdl/'; + if ($schemaTargetNamespace != $namespace) { + $this->wsdl->namespaces['types'] = $schemaTargetNamespace; + } + $this->wsdl->schemas[$schemaTargetNamespace][0] = new nusoap_xmlschema('', '', $this->wsdl->namespaces); + if ($style == 'document') { + $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaInfo['elementFormDefault'] = 'qualified'; + } + $this->wsdl->schemas[$schemaTargetNamespace][0]->schemaTargetNamespace = $schemaTargetNamespace; + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/soap/encoding/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->schemas[$schemaTargetNamespace][0]->imports['http://schemas.xmlsoap.org/wsdl/'][0] = array('location' => '', 'loaded' => true); + $this->wsdl->bindings[$serviceName.'Binding'] = array( + 'name'=>$serviceName.'Binding', + 'style'=>$style, + 'transport'=>$transport, + 'portType'=>$serviceName.'PortType'); + $this->wsdl->ports[$serviceName.'Port'] = array( + 'binding'=>$serviceName.'Binding', + 'location'=>$endpoint, + 'bindingType'=>'http://schemas.xmlsoap.org/wsdl/soap/'); + } +} + +/** + * Backward compatibility + */ +class soap_server extends nusoap_server { +} + +?> +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class wsdl extends nusoap_base { + // URL or filename of the root of this WSDL + var $wsdl; + // define internal arrays of bindings, ports, operations, messages, etc. + var $schemas = array(); + var $currentSchema; + var $message = array(); + var $complexTypes = array(); + var $messages = array(); + var $currentMessage; + var $currentOperation; + var $portTypes = array(); + var $currentPortType; + var $bindings = array(); + var $currentBinding; + var $ports = array(); + var $currentPort; + var $opData = array(); + var $status = ''; + var $documentation = false; + var $endpoint = ''; + // array of wsdl docs to import + var $import = array(); + // parser vars + var $parser; + var $position = 0; + var $depth = 0; + var $depth_array = array(); + // for getting wsdl + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $timeout = 0; + var $response_timeout = 30; + var $curl_options = array(); // User-specified cURL options + var $use_curl = false; // whether to always try to use cURL + // for HTTP authentication + var $username = ''; // Username for HTTP authentication + var $password = ''; // Password for HTTP authentication + var $authtype = ''; // Type of HTTP authentication + var $certRequest = array(); // Certificate for HTTP SSL authentication + + /** + * constructor + * + * @param string $wsdl WSDL document URL + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @param array $curl_options user-specified cURL options + * @param boolean $use_curl try to use cURL + * @access public + */ + function wsdl($wsdl = '',$proxyhost=false,$proxyport=false,$proxyusername=false,$proxypassword=false,$timeout=0,$response_timeout=30,$curl_options=null,$use_curl=false){ + parent::nusoap_base(); + $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout"); + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + if (is_array($curl_options)) + $this->curl_options = $curl_options; + $this->use_curl = $use_curl; + $this->fetchWSDL($wsdl); + } + + /** + * fetches the WSDL document and parses it + * + * @access public + */ + function fetchWSDL($wsdl) { + $this->debug("parse and process WSDL path=$wsdl"); + $this->wsdl = $wsdl; + // parse wsdl file + if ($this->wsdl != "") { + $this->parseWSDL($this->wsdl); + } + // imports + // TODO: handle imports more properly, grabbing them in-line and nesting them + $imported_urls = array(); + $imported = 1; + while ($imported > 0) { + $imported = 0; + // Schema imports + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($xs->imports as $ns2 => $list2) { + for ($ii = 0; $ii < count($list2); $ii++) { + if (! $list2[$ii]['loaded']) { + $this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true; + $url = $list2[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' .$wsdlparts['port'] : '') . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + } + // WSDL imports + $wsdlparts = parse_url($this->wsdl); // this is bogusly simple! + foreach ($this->import as $ns => $list) { + for ($ii = 0; $ii < count($list); $ii++) { + if (! $list[$ii]['loaded']) { + $this->import[$ns][$ii]['loaded'] = true; + $url = $list[$ii]['location']; + if ($url != '') { + $urlparts = parse_url($url); + if (!isset($urlparts['host'])) { + $url = $wsdlparts['scheme'] . '://' . $wsdlparts['host'] . (isset($wsdlparts['port']) ? ':' . $wsdlparts['port'] : '') . + substr($wsdlparts['path'],0,strrpos($wsdlparts['path'],'/') + 1) .$urlparts['path']; + } + if (! in_array($url, $imported_urls)) { + $this->parseWSDL($url); + $imported++; + $imported_urls[] = $url; + } + } else { + $this->debug("Unexpected scenario: empty URL for unloaded import"); + } + } + } + } + } + // add new data to operation data + foreach($this->bindings as $binding => $bindingData) { + if (isset($bindingData['operations']) && is_array($bindingData['operations'])) { + foreach($bindingData['operations'] as $operation => $data) { + $this->debug('post-parse data gathering for ' . $operation); + $this->bindings[$binding]['operations'][$operation]['input'] = + isset($this->bindings[$binding]['operations'][$operation]['input']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['input'], $this->portTypes[ $bindingData['portType'] ][$operation]['input']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['input']; + $this->bindings[$binding]['operations'][$operation]['output'] = + isset($this->bindings[$binding]['operations'][$operation]['output']) ? + array_merge($this->bindings[$binding]['operations'][$operation]['output'], $this->portTypes[ $bindingData['portType'] ][$operation]['output']) : + $this->portTypes[ $bindingData['portType'] ][$operation]['output']; + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['input']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['input']['message'] ]; + } + if(isset($this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ])){ + $this->bindings[$binding]['operations'][$operation]['output']['parts'] = $this->messages[ $this->bindings[$binding]['operations'][$operation]['output']['message'] ]; + } + // Set operation style if necessary, but do not override one already provided + if (isset($bindingData['style']) && !isset($this->bindings[$binding]['operations'][$operation]['style'])) { + $this->bindings[$binding]['operations'][$operation]['style'] = $bindingData['style']; + } + $this->bindings[$binding]['operations'][$operation]['transport'] = isset($bindingData['transport']) ? $bindingData['transport'] : ''; + $this->bindings[$binding]['operations'][$operation]['documentation'] = isset($this->portTypes[ $bindingData['portType'] ][$operation]['documentation']) ? $this->portTypes[ $bindingData['portType'] ][$operation]['documentation'] : ''; + $this->bindings[$binding]['operations'][$operation]['endpoint'] = isset($bindingData['endpoint']) ? $bindingData['endpoint'] : ''; + } + } + } + } + + /** + * parses the wsdl document + * + * @param string $wsdl path or URL + * @access private + */ + function parseWSDL($wsdl = '') { + $this->debug("parse WSDL at path=$wsdl"); + + if ($wsdl == '') { + $this->debug('no wsdl passed to parseWSDL()!!'); + $this->setError('no wsdl passed to parseWSDL()!!'); + return false; + } + + // parse $wsdl for url format + $wsdl_props = parse_url($wsdl); + + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'http' || $wsdl_props['scheme'] == 'https')) { + $this->debug('getting WSDL http(s) URL ' . $wsdl); + // get wsdl + $tr = new soap_transport_http($wsdl, $this->curl_options, $this->use_curl); + $tr->request_method = 'GET'; + $tr->useSOAPAction = false; + if($this->proxyhost && $this->proxyport){ + $tr->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if ($this->authtype != '') { + $tr->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest); + } + $tr->setEncoding('gzip, deflate'); + $wsdl_string = $tr->send('', $this->timeout, $this->response_timeout); + //$this->debug("WSDL request\n" . $tr->outgoing_payload); + //$this->debug("WSDL response\n" . $tr->incoming_payload); + $this->appendDebug($tr->getDebug()); + // catch errors + if($err = $tr->getError() ){ + $errstr = 'Getting ' . $wsdl . ' - HTTP ERROR: '.$err; + $this->debug($errstr); + $this->setError($errstr); + unset($tr); + return false; + } + unset($tr); + $this->debug("got WSDL URL"); + } else { + // $wsdl is not http(s), so treat it as a file URL or plain file path + if (isset($wsdl_props['scheme']) && ($wsdl_props['scheme'] == 'file') && isset($wsdl_props['path'])) { + $path = isset($wsdl_props['host']) ? ($wsdl_props['host'] . ':' . $wsdl_props['path']) : $wsdl_props['path']; + } else { + $path = $wsdl; + } + $this->debug('getting WSDL file ' . $path); + if ($fp = @fopen($path, 'r')) { + $wsdl_string = ''; + while ($data = fread($fp, 32768)) { + $wsdl_string .= $data; + } + fclose($fp); + } else { + $errstr = "Bad path to WSDL file $path"; + $this->debug($errstr); + $this->setError($errstr); + return false; + } + } + $this->debug('Parse WSDL'); + // end new code added + // Create an XML parser. + $this->parser = xml_parser_create(); + // Set the options for parsing the XML data. + // xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element', 'end_element'); + xml_set_character_data_handler($this->parser, 'character_data'); + // Parse the XML file. + if (!xml_parse($this->parser, $wsdl_string, true)) { + // Display an error message. + $errstr = sprintf( + 'XML error parsing WSDL from %s on line %d: %s', + $wsdl, + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser)) + ); + $this->debug($errstr); + $this->debug("XML payload:\n" . $wsdl_string); + $this->setError($errstr); + return false; + } + // free the parser + xml_parser_free($this->parser); + $this->debug('Parsing WSDL done'); + // catch wsdl parse errors + if($this->getError()){ + return false; + } + return true; + } + + /** + * start-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @param string $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) + { + if ($this->status == 'schema') { + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + } elseif (preg_match('/schema$/', $name)) { + $this->debug('Parsing WSDL schema'); + // $this->debug("startElement for $name ($attrs[name]). status = $this->status (".$this->getLocalPart($name).")"); + $this->status = 'schema'; + $this->currentSchema = new nusoap_xmlschema('', '', $this->namespaces); + $this->currentSchema->schemaStartElement($parser, $name, $attrs); + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + } else { + // position in the total number of elements, starting from 0 + $pos = $this->position++; + $depth = $this->depth++; + // set self as current value for this depth + $this->depth_array[$depth] = $pos; + $this->message[$pos] = array('cdata' => ''); + // process attributes + if (count($attrs) > 0) { + // register namespace declarations + foreach($attrs as $k => $v) { + if (preg_match('/^xmlns/',$k)) { + if ($ns_prefix = substr(strrchr($k, ':'), 1)) { + $this->namespaces[$ns_prefix] = $v; + } else { + $this->namespaces['ns' . (count($this->namespaces) + 1)] = $v; + } + if ($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema') { + $this->XMLSchemaVersion = $v; + $this->namespaces['xsi'] = $v . '-instance'; + } + } + } + // expand each attribute prefix to its namespace + foreach($attrs as $k => $v) { + $k = strpos($k, ':') ? $this->expandQname($k) : $k; + if ($k != 'location' && $k != 'soapAction' && $k != 'namespace') { + $v = strpos($v, ':') ? $this->expandQname($v) : $v; + } + $eAttrs[$k] = $v; + } + $attrs = $eAttrs; + } else { + $attrs = array(); + } + // get element prefix, namespace and name + if (preg_match('/:/', $name)) { + // get ns prefix + $prefix = substr($name, 0, strpos($name, ':')); + // get ns + $namespace = isset($this->namespaces[$prefix]) ? $this->namespaces[$prefix] : ''; + // get unqualified name + $name = substr(strstr($name, ':'), 1); + } + // process attributes, expanding any prefixes to namespaces + // find status, register data + switch ($this->status) { + case 'message': + if ($name == 'part') { + if (isset($attrs['type'])) { + $this->debug("msg " . $this->currentMessage . ": found part (with type) $attrs[name]: " . implode(',', $attrs)); + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['type']; + } + if (isset($attrs['element'])) { + $this->debug("msg " . $this->currentMessage . ": found part (with element) $attrs[name]: " . implode(',', $attrs)); + $this->messages[$this->currentMessage][$attrs['name']] = $attrs['element'] . '^'; + } + } + break; + case 'portType': + switch ($name) { + case 'operation': + $this->currentPortOperation = $attrs['name']; + $this->debug("portType $this->currentPortType operation: $this->currentPortOperation"); + if (isset($attrs['parameterOrder'])) { + $this->portTypes[$this->currentPortType][$attrs['name']]['parameterOrder'] = $attrs['parameterOrder']; + } + break; + case 'documentation': + $this->documentation = true; + break; + // merge input/output data + default: + $m = isset($attrs['message']) ? $this->getLocalPart($attrs['message']) : ''; + $this->portTypes[$this->currentPortType][$this->currentPortOperation][$name]['message'] = $m; + break; + } + break; + case 'binding': + switch ($name) { + case 'binding': + // get ns prefix + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['prefix'] = $prefix; + } + $this->bindings[$this->currentBinding] = array_merge($this->bindings[$this->currentBinding], $attrs); + break; + case 'header': + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus]['headers'][] = $attrs; + break; + case 'operation': + if (isset($attrs['soapAction'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['soapAction'] = $attrs['soapAction']; + } + if (isset($attrs['style'])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['style'] = $attrs['style']; + } + if (isset($attrs['name'])) { + $this->currentOperation = $attrs['name']; + $this->debug("current binding operation: $this->currentOperation"); + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['name'] = $attrs['name']; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['binding'] = $this->currentBinding; + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation]['endpoint'] = isset($this->bindings[$this->currentBinding]['endpoint']) ? $this->bindings[$this->currentBinding]['endpoint'] : ''; + } + break; + case 'input': + $this->opStatus = 'input'; + break; + case 'output': + $this->opStatus = 'output'; + break; + case 'body': + if (isset($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus])) { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = array_merge($this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus], $attrs); + } else { + $this->bindings[$this->currentBinding]['operations'][$this->currentOperation][$this->opStatus] = $attrs; + } + break; + } + break; + case 'service': + switch ($name) { + case 'port': + $this->currentPort = $attrs['name']; + $this->debug('current port: ' . $this->currentPort); + $this->ports[$this->currentPort]['binding'] = $this->getLocalPart($attrs['binding']); + + break; + case 'address': + $this->ports[$this->currentPort]['location'] = $attrs['location']; + $this->ports[$this->currentPort]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['bindingType'] = $namespace; + $this->bindings[ $this->ports[$this->currentPort]['binding'] ]['endpoint'] = $attrs['location']; + break; + } + break; + } + // set status + switch ($name) { + case 'import': + if (isset($attrs['location'])) { + $this->import[$attrs['namespace']][] = array('location' => $attrs['location'], 'loaded' => false); + $this->debug('parsing import ' . $attrs['namespace']. ' - ' . $attrs['location'] . ' (' . count($this->import[$attrs['namespace']]).')'); + } else { + $this->import[$attrs['namespace']][] = array('location' => '', 'loaded' => true); + if (! $this->getPrefixFromNamespace($attrs['namespace'])) { + $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; + } + $this->debug('parsing import ' . $attrs['namespace']. ' - [no location] (' . count($this->import[$attrs['namespace']]).')'); + } + break; + //wait for schema + //case 'types': + // $this->status = 'schema'; + // break; + case 'message': + $this->status = 'message'; + $this->messages[$attrs['name']] = array(); + $this->currentMessage = $attrs['name']; + break; + case 'portType': + $this->status = 'portType'; + $this->portTypes[$attrs['name']] = array(); + $this->currentPortType = $attrs['name']; + break; + case "binding": + if (isset($attrs['name'])) { + // get binding name + if (strpos($attrs['name'], ':')) { + $this->currentBinding = $this->getLocalPart($attrs['name']); + } else { + $this->currentBinding = $attrs['name']; + } + $this->status = 'binding'; + $this->bindings[$this->currentBinding]['portType'] = $this->getLocalPart($attrs['type']); + $this->debug("current binding: $this->currentBinding of portType: " . $attrs['type']); + } + break; + case 'service': + $this->serviceName = $attrs['name']; + $this->status = 'service'; + $this->debug('current service: ' . $this->serviceName); + break; + case 'definitions': + foreach ($attrs as $name => $value) { + $this->wsdl_info[$name] = $value; + } + break; + } + } + } + + /** + * end-element handler + * + * @param string $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name){ + // unset schema status + if (/*preg_match('/types$/', $name) ||*/ preg_match('/schema$/', $name)) { + $this->status = ""; + $this->appendDebug($this->currentSchema->getDebug()); + $this->currentSchema->clearDebug(); + $this->schemas[$this->currentSchema->schemaTargetNamespace][] = $this->currentSchema; + $this->debug('Parsing WSDL schema done'); + } + if ($this->status == 'schema') { + $this->currentSchema->schemaEndElement($parser, $name); + } else { + // bring depth down a notch + $this->depth--; + } + // end documentation + if ($this->documentation) { + //TODO: track the node to which documentation should be assigned; it can be a part, message, etc. + //$this->portTypes[$this->currentPortType][$this->currentPortOperation]['documentation'] = $this->documentation; + $this->documentation = false; + } + } + + /** + * element content handler + * + * @param string $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data) + { + $pos = isset($this->depth_array[$this->depth]) ? $this->depth_array[$this->depth] : 0; + if (isset($this->message[$pos]['cdata'])) { + $this->message[$pos]['cdata'] .= $data; + } + if ($this->documentation) { + $this->documentation .= $data; + } + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, certpassword (optional), verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype certRequest="); + $this->appendDebug($this->varDump($certRequest)); + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->certRequest = $certRequest; + } + + function getBindingData($binding) + { + if (is_array($this->bindings[$binding])) { + return $this->bindings[$binding]; + } + } + + /** + * returns an assoc array of operation names => operation data + * + * @param string $portName WSDL port name + * @param string $bindingType eg: soap, smtp, dime (only soap and soap12 are currently supported) + * @return array + * @access public + */ + function getOperations($portName = '', $bindingType = 'soap') { + $ops = array(); + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } else { + $this->debug("getOperations bindingType $bindingType may not be supported"); + } + $this->debug("getOperations for port '$portName' bindingType $bindingType"); + // loop thru ports + foreach($this->ports as $port => $portData) { + $this->debug("getOperations checking port $port bindingType " . $portData['bindingType']); + if ($portName == '' || $port == $portName) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + $this->debug("getOperations found port $port bindingType $bindingType"); + //$this->debug("port data: " . $this->varDump($portData)); + //$this->debug("bindings: " . $this->varDump($this->bindings[ $portData['binding'] ])); + // merge bindings + if (isset($this->bindings[ $portData['binding'] ]['operations'])) { + $ops = array_merge ($ops, $this->bindings[ $portData['binding'] ]['operations']); + } + } + } + } + if (count($ops) == 0) { + $this->debug("getOperations found no operations for port '$portName' bindingType $bindingType"); + } + return $ops; + } + + /** + * returns an associative array of data necessary for calling an operation + * + * @param string $operation name of operation + * @param string $bindingType type of binding eg: soap, soap12 + * @return array + * @access public + */ + function getOperationData($operation, $bindingType = 'soap') + { + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + // get binding + //foreach($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { + foreach(array_keys($this->bindings[ $portData['binding'] ]['operations']) as $bOperation) { + // note that we could/should also check the namespace here + if ($operation == $bOperation) { + $opData = $this->bindings[ $portData['binding'] ]['operations'][$operation]; + return $opData; + } + } + } + } + } + + /** + * returns an associative array of data necessary for calling an operation + * + * @param string $soapAction soapAction for operation + * @param string $bindingType type of binding eg: soap, soap12 + * @return array + * @access public + */ + function getOperationDataForSoapAction($soapAction, $bindingType = 'soap') { + if ($bindingType == 'soap') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap/'; + } elseif ($bindingType == 'soap12') { + $bindingType = 'http://schemas.xmlsoap.org/wsdl/soap12/'; + } + // loop thru ports + foreach($this->ports as $port => $portData) { + // binding type of port matches parameter + if ($portData['bindingType'] == $bindingType) { + // loop through operations for the binding + foreach ($this->bindings[ $portData['binding'] ]['operations'] as $bOperation => $opData) { + if ($opData['soapAction'] == $soapAction) { + return $opData; + } + } + } + } + } + + /** + * returns an array of information about a given type + * returns false if no type exists by the given name + * + * typeDef = array( + * 'elements' => array(), // refs to elements array + * 'restrictionBase' => '', + * 'phpType' => '', + * 'order' => '(sequence|all)', + * 'attrs' => array() // refs to attributes array + * ) + * + * @param string $type the type + * @param string $ns namespace (not prefix) of the type + * @return mixed + * @access public + * @see nusoap_xmlschema + */ + function getTypeDef($type, $ns) { + $this->debug("in getTypeDef: type=$type, ns=$ns"); + if ((! $ns) && isset($this->namespaces['tns'])) { + $ns = $this->namespaces['tns']; + $this->debug("in getTypeDef: type namespace forced to $ns"); + } + if (!isset($this->schemas[$ns])) { + foreach ($this->schemas as $ns0 => $schema0) { + if (strcasecmp($ns, $ns0) == 0) { + $this->debug("in getTypeDef: replacing schema namespace $ns with $ns0"); + $ns = $ns0; + break; + } + } + } + if (isset($this->schemas[$ns])) { + $this->debug("in getTypeDef: have schema for namespace $ns"); + for ($i = 0; $i < count($this->schemas[$ns]); $i++) { + $xs = &$this->schemas[$ns][$i]; + $t = $xs->getTypeDef($type); + $this->appendDebug($xs->getDebug()); + $xs->clearDebug(); + if ($t) { + $this->debug("in getTypeDef: found type $type"); + if (!isset($t['phpType'])) { + // get info for type to tack onto the element + $uqType = substr($t['type'], strrpos($t['type'], ':') + 1); + $ns = substr($t['type'], 0, strrpos($t['type'], ':')); + $etype = $this->getTypeDef($uqType, $ns); + if ($etype) { + $this->debug("found type for [element] $type:"); + $this->debug($this->varDump($etype)); + if (isset($etype['phpType'])) { + $t['phpType'] = $etype['phpType']; + } + if (isset($etype['elements'])) { + $t['elements'] = $etype['elements']; + } + if (isset($etype['attrs'])) { + $t['attrs'] = $etype['attrs']; + } + } else { + $this->debug("did not find type for [element] $type"); + } + } + return $t; + } + } + $this->debug("in getTypeDef: did not find type $type"); + } else { + $this->debug("in getTypeDef: do not have schema for namespace $ns"); + } + return false; + } + + /** + * prints html description of services + * + * @access private + */ + function webDescription(){ + global $HTTP_SERVER_VARS; + + if (isset($_SERVER)) { + $PHP_SELF = $_SERVER['PHP_SELF']; + } elseif (isset($HTTP_SERVER_VARS)) { + $PHP_SELF = $HTTP_SERVER_VARS['PHP_SELF']; + } else { + $this->setError("Neither _SERVER nor HTTP_SERVER_VARS is available"); + } + + $b = ' + NuSOAP: '.$this->serviceName.' + + + + +
+

+
'.$this->serviceName.'
+ +
'; + return $b; + } + + /** + * serialize the parsed wsdl + * + * @param mixed $debug whether to put debug=1 in endpoint URL + * @return string serialization of WSDL + * @access public + */ + function serialize($debug = 0) + { + $xml = ''; + $xml .= "\nnamespaces as $k => $v) { + $xml .= " xmlns:$k=\"$v\""; + } + // 10.9.02 - add poulter fix for wsdl and tns declarations + if (isset($this->namespaces['wsdl'])) { + $xml .= " xmlns=\"" . $this->namespaces['wsdl'] . "\""; + } + if (isset($this->namespaces['tns'])) { + $xml .= " targetNamespace=\"" . $this->namespaces['tns'] . "\""; + } + $xml .= '>'; + // imports + if (sizeof($this->import) > 0) { + foreach($this->import as $ns => $list) { + foreach ($list as $ii) { + if ($ii['location'] != '') { + $xml .= ''; + } else { + $xml .= ''; + } + } + } + } + // types + if (count($this->schemas)>=1) { + $xml .= "\n\n"; + foreach ($this->schemas as $ns => $list) { + foreach ($list as $xs) { + $xml .= $xs->serializeSchema(); + } + } + $xml .= ''; + } + // messages + if (count($this->messages) >= 1) { + foreach($this->messages as $msgName => $msgParts) { + $xml .= "\n'; + if(is_array($msgParts)){ + foreach($msgParts as $partName => $partType) { + // print 'serializing '.$partType.', sv: '.$this->XMLSchemaVersion.'
'; + if (strpos($partType, ':')) { + $typePrefix = $this->getPrefixFromNamespace($this->getPrefix($partType)); + } elseif (isset($this->typemap[$this->namespaces['xsd']][$partType])) { + // print 'checking typemap: '.$this->XMLSchemaVersion.'
'; + $typePrefix = 'xsd'; + } else { + foreach($this->typemap as $ns => $types) { + if (isset($types[$partType])) { + $typePrefix = $this->getPrefixFromNamespace($ns); + } + } + if (!isset($typePrefix)) { + die("$partType has no namespace!"); + } + } + $ns = $this->getNamespaceFromPrefix($typePrefix); + $localPart = $this->getLocalPart($partType); + $typeDef = $this->getTypeDef($localPart, $ns); + if ($typeDef['typeClass'] == 'element') { + $elementortype = 'element'; + if (substr($localPart, -1) == '^') { + $localPart = substr($localPart, 0, -1); + } + } else { + $elementortype = 'type'; + } + $xml .= "\n" . ' '; + } + } + $xml .= '
'; + } + } + // bindings & porttypes + if (count($this->bindings) >= 1) { + $binding_xml = ''; + $portType_xml = ''; + foreach($this->bindings as $bindingName => $attrs) { + $binding_xml .= "\n'; + $binding_xml .= "\n" . ' '; + $portType_xml .= "\n'; + foreach($attrs['operations'] as $opName => $opParts) { + $binding_xml .= "\n" . ' '; + $binding_xml .= "\n" . ' '; + if (isset($opParts['input']['encodingStyle']) && $opParts['input']['encodingStyle'] != '') { + $enc_style = ' encodingStyle="' . $opParts['input']['encodingStyle'] . '"'; + } else { + $enc_style = ''; + } + $binding_xml .= "\n" . ' '; + if (isset($opParts['output']['encodingStyle']) && $opParts['output']['encodingStyle'] != '') { + $enc_style = ' encodingStyle="' . $opParts['output']['encodingStyle'] . '"'; + } else { + $enc_style = ''; + } + $binding_xml .= "\n" . ' '; + $binding_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' ' . htmlspecialchars($opParts['documentation']) . ''; + } + $portType_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' '; + $portType_xml .= "\n" . ' '; + } + $portType_xml .= "\n" . ''; + $binding_xml .= "\n" . ''; + } + $xml .= $portType_xml . $binding_xml; + } + // services + $xml .= "\nserviceName . '">'; + if (count($this->ports) >= 1) { + foreach($this->ports as $pName => $attrs) { + $xml .= "\n" . ' '; + $xml .= "\n" . ' '; + $xml .= "\n" . ' '; + } + } + $xml .= "\n" . ''; + return $xml . "\n"; + } + + /** + * determine whether a set of parameters are unwrapped + * when they are expect to be wrapped, Microsoft-style. + * + * @param string $type the type (element name) of the wrapper + * @param array $parameters the parameter values for the SOAP call + * @return boolean whether they parameters are unwrapped (and should be wrapped) + * @access private + */ + function parametersMatchWrapped($type, &$parameters) { + $this->debug("in parametersMatchWrapped type=$type, parameters="); + $this->appendDebug($this->varDump($parameters)); + + // split type into namespace:unqualified-type + if (strpos($type, ':')) { + $uqType = substr($type, strrpos($type, ':') + 1); + $ns = substr($type, 0, strrpos($type, ':')); + $this->debug("in parametersMatchWrapped: got a prefixed type: $uqType, $ns"); + if ($this->getNamespaceFromPrefix($ns)) { + $ns = $this->getNamespaceFromPrefix($ns); + $this->debug("in parametersMatchWrapped: expanded prefixed type: $uqType, $ns"); + } + } else { + // TODO: should the type be compared to types in XSD, and the namespace + // set to XSD if the type matches? + $this->debug("in parametersMatchWrapped: No namespace for type $type"); + $ns = ''; + $uqType = $type; + } + + // get the type information + if (!$typeDef = $this->getTypeDef($uqType, $ns)) { + $this->debug("in parametersMatchWrapped: $type ($uqType) is not a supported type."); + return false; + } + $this->debug("in parametersMatchWrapped: found typeDef="); + $this->appendDebug($this->varDump($typeDef)); + if (substr($uqType, -1) == '^') { + $uqType = substr($uqType, 0, -1); + } + $phpType = $typeDef['phpType']; + $arrayType = (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : ''); + $this->debug("in parametersMatchWrapped: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: $arrayType"); + + // we expect a complexType or element of complexType + if ($phpType != 'struct') { + $this->debug("in parametersMatchWrapped: not a struct"); + return false; + } + + // see whether the parameter names match the elements + if (isset($typeDef['elements']) && is_array($typeDef['elements'])) { + $elements = 0; + $matches = 0; + foreach ($typeDef['elements'] as $name => $attrs) { + if (isset($parameters[$name])) { + $this->debug("in parametersMatchWrapped: have parameter named $name"); + $matches++; + } else { + $this->debug("in parametersMatchWrapped: do not have parameter named $name"); + } + $elements++; + } + + $this->debug("in parametersMatchWrapped: $matches parameter names match $elements wrapped parameter names"); + if ($matches == 0) { + return false; + } + return true; + } + + // since there are no elements for the type, if the user passed no + // parameters, the parameters match wrapped. + $this->debug("in parametersMatchWrapped: no elements type $ns:$uqType"); + return count($parameters) == 0; + } + + /** + * serialize PHP values according to a WSDL message definition + * contrary to the method name, this is not limited to RPC + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $operation operation name + * @param string $direction (input|output) + * @param mixed $parameters parameter value(s) + * @param string $bindingType (soap|soap12) + * @return mixed parameters serialized as XML or false on error (e.g. operation not found) + * @access public + */ + function serializeRPCParameters($operation, $direction, $parameters, $bindingType = 'soap') { + $this->debug("in serializeRPCParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion, bindingType=$bindingType"); + $this->appendDebug('parameters=' . $this->varDump($parameters)); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation, $bindingType)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation . ' bindingType: ' . $bindingType); + return false; + } + $this->debug('in serializeRPCParameters: opData:'); + $this->appendDebug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + $parts = &$opData[$direction]['parts']; + $part_count = sizeof($parts); + $style = $opData['style']; + $use = $opData[$direction]['use']; + $this->debug("have $part_count part(s) to serialize using $style/$use"); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $parameter_count = count($parameters); + $this->debug("have $parameter_count parameter(s) provided as $parametersArrayType to serialize"); + // check for Microsoft-style wrapped parameters + if ($style == 'document' && $use == 'literal' && $part_count == 1 && isset($parts['parameters'])) { + $this->debug('check whether the caller has wrapped the parameters'); + if ($direction == 'output' && $parametersArrayType == 'arraySimple' && $parameter_count == 1) { + // TODO: consider checking here for double-wrapping, when + // service function wraps, then NuSOAP wraps again + $this->debug("change simple array to associative with 'parameters' element"); + $parameters['parameters'] = $parameters[0]; + unset($parameters[0]); + } + if (($parametersArrayType == 'arrayStruct' || $parameter_count == 0) && !isset($parameters['parameters'])) { + $this->debug('check whether caller\'s parameters match the wrapped ones'); + if ($this->parametersMatchWrapped($parts['parameters'], $parameters)) { + $this->debug('wrap the parameters for the caller'); + $parameters = array('parameters' => $parameters); + $parameter_count = 1; + } + } + } + foreach ($parts as $name => $type) { + $this->debug("serializing part $name of type $type"); + // Track encoding style + if (isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + $this->debug("serializeRPCParameters returning: $xml"); + return $xml; + } + + /** + * serialize a PHP value according to a WSDL message definition + * + * TODO + * - multi-ref serialization + * - validate PHP values against type definitions, return errors if invalid + * + * @param string $operation operation name + * @param string $direction (input|output) + * @param mixed $parameters parameter value(s) + * @return mixed parameters serialized as XML or false on error (e.g. operation not found) + * @access public + * @deprecated + */ + function serializeParameters($operation, $direction, $parameters) + { + $this->debug("in serializeParameters: operation=$operation, direction=$direction, XMLSchemaVersion=$this->XMLSchemaVersion"); + $this->appendDebug('parameters=' . $this->varDump($parameters)); + + if ($direction != 'input' && $direction != 'output') { + $this->debug('The value of the \$direction argument needs to be either "input" or "output"'); + $this->setError('The value of the \$direction argument needs to be either "input" or "output"'); + return false; + } + if (!$opData = $this->getOperationData($operation)) { + $this->debug('Unable to retrieve WSDL data for operation: ' . $operation); + $this->setError('Unable to retrieve WSDL data for operation: ' . $operation); + return false; + } + $this->debug('opData:'); + $this->appendDebug($this->varDump($opData)); + + // Get encoding style for output and set to current + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + if(($direction == 'input') && isset($opData['output']['encodingStyle']) && ($opData['output']['encodingStyle'] != $encodingStyle)) { + $encodingStyle = $opData['output']['encodingStyle']; + $enc_style = $encodingStyle; + } + + // set input params + $xml = ''; + if (isset($opData[$direction]['parts']) && sizeof($opData[$direction]['parts']) > 0) { + + $use = $opData[$direction]['use']; + $this->debug("use=$use"); + $this->debug('got ' . count($opData[$direction]['parts']) . ' part(s)'); + if (is_array($parameters)) { + $parametersArrayType = $this->isArraySimpleOrStruct($parameters); + $this->debug('have ' . $parametersArrayType . ' parameters'); + foreach($opData[$direction]['parts'] as $name => $type) { + $this->debug('serializing part "'.$name.'" of type "'.$type.'"'); + // Track encoding style + if(isset($opData[$direction]['encodingStyle']) && $encodingStyle != $opData[$direction]['encodingStyle']) { + $encodingStyle = $opData[$direction]['encodingStyle']; + $enc_style = $encodingStyle; + } else { + $enc_style = false; + } + // NOTE: add error handling here + // if serializeType returns false, then catch global error and fault + if ($parametersArrayType == 'arraySimple') { + $p = array_shift($parameters); + $this->debug('calling serializeType w/indexed param'); + $xml .= $this->serializeType($name, $type, $p, $use, $enc_style); + } elseif (isset($parameters[$name])) { + $this->debug('calling serializeType w/named param'); + $xml .= $this->serializeType($name, $type, $parameters[$name], $use, $enc_style); + } else { + // TODO: only send nillable + $this->debug('calling serializeType w/null param'); + $xml .= $this->serializeType($name, $type, null, $use, $enc_style); + } + } + } else { + $this->debug('no parameters passed.'); + } + } + $this->debug("serializeParameters returning: $xml"); + return $xml; + } + + /** + * serializes a PHP value according a given type definition + * + * @param string $name name of value (part or element) + * @param string $type XML schema type of value (type or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $use use for part (encoded|literal) + * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style) + * @param boolean $unqualified a kludge for what should be XML namespace form handling + * @return string value serialized as an XML string + * @access private + */ + function serializeType($name, $type, $value, $use='encoded', $encodingStyle=false, $unqualified=false) + { + $this->debug("in serializeType: name=$name, type=$type, use=$use, encodingStyle=$encodingStyle, unqualified=" . ($unqualified ? "unqualified" : "qualified")); + $this->appendDebug("value=" . $this->varDump($value)); + if($use == 'encoded' && $encodingStyle) { + $encodingStyle = ' SOAP-ENV:encodingStyle="' . $encodingStyle . '"'; + } + + // if a soapval has been supplied, let its type override the WSDL + if (is_object($value) && get_class($value) == 'soapval') { + if ($value->type_ns) { + $type = $value->type_ns . ':' . $value->type; + $forceType = true; + $this->debug("in serializeType: soapval overrides type to $type"); + } elseif ($value->type) { + $type = $value->type; + $forceType = true; + $this->debug("in serializeType: soapval overrides type to $type"); + } else { + $forceType = false; + $this->debug("in serializeType: soapval does not override type"); + } + $attrs = $value->attributes; + $value = $value->value; + $this->debug("in serializeType: soapval overrides value to $value"); + if ($attrs) { + if (!is_array($value)) { + $value['!'] = $value; + } + foreach ($attrs as $n => $v) { + $value['!' . $n] = $v; + } + $this->debug("in serializeType: soapval provides attributes"); + } + } else { + $forceType = false; + } + + $xml = ''; + if (strpos($type, ':')) { + $uqType = substr($type, strrpos($type, ':') + 1); + $ns = substr($type, 0, strrpos($type, ':')); + $this->debug("in serializeType: got a prefixed type: $uqType, $ns"); + if ($this->getNamespaceFromPrefix($ns)) { + $ns = $this->getNamespaceFromPrefix($ns); + $this->debug("in serializeType: expanded prefixed type: $uqType, $ns"); + } + + if($ns == $this->XMLSchemaVersion || $ns == 'http://schemas.xmlsoap.org/soap/encoding/'){ + $this->debug('in serializeType: type namespace indicates XML Schema or SOAP Encoding type'); + if ($unqualified && $use == 'literal') { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$elementNS/>"; + } else { + // TODO: depends on nillable, which should be checked before calling this method + $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if ($uqType == 'Array') { + // JBoss/Axis does this sometimes + return $this->serialize_val($value, $name, false, false, false, false, $use); + } + if ($uqType == 'boolean') { + if ((is_string($value) && $value == 'false') || (! $value)) { + $value = 'false'; + } else { + $value = 'true'; + } + } + if ($uqType == 'string' && gettype($value) == 'string') { + $value = $this->expandEntities($value); + } + if (($uqType == 'long' || $uqType == 'unsignedLong') && gettype($value) == 'double') { + $value = sprintf("%.0lf", $value); + } + // it's a scalar + // TODO: what about null/nil values? + // check type isn't a custom type extending xmlschema namespace + if (!$this->getTypeDef($uqType, $ns)) { + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value"; + } else { + $xml = "<$name$elementNS>$value"; + } + } else { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + $this->debug('custom type extends XML Schema or SOAP Encoding namespace (yuck)'); + } else if ($ns == 'http://xml.apache.org/xml-soap') { + $this->debug('in serializeType: appears to be Apache SOAP type'); + if ($uqType == 'Map') { + $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap'); + if (! $tt_prefix) { + $this->debug('in serializeType: Add namespace for Apache SOAP type'); + $tt_prefix = 'ns' . rand(1000, 9999); + $this->namespaces[$tt_prefix] = 'http://xml.apache.org/xml-soap'; + // force this to be added to usedNamespaces + $tt_prefix = $this->getPrefixFromNamespace('http://xml.apache.org/xml-soap'); + } + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing map element: key $k, value $v"); + $contents .= ''; + $contents .= $this->serialize_val($k,'key',false,false,false,false,$use); + $contents .= $this->serialize_val($v,'value',false,false,false,false,$use); + $contents .= ''; + } + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\">$contents"; + } else { + $xml = "<$name>$contents"; + } + } else { + $xml = "<$name xsi:type=\"" . $tt_prefix . ":$uqType\"$encodingStyle>$contents"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + $this->debug('in serializeType: Apache SOAP type, but only support Map'); + } + } else { + // TODO: should the type be compared to types in XSD, and the namespace + // set to XSD if the type matches? + $this->debug("in serializeType: No namespace for type $type"); + $ns = ''; + $uqType = $type; + } + if(!$typeDef = $this->getTypeDef($uqType, $ns)){ + $this->setError("$type ($uqType) is not a supported type."); + $this->debug("in serializeType: $type ($uqType) is not a supported type."); + return false; + } else { + $this->debug("in serializeType: found typeDef"); + $this->appendDebug('typeDef=' . $this->varDump($typeDef)); + if (substr($uqType, -1) == '^') { + $uqType = substr($uqType, 0, -1); + } + } + if (!isset($typeDef['phpType'])) { + $this->setError("$type ($uqType) has no phpType."); + $this->debug("in serializeType: $type ($uqType) has no phpType."); + return false; + } + $phpType = $typeDef['phpType']; + $this->debug("in serializeType: uqType: $uqType, ns: $ns, phptype: $phpType, arrayType: " . (isset($typeDef['arrayType']) ? $typeDef['arrayType'] : '') ); + // if php type == struct, map value to the element names + if ($phpType == 'struct') { + if (isset($typeDef['typeClass']) && $typeDef['typeClass'] == 'element') { + $elementName = $uqType; + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + $elementNS = " xmlns=\"\""; + } + } else { + $elementName = $name; + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs and nillable + $xml = "<$elementName$elementNS/>"; + } else { + $xml = "<$elementName$elementNS xsi:nil=\"true\" xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if (is_object($value)) { + $value = get_object_vars($value); + } + if (is_array($value)) { + $elementAttrs = $this->serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType); + if ($use == 'literal') { + if ($forceType) { + $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">"; + } else { + $xml = "<$elementName$elementNS$elementAttrs>"; + } + } else { + $xml = "<$elementName$elementNS$elementAttrs xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>"; + } + + if (isset($typeDef['simpleContent']) && $typeDef['simpleContent'] == 'true') { + if (isset($value['!'])) { + $xml .= $value['!']; + $this->debug("in serializeType: serialized simpleContent for type $type"); + } else { + $this->debug("in serializeType: no simpleContent to serialize for type $type"); + } + } else { + // complexContent + $xml .= $this->serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use, $encodingStyle); + } + $xml .= ""; + } else { + $this->debug("in serializeType: phpType is struct, but value is not an array"); + $this->setError("phpType is struct, but value is not an array: see debug output for details"); + $xml = ''; + } + } elseif ($phpType == 'array') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if (is_null($value)) { + if ($use == 'literal') { + // TODO: depends on minOccurs + $xml = "<$name$elementNS/>"; + } else { + $xml = "<$name$elementNS xsi:nil=\"true\" xsi:type=\"" . + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . + ":Array\" " . + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') . + ':arrayType="' . + $this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) . + ':' . + $this->getLocalPart($typeDef['arrayType'])."[0]\"/>"; + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + if (isset($typeDef['multidimensional'])) { + $nv = array(); + foreach($value as $v) { + $cols = ',' . sizeof($v); + $nv = array_merge($nv, $v); + } + $value = $nv; + } else { + $cols = ''; + } + if (is_array($value) && sizeof($value) >= 1) { + $rows = sizeof($value); + $contents = ''; + foreach($value as $k => $v) { + $this->debug("serializing array element: $k, $v of type: $typeDef[arrayType]"); + //if (strpos($typeDef['arrayType'], ':') ) { + if (!in_array($typeDef['arrayType'],$this->typemap['http://www.w3.org/2001/XMLSchema'])) { + $contents .= $this->serializeType('item', $typeDef['arrayType'], $v, $use); + } else { + $contents .= $this->serialize_val($v, 'item', $typeDef['arrayType'], null, $this->XMLSchemaVersion, false, $use); + } + } + } else { + $rows = 0; + $contents = null; + } + // TODO: for now, an empty value will be serialized as a zero element + // array. Revisit this when coding the handling of null/nil values. + if ($use == 'literal') { + $xml = "<$name$elementNS>" + .$contents + .""; + } else { + $xml = "<$name$elementNS xsi:type=\"".$this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/').':Array" '. + $this->getPrefixFromNamespace('http://schemas.xmlsoap.org/soap/encoding/') + .':arrayType="' + .$this->getPrefixFromNamespace($this->getPrefix($typeDef['arrayType'])) + .":".$this->getLocalPart($typeDef['arrayType'])."[$rows$cols]\">" + .$contents + .""; + } + } elseif ($phpType == 'scalar') { + if (isset($typeDef['form']) && ($typeDef['form'] == 'qualified')) { + $elementNS = " xmlns=\"$ns\""; + } else { + if ($unqualified) { + $elementNS = " xmlns=\"\""; + } else { + $elementNS = ''; + } + } + if ($use == 'literal') { + if ($forceType) { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\">$value"; + } else { + $xml = "<$name$elementNS>$value"; + } + } else { + $xml = "<$name$elementNS xsi:type=\"" . $this->getPrefixFromNamespace($ns) . ":$uqType\"$encodingStyle>$value"; + } + } + $this->debug("in serializeType: returning: $xml"); + return $xml; + } + + /** + * serializes the attributes for a complexType + * + * @param array $typeDef our internal representation of an XML schema type (or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $ns the namespace of the type + * @param string $uqType the local part of the type + * @return string value serialized as an XML string + * @access private + */ + function serializeComplexTypeAttributes($typeDef, $value, $ns, $uqType) { + $this->debug("serializeComplexTypeAttributes for XML Schema type $ns:$uqType"); + $xml = ''; + if (isset($typeDef['extensionBase'])) { + $nsx = $this->getPrefix($typeDef['extensionBase']); + $uqTypex = $this->getLocalPart($typeDef['extensionBase']); + if ($this->getNamespaceFromPrefix($nsx)) { + $nsx = $this->getNamespaceFromPrefix($nsx); + } + if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) { + $this->debug("serialize attributes for extension base $nsx:$uqTypex"); + $xml .= $this->serializeComplexTypeAttributes($typeDefx, $value, $nsx, $uqTypex); + } else { + $this->debug("extension base $nsx:$uqTypex is not a supported type"); + } + } + if (isset($typeDef['attrs']) && is_array($typeDef['attrs'])) { + $this->debug("serialize attributes for XML Schema type $ns:$uqType"); + if (is_array($value)) { + $xvalue = $value; + } elseif (is_object($value)) { + $xvalue = get_object_vars($value); + } else { + $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType"); + $xvalue = array(); + } + foreach ($typeDef['attrs'] as $aName => $attrs) { + if (isset($xvalue['!' . $aName])) { + $xname = '!' . $aName; + $this->debug("value provided for attribute $aName with key $xname"); + } elseif (isset($xvalue[$aName])) { + $xname = $aName; + $this->debug("value provided for attribute $aName with key $xname"); + } elseif (isset($attrs['default'])) { + $xname = '!' . $aName; + $xvalue[$xname] = $attrs['default']; + $this->debug('use default value of ' . $xvalue[$aName] . ' for attribute ' . $aName); + } else { + $xname = ''; + $this->debug("no value provided for attribute $aName"); + } + if ($xname) { + $xml .= " $aName=\"" . $this->expandEntities($xvalue[$xname]) . "\""; + } + } + } else { + $this->debug("no attributes to serialize for XML Schema type $ns:$uqType"); + } + return $xml; + } + + /** + * serializes the elements for a complexType + * + * @param array $typeDef our internal representation of an XML schema type (or element) + * @param mixed $value a native PHP value (parameter value) + * @param string $ns the namespace of the type + * @param string $uqType the local part of the type + * @param string $use use for part (encoded|literal) + * @param string $encodingStyle SOAP encoding style for the value (if different than the enclosing style) + * @return string value serialized as an XML string + * @access private + */ + function serializeComplexTypeElements($typeDef, $value, $ns, $uqType, $use='encoded', $encodingStyle=false) { + $this->debug("in serializeComplexTypeElements for XML Schema type $ns:$uqType"); + $xml = ''; + if (isset($typeDef['extensionBase'])) { + $nsx = $this->getPrefix($typeDef['extensionBase']); + $uqTypex = $this->getLocalPart($typeDef['extensionBase']); + if ($this->getNamespaceFromPrefix($nsx)) { + $nsx = $this->getNamespaceFromPrefix($nsx); + } + if ($typeDefx = $this->getTypeDef($uqTypex, $nsx)) { + $this->debug("serialize elements for extension base $nsx:$uqTypex"); + $xml .= $this->serializeComplexTypeElements($typeDefx, $value, $nsx, $uqTypex, $use, $encodingStyle); + } else { + $this->debug("extension base $nsx:$uqTypex is not a supported type"); + } + } + if (isset($typeDef['elements']) && is_array($typeDef['elements'])) { + $this->debug("in serializeComplexTypeElements, serialize elements for XML Schema type $ns:$uqType"); + if (is_array($value)) { + $xvalue = $value; + } elseif (is_object($value)) { + $xvalue = get_object_vars($value); + } else { + $this->debug("value is neither an array nor an object for XML Schema type $ns:$uqType"); + $xvalue = array(); + } + // toggle whether all elements are present - ideally should validate against schema + if (count($typeDef['elements']) != count($xvalue)){ + $optionals = true; + } + foreach ($typeDef['elements'] as $eName => $attrs) { + if (!isset($xvalue[$eName])) { + if (isset($attrs['default'])) { + $xvalue[$eName] = $attrs['default']; + $this->debug('use default value of ' . $xvalue[$eName] . ' for element ' . $eName); + } + } + // if user took advantage of a minOccurs=0, then only serialize named parameters + if (isset($optionals) + && (!isset($xvalue[$eName])) + && ( (!isset($attrs['nillable'])) || $attrs['nillable'] != 'true') + ){ + if (isset($attrs['minOccurs']) && $attrs['minOccurs'] <> '0') { + $this->debug("apparent error: no value provided for element $eName with minOccurs=" . $attrs['minOccurs']); + } + // do nothing + $this->debug("no value provided for complexType element $eName and element is not nillable, so serialize nothing"); + } else { + // get value + if (isset($xvalue[$eName])) { + $v = $xvalue[$eName]; + } else { + $v = null; + } + if (isset($attrs['form'])) { + $unqualified = ($attrs['form'] == 'unqualified'); + } else { + $unqualified = false; + } + if (isset($attrs['maxOccurs']) && ($attrs['maxOccurs'] == 'unbounded' || $attrs['maxOccurs'] > 1) && isset($v) && is_array($v) && $this->isArraySimpleOrStruct($v) == 'arraySimple') { + $vv = $v; + foreach ($vv as $k => $v) { + if (isset($attrs['type']) || isset($attrs['ref'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } else { + // serialize generic type (can this ever really happen?) + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } else { + if (is_null($v) && isset($attrs['minOccurs']) && $attrs['minOccurs'] == '0') { + // do nothing + } elseif (is_null($v) && isset($attrs['nillable']) && $attrs['nillable'] == 'true') { + // TODO: serialize a nil correctly, but for now serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } elseif (isset($attrs['type']) || isset($attrs['ref'])) { + // serialize schema-defined type + $xml .= $this->serializeType($eName, isset($attrs['type']) ? $attrs['type'] : $attrs['ref'], $v, $use, $encodingStyle, $unqualified); + } else { + // serialize generic type (can this ever really happen?) + $this->debug("calling serialize_val() for $v, $eName, false, false, false, false, $use"); + $xml .= $this->serialize_val($v, $eName, false, false, false, false, $use); + } + } + } + } + } else { + $this->debug("no elements to serialize for XML Schema type $ns:$uqType"); + } + return $xml; + } + + /** + * adds an XML Schema complex type to the WSDL types + * + * @param string $name + * @param string $typeClass (complexType|simpleType|attribute) + * @param string $phpType currently supported are array and struct (php assoc array) + * @param string $compositor (all|sequence|choice) + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param array $elements e.g. array ( name => array(name=>'',type=>'') ) + * @param array $attrs e.g. array(array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'xsd:string[]')) + * @param string $arrayType as namespace:name (xsd:string) + * @see nusoap_xmlschema + * @access public + */ + function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType='') { + if (count($elements) > 0) { + $eElements = array(); + foreach($elements as $n => $e){ + // expand each element + $ee = array(); + foreach ($e as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $ee[$k] = $v; + } + $eElements[$n] = $ee; + } + $elements = $eElements; + } + + if (count($attrs) > 0) { + foreach($attrs as $n => $a){ + // expand each attribute + foreach ($a as $k => $v) { + $k = strpos($k,':') ? $this->expandQname($k) : $k; + $v = strpos($v,':') ? $this->expandQname($v) : $v; + $aa[$k] = $v; + } + $eAttrs[$n] = $aa; + } + $attrs = $eAttrs; + } + + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + $arrayType = strpos($arrayType,':') ? $this->expandQname($arrayType) : $arrayType; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addComplexType($name,$typeClass,$phpType,$compositor,$restrictionBase,$elements,$attrs,$arrayType); + } + + /** + * adds an XML Schema simple type to the WSDL types + * + * @param string $name + * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) + * @param string $typeClass (should always be simpleType) + * @param string $phpType (should always be scalar) + * @param array $enumeration array of values + * @see nusoap_xmlschema + * @access public + */ + function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { + $restrictionBase = strpos($restrictionBase,':') ? $this->expandQname($restrictionBase) : $restrictionBase; + + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addSimpleType($name, $restrictionBase, $typeClass, $phpType, $enumeration); + } + + /** + * adds an element to the WSDL types + * + * @param array $attrs attributes that must include name and type + * @see nusoap_xmlschema + * @access public + */ + function addElement($attrs) { + $typens = isset($this->namespaces['types']) ? $this->namespaces['types'] : $this->namespaces['tns']; + $this->schemas[$typens][0]->addElement($attrs); + } + + /** + * register an operation with the server + * + * @param string $name operation (method) name + * @param array $in assoc array of input values: key = param name, value = param type + * @param array $out assoc array of output values: key = param name, value = param type + * @param string $namespace optional The namespace for the operation + * @param string $soapaction optional The soapaction for the operation + * @param string $style (rpc|document) optional The style for the operation Note: when 'document' is specified, parameter and return wrappers are created for you automatically + * @param string $use (encoded|literal) optional The use for the parameters (cannot mix right now) + * @param string $documentation optional The description to include in the WSDL + * @param string $encodingStyle optional (usually 'http://schemas.xmlsoap.org/soap/encoding/' for encoded) + * @access public + */ + function addOperation($name, $in = false, $out = false, $namespace = false, $soapaction = false, $style = 'rpc', $use = 'encoded', $documentation = '', $encodingStyle = ''){ + if ($use == 'encoded' && $encodingStyle == '') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } + + if ($style == 'document') { + $elements = array(); + foreach ($in as $n => $t) { + $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified'); + } + $this->addComplexType($name . 'RequestType', 'complexType', 'struct', 'all', '', $elements); + $this->addElement(array('name' => $name, 'type' => $name . 'RequestType')); + $in = array('parameters' => 'tns:' . $name . '^'); + + $elements = array(); + foreach ($out as $n => $t) { + $elements[$n] = array('name' => $n, 'type' => $t, 'form' => 'unqualified'); + } + $this->addComplexType($name . 'ResponseType', 'complexType', 'struct', 'all', '', $elements); + $this->addElement(array('name' => $name . 'Response', 'type' => $name . 'ResponseType', 'form' => 'qualified')); + $out = array('parameters' => 'tns:' . $name . 'Response' . '^'); + } + + // get binding + $this->bindings[ $this->serviceName . 'Binding' ]['operations'][$name] = + array( + 'name' => $name, + 'binding' => $this->serviceName . 'Binding', + 'endpoint' => $this->endpoint, + 'soapAction' => $soapaction, + 'style' => $style, + 'input' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Request', + 'parts' => $in), + 'output' => array( + 'use' => $use, + 'namespace' => $namespace, + 'encodingStyle' => $encodingStyle, + 'message' => $name . 'Response', + 'parts' => $out), + 'namespace' => $namespace, + 'transport' => 'http://schemas.xmlsoap.org/soap/http', + 'documentation' => $documentation); + // add portTypes + // add messages + if($in) + { + foreach($in as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Request'][$pName] = $pType; + } + } else { + $this->messages[$name.'Request']= '0'; + } + if($out) + { + foreach($out as $pName => $pType) + { + if(strpos($pType,':')) { + $pType = $this->getNamespaceFromPrefix($this->getPrefix($pType)).":".$this->getLocalPart($pType); + } + $this->messages[$name.'Response'][$pName] = $pType; + } + } else { + $this->messages[$name.'Response']= '0'; + } + return true; + } +} +?> +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_parser extends nusoap_base { + + var $xml = ''; + var $xml_encoding = ''; + var $method = ''; + var $root_struct = ''; + var $root_struct_name = ''; + var $root_struct_namespace = ''; + var $root_header = ''; + var $document = ''; // incoming SOAP body (text) + // determines where in the message we are (envelope,header,body,method) + var $status = ''; + var $position = 0; + var $depth = 0; + var $default_namespace = ''; + var $namespaces = array(); + var $message = array(); + var $parent = ''; + var $fault = false; + var $fault_code = ''; + var $fault_str = ''; + var $fault_detail = ''; + var $depth_array = array(); + var $debug_flag = true; + var $soapresponse = NULL; // parsed SOAP Body + var $soapheader = NULL; // parsed SOAP Header + var $responseHeaders = ''; // incoming SOAP headers (text) + var $body_position = 0; + // for multiref parsing: + // array of id => pos + var $ids = array(); + // array of id => hrefs => pos + var $multirefs = array(); + // toggle for auto-decoding element content + var $decode_utf8 = true; + + /** + * constructor that actually does the parsing + * + * @param string $xml SOAP message + * @param string $encoding character encoding scheme of message + * @param string $method method for which XML is parsed (unused?) + * @param string $decode_utf8 whether to decode UTF-8 to ISO-8859-1 + * @access public + */ + function nusoap_parser($xml,$encoding='UTF-8',$method='',$decode_utf8=true){ + parent::nusoap_base(); + $this->xml = $xml; + $this->xml_encoding = $encoding; + $this->method = $method; + $this->decode_utf8 = $decode_utf8; + + // Check whether content has been read. + if(!empty($xml)){ + // Check XML encoding + $pos_xml = strpos($xml, '', $pos_xml + 2) - $pos_xml + 1); + if (preg_match("/encoding=[\"']([^\"']*)[\"']/", $xml_decl, $res)) { + $xml_encoding = $res[1]; + if (strtoupper($xml_encoding) != $encoding) { + $err = "Charset from HTTP Content-Type '" . $encoding . "' does not match encoding from XML declaration '" . $xml_encoding . "'"; + $this->debug($err); + if ($encoding != 'ISO-8859-1' || strtoupper($xml_encoding) != 'UTF-8') { + $this->setError($err); + return; + } + // when HTTP says ISO-8859-1 (the default) and XML says UTF-8 (the typical), assume the other endpoint is just sloppy and proceed + } else { + $this->debug('Charset from HTTP Content-Type matches encoding from XML declaration'); + } + } else { + $this->debug('No encoding specified in XML declaration'); + } + } else { + $this->debug('No XML declaration'); + } + $this->debug('Entering nusoap_parser(), length='.strlen($xml).', encoding='.$encoding); + // Create an XML parser - why not xml_parser_create_ns? + $this->parser = xml_parser_create($this->xml_encoding); + // Set the options for parsing the XML data. + //xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); + xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->xml_encoding); + // Set the object for the parser. + xml_set_object($this->parser, $this); + // Set the element handlers for the parser. + xml_set_element_handler($this->parser, 'start_element','end_element'); + xml_set_character_data_handler($this->parser,'character_data'); + + // Parse the XML file. + if(!xml_parse($this->parser,$xml,true)){ + // Display an error message. + $err = sprintf('XML error parsing SOAP payload on line %d: %s', + xml_get_current_line_number($this->parser), + xml_error_string(xml_get_error_code($this->parser))); + $this->debug($err); + $this->debug("XML payload:\n" . $xml); + $this->setError($err); + } else { + $this->debug('in nusoap_parser ctor, message:'); + $this->appendDebug($this->varDump($this->message)); + $this->debug('parsed successfully, found root struct: '.$this->root_struct.' of name '.$this->root_struct_name); + // get final value + $this->soapresponse = $this->message[$this->root_struct]['result']; + // get header value + if($this->root_header != '' && isset($this->message[$this->root_header]['result'])){ + $this->soapheader = $this->message[$this->root_header]['result']; + } + // resolve hrefs/ids + if(sizeof($this->multirefs) > 0){ + foreach($this->multirefs as $id => $hrefs){ + $this->debug('resolving multirefs for id: '.$id); + $idVal = $this->buildVal($this->ids[$id]); + if (is_array($idVal) && isset($idVal['!id'])) { + unset($idVal['!id']); + } + foreach($hrefs as $refPos => $ref){ + $this->debug('resolving href at pos '.$refPos); + $this->multirefs[$id][$refPos] = $idVal; + } + } + } + } + xml_parser_free($this->parser); + } else { + $this->debug('xml was empty, didn\'t parse!'); + $this->setError('xml was empty, didn\'t parse!'); + } + } + + /** + * start-element handler + * + * @param resource $parser XML parser object + * @param string $name element name + * @param array $attrs associative array of attributes + * @access private + */ + function start_element($parser, $name, $attrs) { + // position in a total number of elements, starting from 0 + // update class level pos + $pos = $this->position++; + // and set mine + $this->message[$pos] = array('pos' => $pos,'children'=>'','cdata'=>''); + // depth = how many levels removed from root? + // set mine as current global depth and increment global depth value + $this->message[$pos]['depth'] = $this->depth++; + + // else add self as child to whoever the current parent is + if($pos != 0){ + $this->message[$this->parent]['children'] .= '|'.$pos; + } + // set my parent + $this->message[$pos]['parent'] = $this->parent; + // set self as current parent + $this->parent = $pos; + // set self as current value for this depth + $this->depth_array[$this->depth] = $pos; + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + // set status + if ($name == 'Envelope' && $this->status == '') { + $this->status = 'envelope'; + } elseif ($name == 'Header' && $this->status == 'envelope') { + $this->root_header = $pos; + $this->status = 'header'; + } elseif ($name == 'Body' && $this->status == 'envelope'){ + $this->status = 'body'; + $this->body_position = $pos; + // set method + } elseif($this->status == 'body' && $pos == ($this->body_position+1)) { + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->message[$pos]['type'] = 'struct'; + $this->debug("found root struct $this->root_struct_name, pos $this->root_struct"); + } + // set my status + $this->message[$pos]['status'] = $this->status; + // set name + $this->message[$pos]['name'] = htmlspecialchars($name); + // set attrs + $this->message[$pos]['attrs'] = $attrs; + + // loop through atts, logging ns and type declarations + $attstr = ''; + foreach($attrs as $key => $value){ + $key_prefix = $this->getPrefix($key); + $key_localpart = $this->getLocalPart($key); + // if ns declarations, add to class level array of valid namespaces + if($key_prefix == 'xmlns'){ + if(preg_match('/^http:\/\/www.w3.org\/[0-9]{4}\/XMLSchema$/',$value)){ + $this->XMLSchemaVersion = $value; + $this->namespaces['xsd'] = $this->XMLSchemaVersion; + $this->namespaces['xsi'] = $this->XMLSchemaVersion.'-instance'; + } + $this->namespaces[$key_localpart] = $value; + // set method namespace + if($name == $this->root_struct_name){ + $this->methodNamespace = $value; + } + // if it's a type declaration, set type + } elseif($key_localpart == 'type'){ + if (isset($this->message[$pos]['type']) && $this->message[$pos]['type'] == 'array') { + // do nothing: already processed arrayType + } else { + $value_prefix = $this->getPrefix($value); + $value_localpart = $this->getLocalPart($value); + $this->message[$pos]['type'] = $value_localpart; + $this->message[$pos]['typePrefix'] = $value_prefix; + if(isset($this->namespaces[$value_prefix])){ + $this->message[$pos]['type_namespace'] = $this->namespaces[$value_prefix]; + } else if(isset($attrs['xmlns:'.$value_prefix])) { + $this->message[$pos]['type_namespace'] = $attrs['xmlns:'.$value_prefix]; + } + // should do something here with the namespace of specified type? + } + } elseif($key_localpart == 'arrayType'){ + $this->message[$pos]['type'] = 'array'; + /* do arrayType ereg here + [1] arrayTypeValue ::= atype asize + [2] atype ::= QName rank* + [3] rank ::= '[' (',')* ']' + [4] asize ::= '[' length~ ']' + [5] length ::= nextDimension* Digit+ + [6] nextDimension ::= Digit+ ',' + */ + $expr = '/([A-Za-z0-9_]+):([A-Za-z]+[A-Za-z0-9_]+)\[([0-9]+),?([0-9]*)\]/'; + if(preg_match($expr,$value,$regs)){ + $this->message[$pos]['typePrefix'] = $regs[1]; + $this->message[$pos]['arrayTypePrefix'] = $regs[1]; + if (isset($this->namespaces[$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $this->namespaces[$regs[1]]; + } else if (isset($attrs['xmlns:'.$regs[1]])) { + $this->message[$pos]['arrayTypeNamespace'] = $attrs['xmlns:'.$regs[1]]; + } + $this->message[$pos]['arrayType'] = $regs[2]; + $this->message[$pos]['arraySize'] = $regs[3]; + $this->message[$pos]['arrayCols'] = $regs[4]; + } + // specifies nil value (or not) + } elseif ($key_localpart == 'nil'){ + $this->message[$pos]['nil'] = ($value == 'true' || $value == '1'); + // some other attribute + } elseif ($key != 'href' && $key != 'xmlns' && $key_localpart != 'encodingStyle' && $key_localpart != 'root') { + $this->message[$pos]['xattrs']['!' . $key] = $value; + } + + if ($key == 'xmlns') { + $this->default_namespace = $value; + } + // log id + if($key == 'id'){ + $this->ids[$value] = $pos; + } + // root + if($key_localpart == 'root' && $value == 1){ + $this->status = 'method'; + $this->root_struct_name = $name; + $this->root_struct = $pos; + $this->debug("found root struct $this->root_struct_name, pos $pos"); + } + // for doclit + $attstr .= " $key=\"$value\""; + } + // get namespace - must be done after namespace atts are processed + if(isset($prefix)){ + $this->message[$pos]['namespace'] = $this->namespaces[$prefix]; + $this->default_namespace = $this->namespaces[$prefix]; + } else { + $this->message[$pos]['namespace'] = $this->default_namespace; + } + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } elseif($this->root_struct_name != ''){ + $this->document .= "<" . (isset($prefix) ? $prefix . ':' : '') . "$name$attstr>"; + } + } + + /** + * end-element handler + * + * @param resource $parser XML parser object + * @param string $name element name + * @access private + */ + function end_element($parser, $name) { + // position of current element is equal to the last value left in depth_array for my depth + $pos = $this->depth_array[$this->depth--]; + + // get element prefix + if(strpos($name,':')){ + // get ns prefix + $prefix = substr($name,0,strpos($name,':')); + // get unqualified name + $name = substr(strstr($name,':'),1); + } + + // build to native type + if(isset($this->body_position) && $pos > $this->body_position){ + // deal w/ multirefs + if(isset($this->message[$pos]['attrs']['href'])){ + // get id + $id = substr($this->message[$pos]['attrs']['href'],1); + // add placeholder to href array + $this->multirefs[$id][$pos] = 'placeholder'; + // add set a reference to it as the result value + $this->message[$pos]['result'] =& $this->multirefs[$id][$pos]; + // build complexType values + } elseif($this->message[$pos]['children'] != ''){ + // if result has already been generated (struct/array) + if(!isset($this->message[$pos]['result'])){ + $this->message[$pos]['result'] = $this->buildVal($pos); + } + // build complexType values of attributes and possibly simpleContent + } elseif (isset($this->message[$pos]['xattrs'])) { + if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { + $this->message[$pos]['xattrs']['!'] = null; + } elseif (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { + if (isset($this->message[$pos]['type'])) { + $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $this->message[$pos]['xattrs']['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $this->message[$pos]['xattrs']['!'] = $this->message[$pos]['cdata']; + } + } + } + $this->message[$pos]['result'] = $this->message[$pos]['xattrs']; + // set value of simpleType (or nil complexType) + } else { + //$this->debug('adding data for scalar value '.$this->message[$pos]['name'].' of value '.$this->message[$pos]['cdata']); + if (isset($this->message[$pos]['nil']) && $this->message[$pos]['nil']) { + $this->message[$pos]['xattrs']['!'] = null; + } elseif (isset($this->message[$pos]['type'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $this->message[$pos]['result'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $this->message[$pos]['result'] = $this->message[$pos]['cdata']; + } + } + + /* add value to parent's result, if parent is struct/array + $parent = $this->message[$pos]['parent']; + if($this->message[$parent]['type'] != 'map'){ + if(strtolower($this->message[$parent]['type']) == 'array'){ + $this->message[$parent]['result'][] = $this->message[$pos]['result']; + } else { + $this->message[$parent]['result'][$this->message[$pos]['name']] = $this->message[$pos]['result']; + } + } + */ + } + } + + // for doclit + if($this->status == 'header'){ + if ($this->root_header != $pos) { + $this->responseHeaders .= ""; + } + } elseif($pos >= $this->root_struct){ + $this->document .= ""; + } + // switch status + if ($pos == $this->root_struct){ + $this->status = 'body'; + $this->root_struct_namespace = $this->message[$pos]['namespace']; + } elseif ($pos == $this->root_header) { + $this->status = 'envelope'; + } elseif ($name == 'Body' && $this->status == 'body') { + $this->status = 'envelope'; + } elseif ($name == 'Header' && $this->status == 'header') { // will never happen + $this->status = 'envelope'; + } elseif ($name == 'Envelope' && $this->status == 'envelope') { + $this->status = ''; + } + // set parent back to my parent + $this->parent = $this->message[$pos]['parent']; + } + + /** + * element content handler + * + * @param resource $parser XML parser object + * @param string $data element content + * @access private + */ + function character_data($parser, $data){ + $pos = $this->depth_array[$this->depth]; + if ($this->xml_encoding=='UTF-8'){ + // TODO: add an option to disable this for folks who want + // raw UTF-8 that, e.g., might not map to iso-8859-1 + // TODO: this can also be handled with xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1"); + if($this->decode_utf8){ + $data = utf8_decode($data); + } + } + $this->message[$pos]['cdata'] .= $data; + // for doclit + if($this->status == 'header'){ + $this->responseHeaders .= $data; + } else { + $this->document .= $data; + } + } + + /** + * get the parsed message (SOAP Body) + * + * @return mixed + * @access public + * @deprecated use get_soapbody instead + */ + function get_response(){ + return $this->soapresponse; + } + + /** + * get the parsed SOAP Body (NULL if there was none) + * + * @return mixed + * @access public + */ + function get_soapbody(){ + return $this->soapresponse; + } + + /** + * get the parsed SOAP Header (NULL if there was none) + * + * @return mixed + * @access public + */ + function get_soapheader(){ + return $this->soapheader; + } + + /** + * get the unparsed SOAP Header + * + * @return string XML or empty if no Header + * @access public + */ + function getHeaders(){ + return $this->responseHeaders; + } + + /** + * decodes simple types into PHP variables + * + * @param string $value value to decode + * @param string $type XML type to decode + * @param string $typens XML type namespace to decode + * @return mixed PHP value + * @access private + */ + function decodeSimple($value, $type, $typens) { + // TODO: use the namespace! + if ((!isset($type)) || $type == 'string' || $type == 'long' || $type == 'unsignedLong') { + return (string) $value; + } + if ($type == 'int' || $type == 'integer' || $type == 'short' || $type == 'byte') { + return (int) $value; + } + if ($type == 'float' || $type == 'double' || $type == 'decimal') { + return (double) $value; + } + if ($type == 'boolean') { + if (strtolower($value) == 'false' || strtolower($value) == 'f') { + return false; + } + return (boolean) $value; + } + if ($type == 'base64' || $type == 'base64Binary') { + $this->debug('Decode base64 value'); + return base64_decode($value); + } + // obscure numeric types + if ($type == 'nonPositiveInteger' || $type == 'negativeInteger' + || $type == 'nonNegativeInteger' || $type == 'positiveInteger' + || $type == 'unsignedInt' + || $type == 'unsignedShort' || $type == 'unsignedByte') { + return (int) $value; + } + // bogus: parser treats array with no elements as a simple type + if ($type == 'array') { + return array(); + } + // everything else + return (string) $value; + } + + /** + * builds response structures for compound values (arrays/structs) + * and scalars + * + * @param integer $pos position in node tree + * @return mixed PHP value + * @access private + */ + function buildVal($pos){ + if(!isset($this->message[$pos]['type'])){ + $this->message[$pos]['type'] = ''; + } + $this->debug('in buildVal() for '.$this->message[$pos]['name']."(pos $pos) of type ".$this->message[$pos]['type']); + // if there are children... + if($this->message[$pos]['children'] != ''){ + $this->debug('in buildVal, there are children'); + $children = explode('|',$this->message[$pos]['children']); + array_shift($children); // knock off empty + // md array + if(isset($this->message[$pos]['arrayCols']) && $this->message[$pos]['arrayCols'] != ''){ + $r=0; // rowcount + $c=0; // colcount + foreach($children as $child_pos){ + $this->debug("in buildVal, got an MD array element: $r, $c"); + $params[$r][] = $this->message[$child_pos]['result']; + $c++; + if($c == $this->message[$pos]['arrayCols']){ + $c = 0; + $r++; + } + } + // array + } elseif($this->message[$pos]['type'] == 'array' || $this->message[$pos]['type'] == 'Array'){ + $this->debug('in buildVal, adding array '.$this->message[$pos]['name']); + foreach($children as $child_pos){ + $params[] = &$this->message[$child_pos]['result']; + } + // apache Map type: java hashtable + } elseif($this->message[$pos]['type'] == 'Map' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap'){ + $this->debug('in buildVal, Java Map '.$this->message[$pos]['name']); + foreach($children as $child_pos){ + $kv = explode("|",$this->message[$child_pos]['children']); + $params[$this->message[$kv[1]]['result']] = &$this->message[$kv[2]]['result']; + } + // generic compound type + //} elseif($this->message[$pos]['type'] == 'SOAPStruct' || $this->message[$pos]['type'] == 'struct') { + } else { + // Apache Vector type: treat as an array + $this->debug('in buildVal, adding Java Vector or generic compound type '.$this->message[$pos]['name']); + if ($this->message[$pos]['type'] == 'Vector' && $this->message[$pos]['type_namespace'] == 'http://xml.apache.org/xml-soap') { + $notstruct = 1; + } else { + $notstruct = 0; + } + // + foreach($children as $child_pos){ + if($notstruct){ + $params[] = &$this->message[$child_pos]['result']; + } else { + if (isset($params[$this->message[$child_pos]['name']])) { + // de-serialize repeated element name into an array + if ((!is_array($params[$this->message[$child_pos]['name']])) || (!isset($params[$this->message[$child_pos]['name']][0]))) { + $params[$this->message[$child_pos]['name']] = array($params[$this->message[$child_pos]['name']]); + } + $params[$this->message[$child_pos]['name']][] = &$this->message[$child_pos]['result']; + } else { + $params[$this->message[$child_pos]['name']] = &$this->message[$child_pos]['result']; + } + } + } + } + if (isset($this->message[$pos]['xattrs'])) { + $this->debug('in buildVal, handling attributes'); + foreach ($this->message[$pos]['xattrs'] as $n => $v) { + $params[$n] = $v; + } + } + // handle simpleContent + if (isset($this->message[$pos]['cdata']) && trim($this->message[$pos]['cdata']) != '') { + $this->debug('in buildVal, handling simpleContent'); + if (isset($this->message[$pos]['type'])) { + $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + } else { + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $params['!'] = $this->decodeSimple($this->message[$pos]['cdata'], $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + } else { + $params['!'] = $this->message[$pos]['cdata']; + } + } + } + $ret = is_array($params) ? $params : array(); + $this->debug('in buildVal, return:'); + $this->appendDebug($this->varDump($ret)); + return $ret; + } else { + $this->debug('in buildVal, no children, building scalar'); + $cdata = isset($this->message[$pos]['cdata']) ? $this->message[$pos]['cdata'] : ''; + if (isset($this->message[$pos]['type'])) { + $ret = $this->decodeSimple($cdata, $this->message[$pos]['type'], isset($this->message[$pos]['type_namespace']) ? $this->message[$pos]['type_namespace'] : ''); + $this->debug("in buildVal, return: $ret"); + return $ret; + } + $parent = $this->message[$pos]['parent']; + if (isset($this->message[$parent]['type']) && ($this->message[$parent]['type'] == 'array') && isset($this->message[$parent]['arrayType'])) { + $ret = $this->decodeSimple($cdata, $this->message[$parent]['arrayType'], isset($this->message[$parent]['arrayTypeNamespace']) ? $this->message[$parent]['arrayTypeNamespace'] : ''); + $this->debug("in buildVal, return: $ret"); + return $ret; + } + $ret = $this->message[$pos]['cdata']; + $this->debug("in buildVal, return: $ret"); + return $ret; + } + } +} + +/** + * Backward compatibility + */ +class soap_parser extends nusoap_parser { +} + +?>call( string methodname [ ,array parameters] ); +* +* // bye bye client +* unset($soapclient); +* +* @author Dietrich Ayala +* @author Scott Nichol +* @version $Id: nusoap.php,v 1.123 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_client extends nusoap_base { + + var $username = ''; // Username for HTTP authentication + var $password = ''; // Password for HTTP authentication + var $authtype = ''; // Type of HTTP authentication + var $certRequest = array(); // Certificate for HTTP SSL authentication + var $requestHeaders = false; // SOAP headers in request (text) + var $responseHeaders = ''; // SOAP headers from response (incomplete namespace resolution) (text) + var $responseHeader = NULL; // SOAP Header from response (parsed) + var $document = ''; // SOAP body response portion (incomplete namespace resolution) (text) + var $endpoint; + var $forceEndpoint = ''; // overrides WSDL endpoint + var $proxyhost = ''; + var $proxyport = ''; + var $proxyusername = ''; + var $proxypassword = ''; + var $portName = ''; // port name to use in WSDL + var $xml_encoding = ''; // character set encoding of incoming (response) messages + var $http_encoding = false; + var $timeout = 0; // HTTP connection timeout + var $response_timeout = 30; // HTTP response timeout + var $endpointType = ''; // soap|wsdl, empty for WSDL initialization error + var $persistentConnection = false; + var $defaultRpcParams = false; // This is no longer used + var $request = ''; // HTTP request + var $response = ''; // HTTP response + var $responseData = ''; // SOAP payload of response + var $cookies = array(); // Cookies from response or for request + var $decode_utf8 = true; // toggles whether the parser decodes element content w/ utf8_decode() + var $operations = array(); // WSDL operations, empty for WSDL initialization error + var $curl_options = array(); // User-specified cURL options + var $bindingType = ''; // WSDL operation binding type + var $use_curl = false; // whether to always try to use cURL + + /* + * fault related variables + */ + /** + * @var fault + * @access public + */ + var $fault; + /** + * @var faultcode + * @access public + */ + var $faultcode; + /** + * @var faultstring + * @access public + */ + var $faultstring; + /** + * @var faultdetail + * @access public + */ + var $faultdetail; + + /** + * constructor + * + * @param mixed $endpoint SOAP server or WSDL URL (string), or wsdl instance (object) + * @param mixed $wsdl optional, set to 'wsdl' or true if using WSDL + * @param string $proxyhost optional + * @param string $proxyport optional + * @param string $proxyusername optional + * @param string $proxypassword optional + * @param integer $timeout set the connection timeout + * @param integer $response_timeout set the response timeout + * @param string $portName optional portName in WSDL document + * @access public + */ + function nusoap_client($endpoint,$wsdl = false,$proxyhost = false,$proxyport = false,$proxyusername = false, $proxypassword = false, $timeout = 0, $response_timeout = 30, $portName = ''){ + parent::nusoap_base(); + $this->endpoint = $endpoint; + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + $this->timeout = $timeout; + $this->response_timeout = $response_timeout; + $this->portName = $portName; + + $this->debug("ctor wsdl=$wsdl timeout=$timeout response_timeout=$response_timeout"); + $this->appendDebug('endpoint=' . $this->varDump($endpoint)); + + // make values + if($wsdl){ + if (is_object($endpoint) && (get_class($endpoint) == 'wsdl')) { + $this->wsdl = $endpoint; + $this->endpoint = $this->wsdl->wsdl; + $this->wsdlFile = $this->endpoint; + $this->debug('existing wsdl instance created from ' . $this->endpoint); + $this->checkWSDL(); + } else { + $this->wsdlFile = $this->endpoint; + $this->wsdl = null; + $this->debug('will use lazy evaluation of wsdl from ' . $this->endpoint); + } + $this->endpointType = 'wsdl'; + } else { + $this->debug("instantiate SOAP with endpoint at $endpoint"); + $this->endpointType = 'soap'; + } + } + + /** + * calls method, returns PHP native type + * + * @param string $operation SOAP server URL or path + * @param mixed $params An array, associative or simple, of the parameters + * for the method call, or a string that is the XML + * for the call. For rpc style, this call will + * wrap the XML in a tag named after the method, as + * well as the SOAP Envelope and Body. For document + * style, this will only wrap with the Envelope and Body. + * IMPORTANT: when using an array with document style, + * in which case there + * is really one parameter, the root of the fragment + * used in the call, which encloses what programmers + * normally think of parameters. A parameter array + * *must* include the wrapper. + * @param string $namespace optional method namespace (WSDL can override) + * @param string $soapAction optional SOAPAction value (WSDL can override) + * @param mixed $headers optional string of XML with SOAP header content, or array of soapval objects for SOAP headers, or associative array + * @param boolean $rpcParams optional (no longer used) + * @param string $style optional (rpc|document) the style to use when serializing parameters (WSDL can override) + * @param string $use optional (encoded|literal) the use when serializing parameters (WSDL can override) + * @return mixed response from SOAP call, normally an associative array mirroring the structure of the XML response, false for certain fatal errors + * @access public + */ + function call($operation,$params=array(),$namespace='http://tempuri.org',$soapAction='',$headers=false,$rpcParams=null,$style='rpc',$use='encoded'){ + $this->operation = $operation; + $this->fault = false; + $this->setError(''); + $this->request = ''; + $this->response = ''; + $this->responseData = ''; + $this->faultstring = ''; + $this->faultcode = ''; + $this->opData = array(); + + $this->debug("call: operation=$operation, namespace=$namespace, soapAction=$soapAction, rpcParams=$rpcParams, style=$style, use=$use, endpointType=$this->endpointType"); + $this->appendDebug('params=' . $this->varDump($params)); + $this->appendDebug('headers=' . $this->varDump($headers)); + if ($headers) { + $this->requestHeaders = $headers; + } + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) + return false; + } + // serialize parameters + if($this->endpointType == 'wsdl' && $opData = $this->getOperationData($operation)){ + // use WSDL for operation + $this->opData = $opData; + $this->debug("found operation"); + $this->appendDebug('opData=' . $this->varDump($opData)); + if (isset($opData['soapAction'])) { + $soapAction = $opData['soapAction']; + } + if (! $this->forceEndpoint) { + $this->endpoint = $opData['endpoint']; + } else { + $this->endpoint = $this->forceEndpoint; + } + $namespace = isset($opData['input']['namespace']) ? $opData['input']['namespace'] : $namespace; + $style = $opData['style']; + $use = $opData['input']['use']; + // add ns to ns array + if($namespace != '' && !isset($this->wsdl->namespaces[$namespace])){ + $nsPrefix = 'ns' . rand(1000, 9999); + $this->wsdl->namespaces[$nsPrefix] = $namespace; + } + $nsPrefix = $this->wsdl->getPrefixFromNamespace($namespace); + // serialize payload + if (is_string($params)) { + $this->debug("serializing param string for WSDL operation $operation"); + $payload = $params; + } elseif (is_array($params)) { + $this->debug("serializing param array for WSDL operation $operation"); + $payload = $this->wsdl->serializeRPCParameters($operation,'input',$params,$this->bindingType); + } else { + $this->debug('params must be array or string'); + $this->setError('params must be array or string'); + return false; + } + $usedNamespaces = $this->wsdl->usedNamespaces; + if (isset($opData['input']['encodingStyle'])) { + $encodingStyle = $opData['input']['encodingStyle']; + } else { + $encodingStyle = ''; + } + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + if ($errstr = $this->wsdl->getError()) { + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + return false; + } + } elseif($this->endpointType == 'wsdl') { + // operation not in WSDL + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->setError('operation '.$operation.' not present in WSDL.'); + $this->debug("operation '$operation' not present in WSDL."); + return false; + } else { + // no WSDL + //$this->namespaces['ns1'] = $namespace; + $nsPrefix = 'ns' . rand(1000, 9999); + // serialize + $payload = ''; + if (is_string($params)) { + $this->debug("serializing param string for operation $operation"); + $payload = $params; + } elseif (is_array($params)) { + $this->debug("serializing param array for operation $operation"); + foreach($params as $k => $v){ + $payload .= $this->serialize_val($v,$k,false,false,false,false,$use); + } + } else { + $this->debug('params must be array or string'); + $this->setError('params must be array or string'); + return false; + } + $usedNamespaces = array(); + if ($use == 'encoded') { + $encodingStyle = 'http://schemas.xmlsoap.org/soap/encoding/'; + } else { + $encodingStyle = ''; + } + } + // wrap RPC calls with method element + if ($style == 'rpc') { + if ($use == 'literal') { + $this->debug("wrapping RPC request with literal method element"); + if ($namespace) { + // http://www.ws-i.org/Profiles/BasicProfile-1.1-2004-08-24.html R2735 says rpc/literal accessor elements should not be in a namespace + $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . + $payload . + ""; + } else { + $payload = "<$operation>" . $payload . ""; + } + } else { + $this->debug("wrapping RPC request with encoded method element"); + if ($namespace) { + $payload = "<$nsPrefix:$operation xmlns:$nsPrefix=\"$namespace\">" . + $payload . + ""; + } else { + $payload = "<$operation>" . + $payload . + ""; + } + } + } + // serialize envelope + $soapmsg = $this->serializeEnvelope($payload,$this->requestHeaders,$usedNamespaces,$style,$use,$encodingStyle); + $this->debug("endpoint=$this->endpoint, soapAction=$soapAction, namespace=$namespace, style=$style, use=$use, encodingStyle=$encodingStyle"); + $this->debug('SOAP message length=' . strlen($soapmsg) . ' contents (max 1000 bytes)=' . substr($soapmsg, 0, 1000)); + // send + $return = $this->send($this->getHTTPBody($soapmsg),$soapAction,$this->timeout,$this->response_timeout); + if($errstr = $this->getError()){ + $this->debug('Error: '.$errstr); + return false; + } else { + $this->return = $return; + $this->debug('sent message successfully and got a(n) '.gettype($return)); + $this->appendDebug('return=' . $this->varDump($return)); + + // fault? + if(is_array($return) && isset($return['faultcode'])){ + $this->debug('got fault'); + $this->setError($return['faultcode'].': '.$return['faultstring']); + $this->fault = true; + foreach($return as $k => $v){ + $this->$k = $v; + $this->debug("$k = $v
"); + } + return $return; + } elseif ($style == 'document') { + // NOTE: if the response is defined to have multiple parts (i.e. unwrapped), + // we are only going to return the first part here...sorry about that + return $return; + } else { + // array of return values + if(is_array($return)){ + // multiple 'out' parameters, which we return wrapped up + // in the array + if(sizeof($return) > 1){ + return $return; + } + // single 'out' parameter (normally the return value) + $return = array_shift($return); + $this->debug('return shifted value: '); + $this->appendDebug($this->varDump($return)); + return $return; + // nothing returned (ie, echoVoid) + } else { + return ""; + } + } + } + } + + /** + * check WSDL passed as an instance or pulled from an endpoint + * + * @access private + */ + function checkWSDL() { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('checkWSDL'); + // catch errors + if ($errstr = $this->wsdl->getError()) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('got wsdl error: '.$errstr); + $this->setError('wsdl error: '.$errstr); + } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap')) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->bindingType = 'soap'; + $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType); + } elseif ($this->operations = $this->wsdl->getOperations($this->portName, 'soap12')) { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->bindingType = 'soap12'; + $this->debug('got '.count($this->operations).' operations from wsdl '.$this->wsdlFile.' for binding type '.$this->bindingType); + $this->debug('**************** WARNING: SOAP 1.2 BINDING *****************'); + } else { + $this->appendDebug($this->wsdl->getDebug()); + $this->wsdl->clearDebug(); + $this->debug('getOperations returned false'); + $this->setError('no operations defined in the WSDL document!'); + } + } + + /** + * instantiate wsdl object and parse wsdl file + * + * @access public + */ + function loadWSDL() { + $this->debug('instantiating wsdl class with doc: '.$this->wsdlFile); + $this->wsdl = new wsdl('',$this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword,$this->timeout,$this->response_timeout,$this->curl_options,$this->use_curl); + $this->wsdl->setCredentials($this->username, $this->password, $this->authtype, $this->certRequest); + $this->wsdl->fetchWSDL($this->wsdlFile); + $this->checkWSDL(); + } + + /** + * get available data pertaining to an operation + * + * @param string $operation operation name + * @return array array of data pertaining to the operation + * @access public + */ + function getOperationData($operation){ + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) + return false; + } + if(isset($this->operations[$operation])){ + return $this->operations[$operation]; + } + $this->debug("No data for operation: $operation"); + } + + /** + * send the SOAP message + * + * Note: if the operation has multiple return values + * the return value of this method will be an array + * of those values. + * + * @param string $msg a SOAPx4 soapmsg object + * @param string $soapaction SOAPAction value + * @param integer $timeout set connection timeout in seconds + * @param integer $response_timeout set response timeout in seconds + * @return mixed native PHP types. + * @access private + */ + function send($msg, $soapaction = '', $timeout=0, $response_timeout=30) { + $this->checkCookies(); + // detect transport + switch(true){ + // http(s) + case preg_match('/^http/',$this->endpoint): + $this->debug('transporting via HTTP'); + if($this->persistentConnection == true && is_object($this->persistentConnection)){ + $http =& $this->persistentConnection; + } else { + $http = new soap_transport_http($this->endpoint, $this->curl_options, $this->use_curl); + if ($this->persistentConnection) { + $http->usePersistentConnection(); + } + } + $http->setContentType($this->getHTTPContentType(), $this->getHTTPContentTypeCharset()); + $http->setSOAPAction($soapaction); + if($this->proxyhost && $this->proxyport){ + $http->setProxy($this->proxyhost,$this->proxyport,$this->proxyusername,$this->proxypassword); + } + if($this->authtype != '') { + $http->setCredentials($this->username, $this->password, $this->authtype, array(), $this->certRequest); + } + if($this->http_encoding != ''){ + $http->setEncoding($this->http_encoding); + } + $this->debug('sending message, length='.strlen($msg)); + if(preg_match('/^http:/',$this->endpoint)){ + //if(strpos($this->endpoint,'http:')){ + $this->responseData = $http->send($msg,$timeout,$response_timeout,$this->cookies); + } elseif(preg_match('/^https/',$this->endpoint)){ + //} elseif(strpos($this->endpoint,'https:')){ + //if(phpversion() == '4.3.0-dev'){ + //$response = $http->send($msg,$timeout,$response_timeout); + //$this->request = $http->outgoing_payload; + //$this->response = $http->incoming_payload; + //} else + $this->responseData = $http->sendHTTPS($msg,$timeout,$response_timeout,$this->cookies); + } else { + $this->setError('no http/s in endpoint url'); + } + $this->request = $http->outgoing_payload; + $this->response = $http->incoming_payload; + $this->appendDebug($http->getDebug()); + $this->UpdateCookies($http->incoming_cookies); + + // save transport object if using persistent connections + if ($this->persistentConnection) { + $http->clearDebug(); + if (!is_object($this->persistentConnection)) { + $this->persistentConnection = $http; + } + } + + if($err = $http->getError()){ + $this->setError('HTTP Error: '.$err); + return false; + } elseif($this->getError()){ + return false; + } else { + $this->debug('got response, length='. strlen($this->responseData).' type='.$http->incoming_headers['content-type']); + return $this->parseResponse($http->incoming_headers, $this->responseData); + } + break; + default: + $this->setError('no transport found, or selected transport is not yet supported!'); + return false; + break; + } + } + + /** + * processes SOAP message returned from server + * + * @param array $headers The HTTP headers + * @param string $data unprocessed response data from server + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseResponse($headers, $data) { + $this->debug('Entering parseResponse() for data of length ' . strlen($data) . ' headers:'); + $this->appendDebug($this->varDump($headers)); + if (!isset($headers['content-type'])) { + $this->setError('Response not of type text/xml (no content-type header)'); + return false; + } + if (!strstr($headers['content-type'], 'text/xml')) { + $this->setError('Response not of type text/xml: ' . $headers['content-type']); + return false; + } + if (strpos($headers['content-type'], '=')) { + $enc = str_replace('"', '', substr(strstr($headers["content-type"], '='), 1)); + $this->debug('Got response encoding: ' . $enc); + if(preg_match('/^(ISO-8859-1|US-ASCII|UTF-8)$/i',$enc)){ + $this->xml_encoding = strtoupper($enc); + } else { + $this->xml_encoding = 'US-ASCII'; + } + } else { + // should be US-ASCII for HTTP 1.0 or ISO-8859-1 for HTTP 1.1 + $this->xml_encoding = 'ISO-8859-1'; + } + $this->debug('Use encoding: ' . $this->xml_encoding . ' when creating nusoap_parser'); + $parser = new nusoap_parser($data,$this->xml_encoding,$this->operation,$this->decode_utf8); + // add parser debug data to our debug + $this->appendDebug($parser->getDebug()); + // if parse errors + if($errstr = $parser->getError()){ + $this->setError( $errstr); + // destroy the parser object + unset($parser); + return false; + } else { + // get SOAP headers + $this->responseHeaders = $parser->getHeaders(); + // get SOAP headers + $this->responseHeader = $parser->get_soapheader(); + // get decoded message + $return = $parser->get_soapbody(); + // add document for doclit support + $this->document = $parser->document; + // destroy the parser object + unset($parser); + // return decode message + return $return; + } + } + + /** + * sets user-specified cURL options + * + * @param mixed $option The cURL option (always integer?) + * @param mixed $value The cURL option value + * @access public + */ + function setCurlOption($option, $value) { + $this->debug("setCurlOption option=$option, value="); + $this->appendDebug($this->varDump($value)); + $this->curl_options[$option] = $value; + } + + /** + * sets the SOAP endpoint, which can override WSDL + * + * @param string $endpoint The endpoint URL to use, or empty string or false to prevent override + * @access public + */ + function setEndpoint($endpoint) { + $this->debug("setEndpoint(\"$endpoint\")"); + $this->forceEndpoint = $endpoint; + } + + /** + * set the SOAP headers + * + * @param mixed $headers String of XML with SOAP header content, or array of soapval objects for SOAP headers + * @access public + */ + function setHeaders($headers){ + $this->debug("setHeaders headers="); + $this->appendDebug($this->varDump($headers)); + $this->requestHeaders = $headers; + } + + /** + * get the SOAP response headers (namespace resolution incomplete) + * + * @return string + * @access public + */ + function getHeaders(){ + return $this->responseHeaders; + } + + /** + * get the SOAP response Header (parsed) + * + * @return mixed + * @access public + */ + function getHeader(){ + return $this->responseHeader; + } + + /** + * set proxy info here + * + * @param string $proxyhost + * @param string $proxyport + * @param string $proxyusername + * @param string $proxypassword + * @access public + */ + function setHTTPProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '') { + $this->proxyhost = $proxyhost; + $this->proxyport = $proxyport; + $this->proxyusername = $proxyusername; + $this->proxypassword = $proxypassword; + } + + /** + * if authenticating, set user credentials here + * + * @param string $username + * @param string $password + * @param string $authtype (basic|digest|certificate|ntlm) + * @param array $certRequest (keys must be cainfofile (optional), sslcertfile, sslkeyfile, passphrase, verifypeer (optional), verifyhost (optional): see corresponding options in cURL docs) + * @access public + */ + function setCredentials($username, $password, $authtype = 'basic', $certRequest = array()) { + $this->debug("setCredentials username=$username authtype=$authtype certRequest="); + $this->appendDebug($this->varDump($certRequest)); + $this->username = $username; + $this->password = $password; + $this->authtype = $authtype; + $this->certRequest = $certRequest; + } + + /** + * use HTTP encoding + * + * @param string $enc HTTP encoding + * @access public + */ + function setHTTPEncoding($enc='gzip, deflate'){ + $this->debug("setHTTPEncoding(\"$enc\")"); + $this->http_encoding = $enc; + } + + /** + * Set whether to try to use cURL connections if possible + * + * @param boolean $use Whether to try to use cURL + * @access public + */ + function setUseCURL($use) { + $this->debug("setUseCURL($use)"); + $this->use_curl = $use; + } + + /** + * use HTTP persistent connections if possible + * + * @access public + */ + function useHTTPPersistentConnection(){ + $this->debug("useHTTPPersistentConnection"); + $this->persistentConnection = true; + } + + /** + * gets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style. + * Each call() can override this value. + * + * This is no longer used. + * + * @return boolean + * @access public + * @deprecated + */ + function getDefaultRpcParams() { + return $this->defaultRpcParams; + } + + /** + * sets the default RPC parameter setting. + * If true, default is that call params are like RPC even for document style + * Each call() can override this value. + * + * This is no longer used. + * + * @param boolean $rpcParams + * @access public + * @deprecated + */ + function setDefaultRpcParams($rpcParams) { + $this->defaultRpcParams = $rpcParams; + } + + /** + * dynamically creates an instance of a proxy class, + * allowing user to directly call methods from wsdl + * + * @return object soap_proxy object + * @access public + */ + function getProxy() { + $r = rand(); + $evalStr = $this->_getProxyClassCode($r); + //$this->debug("proxy class: $evalStr"); + if ($this->getError()) { + $this->debug("Error from _getProxyClassCode, so return NULL"); + return null; + } + // eval the class + eval($evalStr); + // instantiate proxy object + eval("\$proxy = new nusoap_proxy_$r('');"); + // transfer current wsdl data to the proxy thereby avoiding parsing the wsdl twice + $proxy->endpointType = 'wsdl'; + $proxy->wsdlFile = $this->wsdlFile; + $proxy->wsdl = $this->wsdl; + $proxy->operations = $this->operations; + $proxy->defaultRpcParams = $this->defaultRpcParams; + // transfer other state + $proxy->soap_defencoding = $this->soap_defencoding; + $proxy->username = $this->username; + $proxy->password = $this->password; + $proxy->authtype = $this->authtype; + $proxy->certRequest = $this->certRequest; + $proxy->requestHeaders = $this->requestHeaders; + $proxy->endpoint = $this->endpoint; + $proxy->forceEndpoint = $this->forceEndpoint; + $proxy->proxyhost = $this->proxyhost; + $proxy->proxyport = $this->proxyport; + $proxy->proxyusername = $this->proxyusername; + $proxy->proxypassword = $this->proxypassword; + $proxy->http_encoding = $this->http_encoding; + $proxy->timeout = $this->timeout; + $proxy->response_timeout = $this->response_timeout; + $proxy->persistentConnection = &$this->persistentConnection; + $proxy->decode_utf8 = $this->decode_utf8; + $proxy->curl_options = $this->curl_options; + $proxy->bindingType = $this->bindingType; + $proxy->use_curl = $this->use_curl; + return $proxy; + } + + /** + * dynamically creates proxy class code + * + * @return string PHP/NuSOAP code for the proxy class + * @access private + */ + function _getProxyClassCode($r) { + $this->debug("in getProxy endpointType=$this->endpointType"); + $this->appendDebug("wsdl=" . $this->varDump($this->wsdl)); + if ($this->endpointType != 'wsdl') { + $evalStr = 'A proxy can only be created for a WSDL client'; + $this->setError($evalStr); + $evalStr = "echo \"$evalStr\";"; + return $evalStr; + } + if ($this->endpointType == 'wsdl' && is_null($this->wsdl)) { + $this->loadWSDL(); + if ($this->getError()) { + return "echo \"" . $this->getError() . "\";"; + } + } + $evalStr = ''; + foreach ($this->operations as $operation => $opData) { + if ($operation != '') { + // create param string and param comment string + if (sizeof($opData['input']['parts']) > 0) { + $paramStr = ''; + $paramArrayStr = ''; + $paramCommentStr = ''; + foreach ($opData['input']['parts'] as $name => $type) { + $paramStr .= "\$$name, "; + $paramArrayStr .= "'$name' => \$$name, "; + $paramCommentStr .= "$type \$$name, "; + } + $paramStr = substr($paramStr, 0, strlen($paramStr)-2); + $paramArrayStr = substr($paramArrayStr, 0, strlen($paramArrayStr)-2); + $paramCommentStr = substr($paramCommentStr, 0, strlen($paramCommentStr)-2); + } else { + $paramStr = ''; + $paramArrayStr = ''; + $paramCommentStr = 'void'; + } + $opData['namespace'] = !isset($opData['namespace']) ? 'http://testuri.com' : $opData['namespace']; + $evalStr .= "// $paramCommentStr + function " . str_replace('.', '__', $operation) . "($paramStr) { + \$params = array($paramArrayStr); + return \$this->call('$operation', \$params, '".$opData['namespace']."', '".(isset($opData['soapAction']) ? $opData['soapAction'] : '')."'); + } + "; + unset($paramStr); + unset($paramCommentStr); + } + } + $evalStr = 'class nusoap_proxy_'.$r.' extends nusoap_client { + '.$evalStr.' +}'; + return $evalStr; + } + + /** + * dynamically creates proxy class code + * + * @return string PHP/NuSOAP code for the proxy class + * @access public + */ + function getProxyClassCode() { + $r = rand(); + return $this->_getProxyClassCode($r); + } + + /** + * gets the HTTP body for the current request. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + return $soapmsg; + } + + /** + * gets the HTTP content type for the current request. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current request. + * @access private + */ + function getHTTPContentType() { + return 'text/xml'; + } + + /** + * gets the HTTP content type charset for the current request. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current request. + * @access private + */ + function getHTTPContentTypeCharset() { + return $this->soap_defencoding; + } + + /* + * whether or not parser should decode utf8 element content + * + * @return always returns true + * @access public + */ + function decodeUTF8($bool){ + $this->decode_utf8 = $bool; + return true; + } + + /** + * adds a new Cookie into $this->cookies array + * + * @param string $name Cookie Name + * @param string $value Cookie Value + * @return boolean if cookie-set was successful returns true, else false + * @access public + */ + function setCookie($name, $value) { + if (strlen($name) == 0) { + return false; + } + $this->cookies[] = array('name' => $name, 'value' => $value); + return true; + } + + /** + * gets all Cookies + * + * @return array with all internal cookies + * @access public + */ + function getCookies() { + return $this->cookies; + } + + /** + * checks all Cookies and delete those which are expired + * + * @return boolean always return true + * @access private + */ + function checkCookies() { + if (sizeof($this->cookies) == 0) { + return true; + } + $this->debug('checkCookie: check ' . sizeof($this->cookies) . ' cookies'); + $curr_cookies = $this->cookies; + $this->cookies = array(); + foreach ($curr_cookies as $cookie) { + if (! is_array($cookie)) { + $this->debug('Remove cookie that is not an array'); + continue; + } + if ((isset($cookie['expires'])) && (! empty($cookie['expires']))) { + if (strtotime($cookie['expires']) > time()) { + $this->cookies[] = $cookie; + } else { + $this->debug('Remove expired cookie ' . $cookie['name']); + } + } else { + $this->cookies[] = $cookie; + } + } + $this->debug('checkCookie: '.sizeof($this->cookies).' cookies left in array'); + return true; + } + + /** + * updates the current cookies with a new set + * + * @param array $cookies new cookies with which to update current ones + * @return boolean always return true + * @access private + */ + function UpdateCookies($cookies) { + if (sizeof($this->cookies) == 0) { + // no existing cookies: take whatever is new + if (sizeof($cookies) > 0) { + $this->debug('Setting new cookie(s)'); + $this->cookies = $cookies; + } + return true; + } + if (sizeof($cookies) == 0) { + // no new cookies: keep what we've got + return true; + } + // merge + foreach ($cookies as $newCookie) { + if (!is_array($newCookie)) { + continue; + } + if ((!isset($newCookie['name'])) || (!isset($newCookie['value']))) { + continue; + } + $newName = $newCookie['name']; + + $found = false; + for ($i = 0; $i < count($this->cookies); $i++) { + $cookie = $this->cookies[$i]; + if (!is_array($cookie)) { + continue; + } + if (!isset($cookie['name'])) { + continue; + } + if ($newName != $cookie['name']) { + continue; + } + $newDomain = isset($newCookie['domain']) ? $newCookie['domain'] : 'NODOMAIN'; + $domain = isset($cookie['domain']) ? $cookie['domain'] : 'NODOMAIN'; + if ($newDomain != $domain) { + continue; + } + $newPath = isset($newCookie['path']) ? $newCookie['path'] : 'NOPATH'; + $path = isset($cookie['path']) ? $cookie['path'] : 'NOPATH'; + if ($newPath != $path) { + continue; + } + $this->cookies[$i] = $newCookie; + $found = true; + $this->debug('Update cookie ' . $newName . '=' . $newCookie['value']); + break; + } + if (! $found) { + $this->debug('Add cookie ' . $newName . '=' . $newCookie['value']); + $this->cookies[] = $newCookie; + } + } + return true; + } +} + +if (!extension_loaded('soap')) { + /** + * For backwards compatiblity, define soapclient unless the PHP SOAP extension is loaded. + */ + class soapclient extends nusoap_client { + } +} +?> diff --git a/classes/lib/nusoapmime.php b/classes/lib/nusoapmime.php new file mode 100644 index 0000000..6493863 --- /dev/null +++ b/classes/lib/nusoapmime.php @@ -0,0 +1,501 @@ + +* @author Thanks to Guillaume and Henning Reich for posting great attachment code to the mail list +* @version $Id: nusoapmime.php,v 1.13 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_client_mime extends nusoap_client { + /** + * @var array Each array element in the return is an associative array with keys + * data, filename, contenttype, cid + * @access private + */ + var $requestAttachments = array(); + /** + * @var array Each array element in the return is an associative array with keys + * data, filename, contenttype, cid + * @access private + */ + var $responseAttachments; + /** + * @var string + * @access private + */ + var $mimeContentType; + + /** + * adds a MIME attachment to the current request. + * + * If the $data parameter contains an empty string, this method will read + * the contents of the file named by the $filename parameter. + * + * If the $cid parameter is false, this method will generate the cid. + * + * @param string $data The data of the attachment + * @param string $filename The filename of the attachment (default is empty string) + * @param string $contenttype The MIME Content-Type of the attachment (default is application/octet-stream) + * @param string $cid The content-id (cid) of the attachment (default is false) + * @return string The content-id (cid) of the attachment + * @access public + */ + function addAttachment($data, $filename = '', $contenttype = 'application/octet-stream', $cid = false) { + if (! $cid) { + $cid = md5(uniqid(time())); + } + + $info['data'] = $data; + $info['filename'] = $filename; + $info['contenttype'] = $contenttype; + $info['cid'] = $cid; + + $this->requestAttachments[] = $info; + + return $cid; + } + + /** + * clears the MIME attachments for the current request. + * + * @access public + */ + function clearAttachments() { + $this->requestAttachments = array(); + } + + /** + * gets the MIME attachments from the current response. + * + * Each array element in the return is an associative array with keys + * data, filename, contenttype, cid. These keys correspond to the parameters + * for addAttachment. + * + * @return array The attachments. + * @access public + */ + function getAttachments() { + return $this->responseAttachments; + } + + /** + * gets the HTTP body for the current request. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + if (count($this->requestAttachments) > 0) { + $params['content_type'] = 'multipart/related; type="text/xml"'; + $mimeMessage = new Mail_mimePart('', $params); + unset($params); + + $params['content_type'] = 'text/xml'; + $params['encoding'] = '8bit'; + $params['charset'] = $this->soap_defencoding; + $mimeMessage->addSubpart($soapmsg, $params); + + foreach ($this->requestAttachments as $att) { + unset($params); + + $params['content_type'] = $att['contenttype']; + $params['encoding'] = 'base64'; + $params['disposition'] = 'attachment'; + $params['dfilename'] = $att['filename']; + $params['cid'] = $att['cid']; + + if ($att['data'] == '' && $att['filename'] <> '') { + if ($fd = fopen($att['filename'], 'rb')) { + $data = fread($fd, filesize($att['filename'])); + fclose($fd); + } else { + $data = ''; + } + $mimeMessage->addSubpart($data, $params); + } else { + $mimeMessage->addSubpart($att['data'], $params); + } + } + + $output = $mimeMessage->encode(); + $mimeHeaders = $output['headers']; + + foreach ($mimeHeaders as $k => $v) { + $this->debug("MIME header $k: $v"); + if (strtolower($k) == 'content-type') { + // PHP header() seems to strip leading whitespace starting + // the second line, so force everything to one line + $this->mimeContentType = str_replace("\r\n", " ", $v); + } + } + + return $output['body']; + } + + return parent::getHTTPBody($soapmsg); + } + + /** + * gets the HTTP content type for the current request. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current request. + * @access private + */ + function getHTTPContentType() { + if (count($this->requestAttachments) > 0) { + return $this->mimeContentType; + } + return parent::getHTTPContentType(); + } + + /** + * gets the HTTP content type charset for the current request. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current request. + * @access private + */ + function getHTTPContentTypeCharset() { + if (count($this->requestAttachments) > 0) { + return false; + } + return parent::getHTTPContentTypeCharset(); + } + + /** + * processes SOAP message returned from server + * + * @param array $headers The HTTP headers + * @param string $data unprocessed response data from server + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseResponse($headers, $data) { + $this->debug('Entering parseResponse() for payload of length ' . strlen($data) . ' and type of ' . $headers['content-type']); + $this->responseAttachments = array(); + if (strstr($headers['content-type'], 'multipart/related')) { + $this->debug('Decode multipart/related'); + $input = ''; + foreach ($headers as $k => $v) { + $input .= "$k: $v\r\n"; + } + $params['input'] = $input . "\r\n" . $data; + $params['include_bodies'] = true; + $params['decode_bodies'] = true; + $params['decode_headers'] = true; + + $structure = Mail_mimeDecode::decode($params); + + foreach ($structure->parts as $part) { + if (!isset($part->disposition) && (strstr($part->headers['content-type'], 'text/xml'))) { + $this->debug('Have root part of type ' . $part->headers['content-type']); + $root = $part->body; + $return = parent::parseResponse($part->headers, $part->body); + } else { + $this->debug('Have an attachment of type ' . $part->headers['content-type']); + $info['data'] = $part->body; + $info['filename'] = isset($part->d_parameters['filename']) ? $part->d_parameters['filename'] : ''; + $info['contenttype'] = $part->headers['content-type']; + $info['cid'] = $part->headers['content-id']; + $this->responseAttachments[] = $info; + } + } + + if (isset($return)) { + $this->responseData = $root; + return $return; + } + + $this->setError('No root part found in multipart/related content'); + return ''; + } + $this->debug('Not multipart/related'); + return parent::parseResponse($headers, $data); + } +} + +/* + * For backwards compatiblity, define soapclientmime unless the PHP SOAP extension is loaded. + */ +if (!extension_loaded('soap')) { + class soapclientmime extends nusoap_client_mime { + } +} + +/** +* nusoap_server_mime server supporting MIME attachments defined at +* http://www.w3.org/TR/SOAP-attachments. It depends on the PEAR Mail_MIME library. +* +* @author Scott Nichol +* @author Thanks to Guillaume and Henning Reich for posting great attachment code to the mail list +* @version $Id: nusoapmime.php,v 1.13 2010/04/26 20:15:08 snichol Exp $ +* @access public +*/ +class nusoap_server_mime extends nusoap_server { + /** + * @var array Each array element in the return is an associative array with keys + * data, filename, contenttype, cid + * @access private + */ + var $requestAttachments = array(); + /** + * @var array Each array element in the return is an associative array with keys + * data, filename, contenttype, cid + * @access private + */ + var $responseAttachments; + /** + * @var string + * @access private + */ + var $mimeContentType; + + /** + * adds a MIME attachment to the current response. + * + * If the $data parameter contains an empty string, this method will read + * the contents of the file named by the $filename parameter. + * + * If the $cid parameter is false, this method will generate the cid. + * + * @param string $data The data of the attachment + * @param string $filename The filename of the attachment (default is empty string) + * @param string $contenttype The MIME Content-Type of the attachment (default is application/octet-stream) + * @param string $cid The content-id (cid) of the attachment (default is false) + * @return string The content-id (cid) of the attachment + * @access public + */ + function addAttachment($data, $filename = '', $contenttype = 'application/octet-stream', $cid = false) { + if (! $cid) { + $cid = md5(uniqid(time())); + } + + $info['data'] = $data; + $info['filename'] = $filename; + $info['contenttype'] = $contenttype; + $info['cid'] = $cid; + + $this->responseAttachments[] = $info; + + return $cid; + } + + /** + * clears the MIME attachments for the current response. + * + * @access public + */ + function clearAttachments() { + $this->responseAttachments = array(); + } + + /** + * gets the MIME attachments from the current request. + * + * Each array element in the return is an associative array with keys + * data, filename, contenttype, cid. These keys correspond to the parameters + * for addAttachment. + * + * @return array The attachments. + * @access public + */ + function getAttachments() { + return $this->requestAttachments; + } + + /** + * gets the HTTP body for the current response. + * + * @param string $soapmsg The SOAP payload + * @return string The HTTP body, which includes the SOAP payload + * @access private + */ + function getHTTPBody($soapmsg) { + if (count($this->responseAttachments) > 0) { + $params['content_type'] = 'multipart/related; type="text/xml"'; + $mimeMessage = new Mail_mimePart('', $params); + unset($params); + + $params['content_type'] = 'text/xml'; + $params['encoding'] = '8bit'; + $params['charset'] = $this->soap_defencoding; + $mimeMessage->addSubpart($soapmsg, $params); + + foreach ($this->responseAttachments as $att) { + unset($params); + + $params['content_type'] = $att['contenttype']; + $params['encoding'] = 'base64'; + $params['disposition'] = 'attachment'; + $params['dfilename'] = $att['filename']; + $params['cid'] = $att['cid']; + + if ($att['data'] == '' && $att['filename'] <> '') { + if ($fd = fopen($att['filename'], 'rb')) { + $data = fread($fd, filesize($att['filename'])); + fclose($fd); + } else { + $data = ''; + } + $mimeMessage->addSubpart($data, $params); + } else { + $mimeMessage->addSubpart($att['data'], $params); + } + } + + $output = $mimeMessage->encode(); + $mimeHeaders = $output['headers']; + + foreach ($mimeHeaders as $k => $v) { + $this->debug("MIME header $k: $v"); + if (strtolower($k) == 'content-type') { + // PHP header() seems to strip leading whitespace starting + // the second line, so force everything to one line + $this->mimeContentType = str_replace("\r\n", " ", $v); + } + } + + return $output['body']; + } + + return parent::getHTTPBody($soapmsg); + } + + /** + * gets the HTTP content type for the current response. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type for the current response. + * @access private + */ + function getHTTPContentType() { + if (count($this->responseAttachments) > 0) { + return $this->mimeContentType; + } + return parent::getHTTPContentType(); + } + + /** + * gets the HTTP content type charset for the current response. + * returns false for non-text content types. + * + * Note: getHTTPBody must be called before this. + * + * @return string the HTTP content type charset for the current response. + * @access private + */ + function getHTTPContentTypeCharset() { + if (count($this->responseAttachments) > 0) { + return false; + } + return parent::getHTTPContentTypeCharset(); + } + + /** + * processes SOAP message received from client + * + * @param array $headers The HTTP headers + * @param string $data unprocessed request data from client + * @return mixed value of the message, decoded into a PHP type + * @access private + */ + function parseRequest($headers, $data) { + $this->debug('Entering parseRequest() for payload of length ' . strlen($data) . ' and type of ' . $headers['content-type']); + $this->requestAttachments = array(); + if (strstr($headers['content-type'], 'multipart/related')) { + $this->debug('Decode multipart/related'); + $input = ''; + foreach ($headers as $k => $v) { + $input .= "$k: $v\r\n"; + } + $params['input'] = $input . "\r\n" . $data; + $params['include_bodies'] = true; + $params['decode_bodies'] = true; + $params['decode_headers'] = true; + + $structure = Mail_mimeDecode::decode($params); + + foreach ($structure->parts as $part) { + if (!isset($part->disposition) && (strstr($part->headers['content-type'], 'text/xml'))) { + $this->debug('Have root part of type ' . $part->headers['content-type']); + $return = parent::parseRequest($part->headers, $part->body); + } else { + $this->debug('Have an attachment of type ' . $part->headers['content-type']); + $info['data'] = $part->body; + $info['filename'] = isset($part->d_parameters['filename']) ? $part->d_parameters['filename'] : ''; + $info['contenttype'] = $part->headers['content-type']; + $info['cid'] = $part->headers['content-id']; + $this->requestAttachments[] = $info; + } + } + + if (isset($return)) { + return $return; + } + + $this->setError('No root part found in multipart/related content'); + return; + } + $this->debug('Not multipart/related'); + return parent::parseRequest($headers, $data); + } +} + +/* + * For backwards compatiblity + */ +class nusoapservermime extends nusoap_server_mime { +} + +?> diff --git a/classes/test.php b/classes/test.php new file mode 100644 index 0000000..c57e72a --- /dev/null +++ b/classes/test.php @@ -0,0 +1,70 @@ +soap_defencoding = 'utf-8'; +// We define the parameters as a string array. Each Key/Val represents a parameter of the soap call +// On défini les paramètres dans un tableau de chaînes. Chaque paire Clé/Valeur est un paramètre de l'appel +$params = array( +'Enseigne' => $MR_WebSiteId, + 'Pays' => "FR", + //'NumPointRelais' => "", + 'Ville' => "LYON", + 'CP' => "69008", + 'Latitude' => "", + 'Longitude' => "", + 'Taille' => "", + 'Poids' => "70000", + 'Action' => "", + 'DelaiEnvoi' => "0", + 'RayonRecherche' => "20", + //'TypeActivite' => "", + //'NACE' => "", +); +// We generate the request's security code +// On génère la clé de sécurité de l'appel +$code = implode("", $params); +$ip_address = array("78.192.80.40", "127.0.0.1", "::1"); +$ip_key = array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ? 'HTTP_X_FORWARDED_FOR' : 'REMOTE_ADDR'; +if(in_array($_SERVER[$ip_key], $ip_address)){ + echo "
";
+    var_dump($code);
+    var_dump($MR_WebSiteKey);
+    echo "
"; + die; +} +$code .= $MR_WebSiteKey; +$params["Security"] = strtoupper(md5($code)); +// We make the call and load it in the $result var +// On rƩalise l'appel et stocke le rƩsultat dans la variable $result +$result = $client->call( + 'WSI3_PointRelais_Recherche', + $params, + 'http://api.mondialrelay.com/', + 'http://api.mondialrelay.com/WSI3_PointRelais_Recherche' +);// We check their is no error during the process +// On vƩrifie qu'il n'y a pas eu d'erreur +if ($client->fault) +{ +echo '

Fault (Expect - The request contains an invalid SOAP body)

';
+print_r($result);
+echo '
'; +} +else +{ +$err = $client->getError(); +if ($err) { echo '

Error

' . $err . '
'; } +else +{ +echo '

Result

';
+print_r($result);
+echo '
'; +} +} +echo '

Request

' . htmlspecialchars($client->request, ENT_QUOTES) . '
'; +echo '

Response

' . htmlspecialchars($client->response, ENT_QUOTES) . '
'; +echo '

Debug

' . htmlspecialchars($client->getDebug(), ENT_QUOTES) . '
'; +?> \ No newline at end of file diff --git a/config.xml b/config.xml index 32240ea..8ecf352 100644 --- a/config.xml +++ b/config.xml @@ -1,12 +1,12 @@ - - mondialrelay - - - - - - 1 - 1 + + mondialrelay + + + + + + 1 + 1 - + \ No newline at end of file diff --git a/config_fr.xml b/config_fr.xml new file mode 100644 index 0000000..345b39a --- /dev/null +++ b/config_fr.xml @@ -0,0 +1,12 @@ + + + mondialrelay + + + + + + 1 + 1 + + \ No newline at end of file diff --git a/connexion.php b/connexion.php index 1b794f4..f35f265 100644 --- a/connexion.php +++ b/connexion.php @@ -28,33 +28,34 @@ include_once(dirname(__FILE__).'/mondialrelay.php'); include_once(dirname(__FILE__).'/errorCode.php'); try{ - if (!Tools::getValue('token')) - die('{"error":"Security error"}'); - if (sha1('mr'._COOKIE_KEY_.'Back') != Tools::getValue('token')) - die('{"error":"Security error"}'); - - $webservice = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; - $client = new SoapClient($webservice); - $params = array(); - $params['Enseigne'] = Tools::getValue('enseigne'); - $params['Poids'] = ''; - $params['Taille'] = ''; - $params['CP'] = (Configuration::get('PS_SHOP_CODE')) ? Configuration::get('PS_SHOP_CODE') : '75000'; - $params['Ville'] = ''; - $id_country = (Configuration::get('PS_SHOP_COUNTRY_ID')) ? Configuration::get('PS_SHOP_COUNTRY_ID') : Configuration::get('PS_COUNTRY_DEFAULT'); - $params['Pays'] = Country::getIsoById($id_country); - $params['Action'] = ''; - $concat = $params['Enseigne'].$params['Pays'].$params['Ville'].$params['CP'].$params['Poids'].Tools::getValue('key'); - $params['Security'] = Tools::strtoupper(md5($concat)); - $result_mr = $client->WSI2_RecherchePointRelais($params); - if (($errorNumber = $result_mr->WSI2_RecherchePointRelaisResult->STAT) != 0) - { - echo '{"error":"'.str_replace('"', '', $statCode[$errorNumber]).'"}'; - die(); - } - echo '{"success":1}'; + if (!Tools::getValue('token')) + die('{"error":"Security error"}'); + if (sha1('mr'._COOKIE_KEY_.'Back') != Tools::getValue('token')) + die('{"error":"Security error"}'); + + $webservice = MondialRelay::MR_URL.'webservice/Web_Services.asmx?WSDL'; + $client = new SoapClient($webservice); + $params = array(); + $params['Enseigne'] = Tools::getValue('enseigne'); + $params['Poids'] = ''; + $params['Taille'] = ''; + $params['CP'] = (Configuration::get('PS_SHOP_CODE')) ? Configuration::get('PS_SHOP_CODE') : '75000'; + $params['Ville'] = ''; + $id_country = (Configuration::get('PS_SHOP_COUNTRY_ID')) ? Configuration::get('PS_SHOP_COUNTRY_ID') : Configuration::get('PS_COUNTRY_DEFAULT'); + $params['Pays'] = Country::getIsoById($id_country); + $params['Action'] = ''; + $params['RayonRecherche'] = ''; + $concat = $params['Enseigne'].$params['Pays'].$params['Ville'].$params['CP'].$params['Poids'].Tools::getValue('key'); + $params['Security'] = Tools::strtoupper(md5($concat)); + $result_mr = $client->WSI2_RecherchePointRelais($params); + if (($errorNumber = $result_mr->WSI2_RecherchePointRelaisResult->STAT) != 0) + { + echo '{"error":"'.str_replace('"', '', $statCode[$errorNumber]).'"}'; + die(); + } + echo '{"success":1}'; } catch(Exception $e) { - echo '{"error":"'.str_replace('"', '', $statCode[99]).'"}'; - die(); + echo '{"error":"'.str_replace('"', '', $statCode[99]).'"}'; + die(); } \ No newline at end of file diff --git a/cron.php b/cron.php index fb55596..05157f8 100644 --- a/cron.php +++ b/cron.php @@ -28,7 +28,7 @@ include_once('mondialrelay.php'); if (Tools::getValue('secure_key') != Configuration::get('MONDIAL_RELAY_SECURE_KEY')) - exit; + exit; $account_shop = MondialRelay::getAccountDetail(); @@ -39,10 +39,10 @@ WHERE `exp_number` != 0'); if (empty($expeditions)) - die('No order'); + die('No order'); else - echo 'Start progress
'; - + echo 'Start progress
'; + $params = array( 'Enseigne' => $account_shop['MR_ENSEIGNE_WEBSERVICE'], 'Langue' => 'FR' @@ -55,34 +55,34 @@ $i = 0; foreach ($expeditions as $expedition) { - if ($expedition['id_order'] == null) - continue; - $order = new Order((int)($expedition['id_order'])); - if ($order->current_state == Configuration::get('PS_OS_DELIVERED')) - continue; - $params['Expedition'] = $expedition['exp_number']; - $params['Security'] = Tools::strtoupper(md5($params['Enseigne'].$params['Expedition'].'FR'.$account_shop['MR_KEY_WEBSERVICE'])); - - $is_delivered = 0; - $result_mr = $client_mr->WSI2_TracingColisDetaille($params); - - if (isset($result_mr->WSI2_TracingColisDetailleResult->Tracing->ret_WSI2_sub_TracingColisDetaille)) - foreach ($result_mr->WSI2_TracingColisDetailleResult->Tracing->ret_WSI2_sub_TracingColisDetaille as $result) - if (isset($result->Libelle) && $result->Libelle == 'COLIS LIVRƉ') - { - $is_delivered = 1; - break; - } - - if ($is_delivered == 1) - { - $history = new OrderHistory(); - $history->id_order = (int)($expedition['id_order']); - $history->changeIdOrderState((int)(Configuration::get('PS_OS_DELIVERED')), (int)($expedition['id_order'])); - $history->addWithemail(); - } - $i++; - - echo '

'.$i.'

'; + if ($expedition['id_order'] == null) + continue; + $order = new Order((int)($expedition['id_order'])); + if ($order->current_state == Configuration::get('PS_OS_DELIVERED')) + continue; + $params['Expedition'] = $expedition['exp_number']; + $params['Security'] = Tools::strtoupper(md5($params['Enseigne'].$params['Expedition'].'FR'.$account_shop['MR_KEY_WEBSERVICE'])); + + $is_delivered = 0; + $result_mr = $client_mr->WSI2_TracingColisDetaille($params); + + if (isset($result_mr->WSI2_TracingColisDetailleResult->Tracing->ret_WSI2_sub_TracingColisDetaille)) + foreach ($result_mr->WSI2_TracingColisDetailleResult->Tracing->ret_WSI2_sub_TracingColisDetaille as $result) + if (isset($result->Libelle) && $result->Libelle == 'COLIS LIVRƉ') + { + $is_delivered = 1; + break; + } + + if ($is_delivered == 1) + { + $history = new OrderHistory(); + $history->id_order = (int)($expedition['id_order']); + $history->changeIdOrderState((int)(Configuration::get('PS_OS_DELIVERED')), (int)($expedition['id_order'])); + $history->addWithemail(); + } + $i++; + + echo '

'.$i.'

'; } echo 'End progress
'; \ No newline at end of file diff --git a/css/index.php b/css/index.php index ce38668..dc40d62 100644 --- a/css/index.php +++ b/css/index.php @@ -24,13 +24,13 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ - + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/docs/index.php b/docs/index.php index 9d353d7..cd81008 100644 --- a/docs/index.php +++ b/docs/index.php @@ -23,13 +23,13 @@ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ - + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/docs/install.pdf b/docs/install.pdf index 663942b..bd2df23 100644 Binary files a/docs/install.pdf and b/docs/install.pdf differ diff --git a/errorCode.php b/errorCode.php index 36a55ca..e77b912 100644 --- a/errorCode.php +++ b/errorCode.php @@ -25,77 +25,77 @@ */ $statCode = array( - '1' => 'Enseigne invalide', - '2' => 'NumĆ©ro d\'enseigne vide ou inexistant', - '3' => 'NumĆ©ro de compte enseigne invalide', - '5' => 'NumĆ©ro de dossier enseigne invalide', - '7' => 'NumĆ©ro de client enseigne invalide', - '9' => 'Nom de ville non reconnu ou non unique', - '10' => 'Type de collecte invalide ou incorrect (1/D > Domicile -- 3/R > Relais)', - '11' => 'NumĆ©ro de Point Relais de collecte invalide', - '12' => 'Pays du Point Relais de collecte invalide', - '13' => 'Type de livraison invalide ou incorrect (1/D > Domicile -- 3/R > Relais)', - '14' => 'NumĆ©ro du Point Relais de livraison invalide', - '15' => 'Pays du Point Relais de livraison invalide', - '16' => 'Code pays invalide', - '17' => 'Adresse invalide', - '18' => 'Ville invalide', - '19' => 'Code postal invalide', - '20' => 'Poids du colis invalide', - '21' => 'Taille (Longueur + Hauteur) du colis invalide', - '22' => 'Taille du Colis invalide', - '24' => 'NumĆ©ro de Colis Mondial Relay invalide', - '29' => 'Mode de livraison invalide', - '30' => 'Adresse (L1) de l\'expĆ©diteur invalide', - '31' => 'Adresse (L2) de l\'expĆ©diteur invalide', - '33' => 'Adresse (L3) de l\'expĆ©diteur invalide', - '34' => 'Adresse (L4) de l\'expĆ©diteur invalide', - '35' => 'Ville de l\'expĆ©diteur invalide', - '36' => 'Code postal de l\'expĆ©diteur invalide', - '37' => 'Pays de l\'expĆ©diteur invalide', - '38' => 'NumĆ©ro de tĆ©lĆ©phone de l\'expĆ©diteur invalide', - '39' => 'Adresse e-mail de l\'expĆ©diteur invalide', - '40' => 'Action impossible sans ville ni code postal', - '41' => 'Mode de livraison invalide', - '42' => 'Montant CRT invalide', - '43' => 'Devise CRT invalide', - '44' => 'Valeur du colis invalide', - '45' => 'Devise de la valeur du colis invalide', - '46' => 'Plage de numĆ©ro d\'expĆ©dition Ć©puisĆ©e', - '47' => 'Nombre de colis invalide', - '48' => 'Multi-colis en Point Relais Interdit', - '49' => 'Mode de collecte ou de livraison invalide', - '50' => 'Adresse (L1) du destinataire invalide', - '51' => 'Adresse (L2) du destinataire invalide', - '53' => 'Adresse (L3) du destinataire invalide', - '54' => 'Adresse (L4) du destinataire invalide', - '55' => 'Ville du destinataire invalide', - '56' => 'Code postal du destinataire invalide', - '57' => 'Pays du destinataire invalide', - '58' => 'NumĆ©ro de tĆ©lĆ©phone du destinataire invalide', - '59' => 'Adresse e-mail du destinataire invalide', - '60' => 'Champ texte libre invalide', - '61' => 'Top avisage invalide', - '62' => 'Instruction de livraison invalide', - '63' => 'Assurance invalide ou incorrecte', - '64' => 'Temps de montage invalide', - '65' => 'Top rendez-vous invalide', - '66' => 'Top reprise invalide', - '70' => 'NumĆ©ro de Point Relais invalide', - '72' => 'Langue expĆ©diteur invalide', - '73' => 'Langue destinataire invalide', - '74' => 'Langue invalide', - '80' => 'Code tracing : Colis enregistrĆ©', - '81' => 'Code tracing : Colis en traitement chez Mondial Relay', - '82' => 'Code tracing : Colis livrĆ©', - '83' => 'Code tracing : Anomalie', - '90' => 'AS400 indisponible', - '91' => 'NumĆ©ro d\'expĆ©dition invalide', - '94' => 'Colis Inexistant', - '95' => 'Compte Enseigne non activĆ©', - '96' => 'Type d\'enseigne incorrect en Base', - '97' => 'ClĆ© de sĆ©curitĆ© invalide', - '98' => 'Service Indisponible', - '99' => 'Erreur gĆ©nĆ©rique du service. Cette erreur peut ĆŖtre dĆ» autant Ć  un problĆØme technique du service qu\'Ć  des donnĆ©es incorrectes ou inexistantes dans la Base de DonnĆ©es. Lorsque vous avez cette erreur veuillez la notifier Ć  Mondial Relay en prĆ©cisant la date et l\'heure de la connexion ainsi que les informations envoyĆ©s au WebService afin d\'effectuer une vĆ©rification.'); + '1' => 'Enseigne invalide', + '2' => 'NumĆ©ro d\'enseigne vide ou inexistant', + '3' => 'NumĆ©ro de compte enseigne invalide', + '5' => 'NumĆ©ro de dossier enseigne invalide', + '7' => 'NumĆ©ro de client enseigne invalide', + '9' => 'Nom de ville non reconnu ou non unique', + '10' => 'Type de collecte invalide ou incorrect (1/D > Domicile -- 3/R > Relais)', + '11' => 'NumĆ©ro de Point Relais de collecte invalide', + '12' => 'Pays du Point Relais de collecte invalide', + '13' => 'Type de livraison invalide ou incorrect (1/D > Domicile -- 3/R > Relais)', + '14' => 'NumĆ©ro du Point Relais de livraison invalide', + '15' => 'Pays du Point Relais de livraison invalide', + '16' => 'Code pays invalide', + '17' => 'Adresse invalide', + '18' => 'Ville invalide', + '19' => 'Code postal invalide', + '20' => 'Poids du colis invalide', + '21' => 'Taille (Longueur + Hauteur) du colis invalide', + '22' => 'Taille du Colis invalide', + '24' => 'NumĆ©ro de Colis Mondial Relay invalide', + '29' => 'Mode de livraison invalide', + '30' => 'Adresse (L1) de l\'expĆ©diteur invalide', + '31' => 'Adresse (L2) de l\'expĆ©diteur invalide', + '33' => 'Adresse (L3) de l\'expĆ©diteur invalide', + '34' => 'Adresse (L4) de l\'expĆ©diteur invalide', + '35' => 'Ville de l\'expĆ©diteur invalide', + '36' => 'Code postal de l\'expĆ©diteur invalide', + '37' => 'Pays de l\'expĆ©diteur invalide', + '38' => 'NumĆ©ro de tĆ©lĆ©phone de l\'expĆ©diteur invalide', + '39' => 'Adresse e-mail de l\'expĆ©diteur invalide', + '40' => 'Action impossible sans ville ni code postal', + '41' => 'Mode de livraison invalide', + '42' => 'Montant CRT invalide', + '43' => 'Devise CRT invalide', + '44' => 'Valeur du colis invalide', + '45' => 'Devise de la valeur du colis invalide', + '46' => 'Plage de numĆ©ro d\'expĆ©dition Ć©puisĆ©e', + '47' => 'Nombre de colis invalide', + '48' => 'Multi-colis en Point Relais Interdit', + '49' => 'Mode de collecte ou de livraison invalide', + '50' => 'Adresse (L1) du destinataire invalide', + '51' => 'Adresse (L2) du destinataire invalide', + '53' => 'Adresse (L3) du destinataire invalide', + '54' => 'Adresse (L4) du destinataire invalide', + '55' => 'Ville du destinataire invalide', + '56' => 'Code postal du destinataire invalide', + '57' => 'Pays du destinataire invalide', + '58' => 'NumĆ©ro de tĆ©lĆ©phone du destinataire invalide', + '59' => 'Adresse e-mail du destinataire invalide', + '60' => 'Champ texte libre invalide', + '61' => 'Top avisage invalide', + '62' => 'Instruction de livraison invalide', + '63' => 'Assurance invalide ou incorrecte', + '64' => 'Temps de montage invalide', + '65' => 'Top rendez-vous invalide', + '66' => 'Top reprise invalide', + '70' => 'NumĆ©ro de Point Relais invalide', + '72' => 'Langue expĆ©diteur invalide', + '73' => 'Langue destinataire invalide', + '74' => 'Langue invalide', + '80' => 'Code tracing : Colis enregistrĆ©', + '81' => 'Code tracing : Colis en traitement chez Mondial Relay', + '82' => 'Code tracing : Colis livrĆ©', + '83' => 'Code tracing : Anomalie', + '90' => 'AS400 indisponible', + '91' => 'NumĆ©ro d\'expĆ©dition invalide', + '94' => 'Colis Inexistant', + '95' => 'Compte Enseigne non activĆ©', + '96' => 'Type d\'enseigne incorrect en Base', + '97' => 'ClĆ© de sĆ©curitĆ© invalide', + '98' => 'Service Indisponible', + '99' => 'Erreur gĆ©nĆ©rique du service. Cette erreur peut ĆŖtre dĆ» autant Ć  un problĆØme technique du service qu\'Ć  des donnĆ©es incorrectes ou inexistantes dans la Base de DonnĆ©es. Lorsque vous avez cette erreur veuillez la notifier Ć  Mondial Relay en prĆ©cisant la date et l\'heure de la connexion ainsi que les informations envoyĆ©s au WebService afin d\'effectuer une vĆ©rification.'); ?> diff --git a/fr.php b/fr.php index ba836d1..068ebe1 100644 --- a/fr.php +++ b/fr.php @@ -217,7 +217,9 @@ $_MODULE['<{mondialrelay}prestashop>settings_ca57625685f058f54d82c6e05876a131'] = 'Choississez le statut pour la gĆ©nĆ©ration d\'Ć©tiquette.'; $_MODULE['<{mondialrelay}prestashop>settings_c9cc8cce247e49bae79f15173ce97354'] = 'Sauvegarder'; $_MODULE['<{mondialrelay}prestashop>settings_f4f70727dc34561dfde1a3c529b6205c'] = 'ParamĆØtres'; +$_MODULE['<{mondialrelay}prestashop>checkout_process_50fa1af2938c04e8fb499c483c1fecb8'] = 'Le JavaScript n\'est pas activĆ© sur votre navigateur, nous vous invitons Ć  l’activer afin de sĆ©lectionner un Point RelaisĀ® lors de votre commande.'; $_MODULE['<{mondialrelay}prestashop>checkout_process_836f6c949a2f2d61489bf410def82111'] = 'Mondialrelay ne peut rĆ©cupĆ©rer les points relais du Ć  une erreur de Prestashop'; +$_MODULE['<{mondialrelay}prestashop>checkout_process_widget_50fa1af2938c04e8fb499c483c1fecb8'] = 'Le JavaScript n\'est pas activĆ© sur votre navigateur, nous vous invitons Ć  l’activer afin de sĆ©lectionner un Point RelaisĀ® lors de votre commande.'; $_MODULE['<{mondialrelay}prestashop>checkout_process_widget_ad3d06d03d94223fa652babc913de686'] = 'Valider votre Point RelaisĀ®'; $_MODULE['<{mondialrelay}prestashop>checkout_process_widget_b5cdf9b63fda35c33bb5e35cc3716b95'] = 'Point RelaisĀ® sĆ©lectionnĆ© :'; $_MODULE['<{mondialrelay}prestashop>header_e0626222614bdee31951d84c64e5e9ff'] = 'Choisir'; diff --git a/img/icones/index.php b/img/icones/index.php index 4deda52..9cf60e3 100644 --- a/img/icones/index.php +++ b/img/icones/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/img/index.php b/img/index.php index 0bfbc8e..fa0395b 100644 --- a/img/index.php +++ b/img/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/index.php b/index.php index 0bfbc8e..fa0395b 100644 --- a/index.php +++ b/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/js/common.js b/js/common.js new file mode 100644 index 0000000..26c1402 --- /dev/null +++ b/js/common.js @@ -0,0 +1,135 @@ +/** +* 2007-2014 Mondial relay +* +* NOTICE OF LICENSE +* +* This source file is subject to the Academic Free License (AFL 3.0) +* that is bundled with this package in the file LICENSE.txt. +* It is also available through the world-wide-web at this URL: +* http://opensource.org/licenses/afl-3.0.php +* If you did not receive a copy of the license and are unable to +* obtain it through the world-wide-web, please send an email +* to Mondial relay so we can send you a copy immediately. +* +* DISCLAIMER +* +* Do not edit or add to this file if you wish to upgrade Mondial relay to newer +* versions in the future. If you wish to customize Mondial relay for your +* needs please refer to Mondial relay for more information. +* +* @author Mondial relay +* @copyright 2007-2014 Mondial relay +* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) +* International Registered Trademark & Property of Mondial relay +*/ + +function checkMrSelection() { + + if (PS_MRData.PS_VERSION < '1.5') { + var selected_carrier_id = $('input[name=id_carrier]:checked').val(); + } else { + var selected_carrier_id = parseInt($('.delivery_option_radio:checked').val()); + } + + if (PS_MRSelectedRelayPoint['relayPointNum'] == -1) + return true; + + var MR_carrier_selected = (selected_carrier_id==PS_MRSelectedRelayPoint['carrier_id']); // is a mondial relay carrier ? + var MR_relay_selected = (PS_MRSelectedRelayPoint['relayPointNum']>0); // a relay has been selected ? + + if (!MR_carrier_selected) + return true; + + if (MR_relay_selected) { + return true; + } else { + if (!!$.prototype.fancybox && !(PS_MRData.PS_VERSION < '1.5')) + $.fancybox.open([ + { + type: 'inline', + autoScale: true, + minHeight: 30, + content: '

' + PS_MRTranslationList['errorSelection'] + '

' + }], + { + padding: 0 + }); + else + alert(PS_MRTranslationList['errorSelection']); + + return false; + } +} + +function isOnePageCheckout(){ + + if($('.payment_module a').length && typeof(PS_MRData) != 'undefined'){ + return true; + } + return false; +} + +function setProtectRelaySelected(){ + if(isOnePageCheckout()){ + $('.payment_module a').each(function(){ + if(typeof($(this).attr('onclick'))!='undefined'){ + $(this).data('onclick', $(this).attr('onclick')); + $(this).attr('onclick', ''); + } + }); + + //OPC Validation - Warn user to select a relay point + $('.payment_module').click(function(event) { + if(!checkMrSelection()){ + event.stopPropagation(); + return false; + } + else{ + var lien= $('a', $(this)); + if( lien.is("[onclick]") && lien.attr('onclick') == '' ){ + lien.attr('onclick', lien.data('onclick')); + lien.trigger('click'); + } + } + }); + + $('form').submit(function(event) { + + if(checkMrSelection()){ + $(this).off('submit').submit(); + + } + event.stopPropagation(); + return false; + }); + + } + + $('#form button[name="processCarrier"], #form input[name="processCarrier"]').click(function(event) { + if(!checkMrSelection()){ + event.stopPropagation(); + return false; + } + }); + +} + +(function($) { + + $(function(){ + $('#cgv').click(function(){ + if($(this).attr('checked')){ + $("body").bind("ajaxComplete", function(e, xhr, settings){ + setProtectRelaySelected(); + + }); + + } + }); + + + + + }); +})(jQuery); + diff --git a/js/index.php b/js/index.php index fc4110f..087b026 100644 --- a/js/index.php +++ b/js/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/js/mondialrelay.js b/js/mondialrelay.js index 504d0b7..bb8a0f3 100644 --- a/js/mondialrelay.js +++ b/js/mondialrelay.js @@ -537,7 +537,6 @@ var PS_MRObject = (function($, undefined) { function PS_MRAddSelectedCarrierInDB(id_carrier) { PS_MRHideLastRelayPointList(); - // Make the request $.ajax({ type: 'POST', @@ -567,8 +566,10 @@ var PS_MRObject = (function($, undefined) { // Reset for any carrier changement if (MRLivraisonType != 'LD1' && MRLivraisonType != 'LDS' && MRLivraisonType != 'HOM') { + if(PS_MRSelectedRelayPoint['relayPointNum'] == -1) + PS_MRSelectedRelayPoint['relayPointNum'] = 0; // Seek Relay point if it doesn't a home delivery mode - PS_MRGetRelayPoint(carrierSelected); + PS_MRGetRelayPoint(carrierSelected, MRLivraisonType); } else { @@ -594,7 +595,6 @@ var PS_MRObject = (function($, undefined) { function PS_MRCheckSelectedRelayPoint() { var input; - // Check if the input is linked to the module and look into // a temporary variable if a relay point has been selected if ((input = $('input[name=id_carrier]:checked')).length && @@ -663,7 +663,10 @@ var PS_MRObject = (function($, undefined) { for (relayPoint in json.success) { // Check if the the content wasn't already displayed - contentBlockid = 'relayPoint_' + json.success[relayPoint].Num + '_' + carrier_id; + + if (json.success[relayPoint].Num != '') { + contentBlockid = 'relayPoint_' + json.success[relayPoint].Num + '_' + carrier_id; + } if (!$('#' + contentBlockid).size()) { // Set translation if a preselection exist @@ -726,7 +729,7 @@ var PS_MRObject = (function($, undefined) { * * @param carrierSelected */ - function PS_MRFetchRelayPoint(carrierSelected) + function PS_MRFetchRelayPoint(carrierSelected, MRLivraisonType) { carrier_id = carrierSelected.val().replace(',', ''); // Block is an input, we need the 'tr' element @@ -748,6 +751,7 @@ var PS_MRObject = (function($, undefined) { 'id_carrier' : carrier_id, 'ajax' : true, 'step' : 2, + 'mode_liv' : MRLivraisonType, 'mrtoken' : mrtoken}, dataType: 'json', success: function(json) @@ -770,10 +774,9 @@ var PS_MRObject = (function($, undefined) { * * @param carrierSelected */ - function PS_MRGetRelayPoint(carrierSelected) + function PS_MRGetRelayPoint(carrierSelected, MRLivraisonType) { carrier_id = carrierSelected.val().replace(',',''); - // Init back the inital view, hide existing element, (keep cached) element = 0; totalElement = $('.PS_MRSelectedCarrier').size(); @@ -792,12 +795,12 @@ var PS_MRObject = (function($, undefined) { return ; } // If the element isn't cached, we fetch it - PS_MRFetchRelayPoint(carrierSelected); + PS_MRFetchRelayPoint(carrierSelected, MRLivraisonType); } ++element; }); else - PS_MRFetchRelayPoint(carrierSelected); + PS_MRFetchRelayPoint(carrierSelected, MRLivraisonType); } /** @@ -1155,6 +1158,7 @@ var PS_MRObject = (function($, undefined) { var carrier_selected = $('input.delivery_option_radio:checked').val(); var parent_element = $(e).parents('.delivery_option'); var new_element = 'MR_PR_list_'+MR_idcarrier; + if($('#'+new_element).length > 0) { $('#'+new_element).remove(); if( isMRCarrier(MR_idcarrier)!=false && typeof(MR_carrier) != "undefined" && (MR_carrier.dlv_mode!='LD1' && MR_carrier.dlv_mode!='LDS' && MR_carrier.dlv_mode!='HOM')) { @@ -1176,7 +1180,6 @@ var PS_MRObject = (function($, undefined) { // Hide MR input if one of them is not selected if($(e).val() == carrier_selected) { - console.log(carrier_selected); if(MR_carrier != false) { PS_MRCarrierMethodList[MR_idcarrier] = MR_carrier.id_mr_method; PS_MRSelectedRelayPoint['carrier_id'] = MR_idcarrier; @@ -1197,6 +1200,7 @@ var PS_MRObject = (function($, undefined) { } }); } + } } @@ -1281,20 +1285,7 @@ var PS_MRObject = (function($, undefined) { $('#MR_config_menu').children('ul').css('margin-left', width + 'px'); } - // 1.5 OPC Validation - Warn user to select a relay point - $('.payment_module a').live('click', function() { - if (typeof PS_MRData != 'undefined') - { - if (PS_MRData.PS_VERSION >= '1.5' && PS_MRData.carrier) - { - var _return = !(!PS_MRSelectedRelayPoint['carrier_id'] || !PS_MRSelectedRelayPoint['relayPointNum']); - if (!_return) - alert(PS_MRTranslationList['errorSelection']); - return _return; - } - } - }); - +/* // If MR carrier selected, check MR relay point is selected too $('input[name=processCarrier], button[name=processCarrier]').click(function(){ var _return = !(PS_MRSelectedRelayPoint['carrier_id'] && !PS_MRSelectedRelayPoint['relayPointNum']); @@ -1303,15 +1294,14 @@ var PS_MRObject = (function($, undefined) { return _return; }); - - if (typeof PS_MRData != 'undefined') +*/ + // En OPC, si l'utilisateur est déconnecter, PS_MRData n'existe pas au début + $('input[name="id_carrier"]').live('click', function(){ + if (typeof PS_MRData != 'undefined' && PS_MRData.PS_VERSION < '1.5') { - if (PS_MRData.PS_VERSION < '1.5') { - $('input[name="id_carrier"]').click(function(){ - checkToDisplayRelayList(); - }); - } + checkToDisplayRelayList(); } + }); // Handle input click of the other input to hide the previous relay point list displayed }); @@ -1320,6 +1310,7 @@ var PS_MRObject = (function($, undefined) { return { initFront : function() { checkToDisplayRelayList(); + setProtectRelaySelected(); }, uninstall : function(url) { @@ -1358,4 +1349,6 @@ function mr_checkConnexion() { } } }); -} \ No newline at end of file +} + + diff --git a/js/mondialrelay_widget.js b/js/mondialrelay_widget.js index 332427f..755d763 100644 --- a/js/mondialrelay_widget.js +++ b/js/mondialrelay_widget.js @@ -1,258 +1,235 @@ /* -* 2007-2015 PrestaShop -* -* NOTICE OF LICENSE -* -* This source file is subject to the Academic Free License (AFL 3.0) -* that is bundled with this package in the file LICENSE.txt. -* It is also available through the world-wide-web at this URL: -* http://opensource.org/licenses/afl-3.0.php -* If you did not receive a copy of the license and are unable to -* obtain it through the world-wide-web, please send an email -* to license@prestashop.com so we can send you a copy immediately. -* -* DISCLAIMER -* -* Do not edit or add to this file if you wish to upgrade PrestaShop to newer -* versions in the future. If you wish to customize PrestaShop for your -* needs please refer to http://www.prestashop.com for more information. -* -* @author PrestaShop SA -* @copyright 2007-2015 PrestaShop SA -* @version Release: $Revision: 16986 $ -* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) -* International Registered Trademark & Property of PrestaShop SA -*/ - function getNumeric(val) { - var reg=new RegExp("[0-9]+","g"); - var res = reg.exec(val); - if(isNaN(res)) - return 0; - else - return res; - } - - function checkToDisplayRelayList() - { - if (typeof PS_MRData != 'undefined') - { - //============================================================ - // auto display fancybox if radio already check - //============================================================ - PS_MRSelectedRelayPoint['relayPointNum'] = PS_MRData.pre_selected_relay; - // PS_VERSION < '1.5' - if (PS_MRData.PS_VERSION < '1.5') - { - // Bind id_carrierX to an ajax call - $.each(PS_MRData.carrier_list, function(i, carrier) { - PS_MRCarrierMethodList[carrier.id_carrier] = carrier.id_mr_method; - if ($('#id_carrier' + carrier.id_carrier).attr('checked')) { - PS_MRSelectedRelayPoint['carrier_id'] = carrier.id_carrier; - PS_MRDisplayWidget(carrier.id_carrier); - } - }); - - } - else if (PS_MRData.PS_VERSION >= '1.5' - && typeof PS_MRData.carrier != 'undefined' - ) - { // 1.5 way - var carrier_selected = $('input[class=delivery_option_radio]:checked').val(); - $.each(PS_MRData.carrier_list, function(i, carrier) { - PS_MRCarrierMethodList[carrier.id_carrier] = carrier.id_mr_method; - if (carrier.id_carrier+',' == carrier_selected || carrier.id_carrier == carrier_selected) { - overrideUpdateExtraCarrier(carrier_selected, id_address); - PS_MRSelectedRelayPoint['carrier_id'] = carrier.id_carrier; - PS_MRDisplayWidget(carrier.id_carrier); - } - }); - } - //============================================================ - // Handle input click of the other input to hide the previous relay point list displayed - $('input[name=id_carrier], input.delivery_option_radio').click(function(e){ - displayPickupPlace(0); - }); - } - //return false; - } - - function isMRCarrier(id_carrier){ - var carrier_list = PS_MRData.carrier_list; - for(i in carrier_list){ - var MR_carrier = carrier_list[i]; - if(MR_carrier.id_carrier == id_carrier) { - return MR_carrier; - } - } - return false; - } - - function hideRelaySelectedBox(_this){ - // Hide MR input if one of them is not selected - if (PS_MRCarrierMethodList[_this.val()] == undefined){ - // 1.5 way - var id = getNumeric(_this.val()); - if(PS_MRCarrierMethodList[id] == undefined) { - displayPickupPlace(0); - PS_MRSelectedRelayPoint['carrier_id'] = 0; - PS_MRDisplayWidget(0); - PS_MRSelectedRelayPoint['relayPointNum'] = 0; - } - else { - PS_MRSelectedRelayPoint['carrier_id'] = id; - PS_MRDisplayWidget(id); - } - } - else { - PS_MRSelectedRelayPoint['carrier_id'] = _this.val(); - PS_MRDisplayWidget(_this.val()); - } - return false; - } - - function PS_MRDisplayWidget(carrier_id) { - var dlv_mode = ''; - $.each(PS_MRData.carrier_list, function(i, carrier) { - if (carrier.id_carrier == carrier_id) - dlv_mode = carrier.dlv_mode; - PS_MRSelectedRelayPoint['relayPointNum'] = -1; - PS_MRAddSelectedCarrierInDB(carrier_id); - } - ); - if(carrier_id) { - if(dlv_mode!='LD1' && dlv_mode!='LDS' && dlv_mode!='HOM') { - loadMR_Map("#Zone_Widget", dlv_mode); - $("#link_zone_widget").click(); - if(PS_MRSelectedRelayPoint['relayPointNum'] == -1) - PS_MRSelectedRelayPoint['relayPointNum'] = 0; - } - else - displayPickupPlace(0); - } - return false; - } - - - function overrideUpdateExtraCarrier(id_delivery_option, id_address) - { - if(PS_MRData.PS_VERSION < '1.5') - return false; - - var url = ""; - var method = 'updateExtraCarrier'; - var params = ''; - if(typeof(orderOpcUrl) !== 'undefined') { - method = 'updateCarrierAndGetPayments'; - params += '&recyclable='+(($('#recyclable:checked').val() != undefined)? 1:0); - params += '&gift='+(($('#gift:checked').val() != undefined)? 1:0); - params += '&gift_message='+$('#gift_message').val(); - params += '&delivery_option['+id_address+']='+id_delivery_option; - url = orderOpcUrl; - } - else - url = orderUrl; - - $.ajax({ - type: 'POST', - headers: { "cache-control": "no-cache" }, - url: url + '?rand=' + new Date().getTime(), - async: false, - cache: false, - dataType : "json", - data: 'ajax=true' - +'&method='+method - +params - +'&id_address='+id_address - +'&id_delivery_option='+id_delivery_option - +'&token='+static_token - +'&allow_refresh=1', - success: function(jsonData) - { - //$('#HOOK_EXTRACARRIER_'+id_address).html(jsonData['content']); - return false; - } - }); - return false; - } - - function displayPickupPlace(info) { - var id = "relay_point_selected_box"; - - if(!info) { - $('#'+id).hide(); - $('#'+id).remove(); - return false; - } - - if (PS_MRData.PS_VERSION < '1.5') - { - var block_carrier = $('#carrierTable'); - } - else { - var block_carrier = $('.delivery_options_address'); - } - if($('#'+id).length !== 0) { - $('#'+id).html('

'+relay_point_selected_box_label+'

'+info); - $('#'+id).show(); - } - else { - $('

'+relay_point_selected_box_label+'

'+info+'
').insertAfter(block_carrier); - } - return false; - } - - function PS_MRAddSelectedCarrierInDB(id_carrier) - { - // Make the request - $.ajax({ - type: 'POST', - url: _PS_MR_MODULE_DIR_ + 'ajax.php', - data: {'method' : 'addSelectedCarrierToDB', - 'id_carrier' : id_carrier, - 'id_mr_method' : PS_MRCarrierMethodList[id_carrier], - 'mrtoken' : mrtoken}, - success: function(json) - { - return false; - } - }); - return false; - } - - $(document).ready(function() - { - // 1.5 OPC Validation - Warn user to select a relay point - $('.payment_module a').live('click', function() { - if (typeof PS_MRData != 'undefined') - { - if (PS_MRData.PS_VERSION >= '1.5' && PS_MRData.carrier && PS_MRSelectedRelayPoint['carrier_id']!=0) - { - var _return = !(!PS_MRSelectedRelayPoint['carrier_id'] || !PS_MRSelectedRelayPoint['relayPointNum']); - if (!_return) - alert(PS_MRTranslationList['errorSelection']); - return _return; - } - } - }); - - // If MR carrier selected, check MR relay point is selected too - $('input[name=processCarrier], button[name=processCarrier]').click(function(){ - var carrier_selected = $('input[class=delivery_option_radio]:checked').val(); - if (PS_MRSelectedRelayPoint['carrier_id']+',' == carrier_selected || PS_MRSelectedRelayPoint['carrier_id'] == carrier_selected) - { - var _return = !(PS_MRSelectedRelayPoint['carrier_id'] && !PS_MRSelectedRelayPoint['relayPointNum']); - if (!_return) - alert(PS_MRTranslationList['errorSelection']); - return _return; - } - }); - - if (typeof PS_MRData != 'undefined') - { - if (PS_MRData.PS_VERSION < '1.5') { - $('input[name="id_carrier"]').click(function(){ - checkToDisplayRelayList(); - }); - } - } - }); + * 2007-2015 PrestaShop + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License (AFL 3.0) + * that is bundled with this package in the file LICENSE.txt. + * It is also available through the world-wide-web at this URL: + * http://opensource.org/licenses/afl-3.0.php + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * DISCLAIMER + * + * Do not edit or add to this file if you wish to upgrade PrestaShop to newer + * versions in the future. If you wish to customize PrestaShop for your + * needs please refer to http://www.prestashop.com for more information. + * + * @author PrestaShop SA + * @copyright 2007-2015 PrestaShop SA + * @version Release: $Revision: 16986 $ + * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) + * International Registered Trademark & Property of PrestaShop SA + */ +function getNumeric(val) { + var reg = new RegExp("[0-9]+", "g"); + var res = reg.exec(val); + if (isNaN(res)) + return 0; + else + return res; +} + +function checkToDisplayRelayList() +{ + if (typeof PS_MRData != 'undefined') + { + //============================================================ + // auto display fancybox if radio already check + //============================================================ + PS_MRSelectedRelayPoint['relayPointNum'] = PS_MRData.pre_selected_relay; + // PS_VERSION < '1.5' + if (PS_MRData.PS_VERSION < '1.5') + { + // Bind id_carrierX to an ajax call + $.each(PS_MRData.carrier_list, function (i, carrier) { + PS_MRCarrierMethodList[carrier.id_carrier] = carrier.id_mr_method; + if ($('#id_carrier' + carrier.id_carrier).attr('checked')) { + PS_MRSelectedRelayPoint['carrier_id'] = carrier.id_carrier; + PS_MRDisplayWidget(carrier.id_carrier); + setProtectRelaySelected(); + } + }); + + } + else if (PS_MRData.PS_VERSION >= '1.5' + && typeof PS_MRData.carrier != 'undefined' + ) + { // 1.5 way + var carrier_selected = $('input[class=delivery_option_radio]:checked').val(); + $.each(PS_MRData.carrier_list, function (i, carrier) { + PS_MRCarrierMethodList[carrier.id_carrier] = carrier.id_mr_method; + if (carrier.id_carrier + ',' == carrier_selected || carrier.id_carrier == carrier_selected) { + overrideUpdateExtraCarrier(carrier_selected, id_address); + PS_MRSelectedRelayPoint['carrier_id'] = carrier.id_carrier; + PS_MRDisplayWidget(carrier.id_carrier); + } + }); + } + //============================================================ + // Handle input click of the other input to hide the previous relay point list displayed + $('input[name=id_carrier], input.delivery_option_radio').click(function (e) { + displayPickupPlace(0); + }); + } + //return false; +} + +function isMRCarrier(id_carrier) { + var carrier_list = PS_MRData.carrier_list; + for (i in carrier_list) { + var MR_carrier = carrier_list[i]; + if (MR_carrier.id_carrier == id_carrier) { + return MR_carrier; + } + } + return false; +} + +function hideRelaySelectedBox(_this) { + // Hide MR input if one of them is not selected + if (PS_MRCarrierMethodList[_this.val()] == undefined) { + // 1.5 way + var id = getNumeric(_this.val()); + if (PS_MRCarrierMethodList[id] == undefined) { + displayPickupPlace(0); + PS_MRSelectedRelayPoint['carrier_id'] = 0; + PS_MRDisplayWidget(0); + PS_MRSelectedRelayPoint['relayPointNum'] = 0; + } + else { + PS_MRSelectedRelayPoint['carrier_id'] = id; + PS_MRDisplayWidget(id); + } + } + else { + PS_MRSelectedRelayPoint['carrier_id'] = _this.val(); + PS_MRDisplayWidget(_this.val()); + } + return false; +} + +function PS_MRDisplayWidget(carrier_id) { + var dlv_mode = ''; + $.each(PS_MRData.carrier_list, function (i, carrier) { + if (carrier.id_carrier == carrier_id) + dlv_mode = carrier.dlv_mode; + PS_MRSelectedRelayPoint['relayPointNum'] = -1; + PS_MRAddSelectedCarrierInDB(carrier_id); + } + ); + if (carrier_id) { + if (dlv_mode != 'LD1' && dlv_mode != 'LDS' && dlv_mode != 'HOM') { + loadMR_Map("#Zone_Widget", dlv_mode); + $("#link_zone_widget").click(); + if (PS_MRSelectedRelayPoint['relayPointNum'] == -1) + PS_MRSelectedRelayPoint['relayPointNum'] = 0; + } + else + displayPickupPlace(0); + } + return false; +} + + +function overrideUpdateExtraCarrier(id_delivery_option, id_address) +{ + if (PS_MRData.PS_VERSION < '1.5') + return false; + + var url = ""; + var method = 'updateExtraCarrier'; + var params = ''; + if (typeof (orderOpcUrl) !== 'undefined') { + method = 'updateCarrierAndGetPayments'; + params += '&recyclable=' + (($('#recyclable:checked').val() != undefined) ? 1 : 0); + params += '&gift=' + (($('#gift:checked').val() != undefined) ? 1 : 0); + params += '&gift_message=' + $('#gift_message').val(); + params += '&delivery_option[' + id_address + ']=' + id_delivery_option; + url = orderOpcUrl; + } + else + url = orderUrl; + + $.ajax({ + type: 'POST', + headers: {"cache-control": "no-cache"}, + url: url + '?rand=' + new Date().getTime(), + async: false, + cache: false, + dataType: "json", + data: 'ajax=true' + + '&method=' + method + + params + + '&id_address=' + id_address + + '&id_delivery_option=' + id_delivery_option + + '&token=' + static_token + + '&allow_refresh=1', + success: function (jsonData) + { + //$('#HOOK_EXTRACARRIER_'+id_address).html(jsonData['content']); + setProtectRelaySelected(); + return false; + } + }); + return false; +} + +function displayPickupPlace(info) { + var id = "relay_point_selected_box"; + + if (!info) { + $('#' + id).hide(); + $('#' + id).remove(); + return false; + } + + if (PS_MRData.PS_VERSION < '1.5') + { + var block_carrier = $('#carrierTable'); + } + else { + var block_carrier = $('.delivery_options_address'); + } + if ($('#' + id).length !== 0) { + $('#' + id).html('

' + relay_point_selected_box_label + '

' + info); + $('#' + id).show(); + } + else { + $('

' + relay_point_selected_box_label + '

' + info + '
').insertAfter(block_carrier); + } + return false; +} + +function PS_MRAddSelectedCarrierInDB(id_carrier) +{ + // Make the request + $.ajax({ + type: 'POST', + url: _PS_MR_MODULE_DIR_ + 'ajax.php', + data: {'method': 'addSelectedCarrierToDB', + 'id_carrier': id_carrier, + 'id_mr_method': PS_MRCarrierMethodList[id_carrier], + 'mrtoken': mrtoken}, + success: function (json) + { + return false; + } + }); + return false; +} + +$(document).ready(function () +{ + + if (typeof PS_MRData != 'undefined') + { + if (PS_MRData.PS_VERSION < '1.5') { + $('input[name="id_carrier"]').click(function () { + checkToDisplayRelayList(); + }); + } + } +}); diff --git a/logo.gif b/logo.gif index e1e406a..06f8471 100644 Binary files a/logo.gif and b/logo.gif differ diff --git a/logo.png b/logo.png index 3129774..56a3ec7 100644 Binary files a/logo.png and b/logo.png differ diff --git a/mails/en/index.php b/mails/en/index.php index 4deda52..9cf60e3 100644 --- a/mails/en/index.php +++ b/mails/en/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/mails/fr/index.php b/mails/fr/index.php index 4deda52..9cf60e3 100644 --- a/mails/fr/index.php +++ b/mails/fr/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/mails/index.php b/mails/index.php index 4deda52..9cf60e3 100644 --- a/mails/index.php +++ b/mails/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/mondialrelay.php b/mondialrelay.php index c1e54ae..dc7552a 100644 --- a/mondialrelay.php +++ b/mondialrelay.php @@ -25,1225 +25,1234 @@ */ if (!defined('_PS_VERSION_')) - exit; + exit; require_once(_PS_MODULE_DIR_.'mondialrelay/classes/MRTools.php'); class MondialRelay extends Module { - const INSTALL_SQL_FILE = 'sql/mrInstall.sql'; - - private $_postErrors; - - public static $modulePath = ''; - public static $moduleURL = ''; - static public $MRFrontToken = ''; - static public $MRBackToken = ''; - - /* Added for 1.3 compatibility */ - const ONLY_PRODUCTS = 1; - const ONLY_DISCOUNTS = 2; - const BOTH = 3; - const BOTH_WITHOUT_SHIPPING = 4; - const ONLY_SHIPPING = 5; - const ONLY_WRAPPING = 6; - const ONLY_PRODUCTS_WITHOUT_SHIPPING = 7; - - /* SQL FILTER ORDER */ - const NO_FILTER = 0; - const WITHOUT_HOME_DELIVERY = 1; - - const MR_URL = 'http://www.mondialrelay.fr/'; - - /* Contains the details of the current shop used */ - public $account_shop = array( - 'MR_ENSEIGNE_WEBSERVICE' => '', - 'MR_CODE_MARQUE' => '', - 'MR_KEY_WEBSERVICE' => '', - 'MR_LANGUAGE' => '', - 'MR_WEIGHT_COEFFICIENT' => '', - 'MR_ORDER_STATE' => 3, - 'id_shop' => 1 - ); - - public $upgrade_detail = array(); - - public function __construct() - { - $this->name = 'mondialrelay'; - $this->tab = 'shipping_logistics'; - $this->version = '2.1.6'; - $this->installed_version = ''; - $this->module_key = '366584e511d311cfaa899fc2d9ec1bd0'; - $this->author = 'PrestaShop'; - - parent::__construct(); - - $this->displayName = $this->l('Mondial Relay'); - $this->description = $this->l('Deliver in Relay points'); - - /** Backward compatibility */ - require(_PS_MODULE_DIR_.'/mondialrelay/backward_compatibility/backward.php'); - - self::initModuleAccess(); - - /* Call everytime to prevent the change of the module by a recent one */ - $this->_updateProcess(); - $this->initAccount(); - - /* Check if it's a mondialrelay ajax query */ - $this->checkAJAX(); - } - - public function install() - { - if (!function_exists('curl_version') || !extension_loaded('soap')) - { - $this->_errors[] = $this->l('Mondial Relay needs SOAP & cURL to be installed on your server.'); - return false; - } - - if (!parent::install()) - return false; - - if (!$this->registerHookByVersion()) - return false; - - if ((!file_exists(MondialRelay::$modulePath.MondialRelay::INSTALL_SQL_FILE)) || - (!$sql = Tools::file_get_contents(MondialRelay::$modulePath.MondialRelay::INSTALL_SQL_FILE))) - return false; - - $sql = str_replace('PREFIX_', _DB_PREFIX_, $sql); - $sql = preg_split("/;\s*[\r\n]+/", $sql); - foreach ($sql as $query) - if (!empty($query)) - Db::getInstance()->execute(trim($query)); - - $result = Db::getInstance()->getRow(' - SELECT id_tab - FROM `'._DB_PREFIX_.'tab` - WHERE class_name="AdminMondialRelay"'); - - if (!$result) - { - $tab = new Tab(); - $languages = Language::getLanguages(false); - foreach ($languages as $language) - $tab->name[$language['id_lang']] = 'Mondial Relay'; - $tab->class_name = 'AdminMondialRelay'; - $tab->module = 'mondialrelay'; - $tab->id_parent = Tab::getIdFromClassName('AdminOrders'); - - if (!$tab->add()) - return false; - - if (is_dir(_PS_MODULE_DIR_.'mondialrelay/')) - @copy(_PS_MODULE_DIR_.'mondialrelay/img/AdminMondialRelay.gif', _PS_IMG_DIR_.'/AdminMondialRelay.gif'); - } - - /* If module isn't installed, set default value */ - if (!Configuration::get('MONDIAL_RELAY')) - { - Configuration::updateValue('MONDIAL_RELAY', $this->version); - Configuration::updateValue('MONDIAL_RELAY_SECURE_KEY', md5( _COOKIE_KEY_.time())); - Configuration::updateValue('MONDIAL_RELAY_MODE', 'widget'); - } - else - { - $query = 'UPDATE `'._DB_PREFIX_.'carrier` c, `'._DB_PREFIX_.'mr_method` m - SET c.`deleted` = 0 - WHERE c.`id_carrier` = m.`id_carrier` - AND m.`is_deleted` = 0'; - - /* Reactive transport if database wasn't remove at the last uninstall */ - Db::getInstance()->execute($query); - } - return true; - } - - /* - ** Return the token depend of the type - */ - public static function getToken($type = 'front') - { - return ($type == 'front') ? MondialRelay::$MRFrontToken : (($type == 'back') ? - MondialRelay::$MRBackToken : null); - } - - /* - ** Register hook depending of the Prestashop version used - */ - private function registerHookByVersion() - { - if (version_compare(_PS_VERSION_, '1.3', '>=') && (!$this->registerHook('extraCarrier') || !$this->registerHook('updateCarrier') || !$this->registerHook('newOrder') || !$this->registerHook('BackOfficeHeader') || !$this->registerHook('header'))) - return false; - if (version_compare(_PS_VERSION_, '1.4', '>=') && (!$this->registerHook('processCarrier') || !$this->registerHook('orderDetailDisplayed'))) - return false; - if (version_compare(_PS_VERSION_, '1.6', '>=') && (!$this->registerHook('displayBackOfficeHeader'))) - return false; - return true; - } - - public function uninstallCommonData() - { - /* Tab uninstall */ - if (($id_tab = Tab::getIdFromClassName('AdminMondialRelay'))) - { - $tab = new Tab($id_tab); - $tab->delete(); - } - - if (!Db::getInstance()->execute(' - UPDATE '._DB_PREFIX_.'carrier c, '._DB_PREFIX_.'mr_method m - SET c.`deleted` = 1 - WHERE c.`id_carrier` = m.`id_carrier`')) - return false; - - return true; - } - - public function uninstall() - { - if (!parent::uninstall()) - return false; - - /* Uninstall data that doesn't need to be keep */ - if (!$this->uninstallCommonData()) - return false; - - if (Tools::getValue('keepDatabase')) - return true; - - Configuration::deleteByName('MR_ACCOUNT_DETAIL'); - - /* Drop databases */ - if (!Db::getInstance()->execute(' - DROP TABLE - '._DB_PREFIX_.'mr_history, - '._DB_PREFIX_.'mr_method, - '._DB_PREFIX_.'mr_selected, - '._DB_PREFIX_.'mr_method_shop')) - { - /* If drop failed, try to turn off the carriers */ - Db::getInstance()->execute(' - UPDATE '._DB_PREFIX_.'carrier c, '._DB_PREFIX_.'mr_method m - SET c.`deleted` = 1 - WHERE c.`id_carrier` = m.`id_carrier`'); - return false; - } - return true; - } - - /** - * Launch upgrade process for 1.3 and 1.4 - */ - public function runUpgrade() - { - /* List of upgraded version existing */ - $files_version = array('1.8.0', '1.8.3', '2.0.6'); - - $upgrade_path = dirname(__FILE__).'/upgrade/'; - - if (version_compare(_PS_VERSION_, '1.5', '<')) - { - foreach ($files_version as $version) - { - $file = $upgrade_path.'install-'.$version.'.php'; - if ($this->installed_version < $version && file_exists($file)) - { - include_once($file); - call_user_func('upgrade_module_'.str_replace('.', '_', $version), $this); - } - } - } - } - - /* - ** UpdateProcess if merchant update the module without a - ** normal installation - */ - private function _updateProcess() - { - if (Module::isInstalled('mondialrelay') && - (($this->installed_version = Configuration::get('MONDIAL_RELAY')) || - $this->installed_version = Configuration::get('MONDIAL_RELAY_1_4')) - && $this->installed_version < $this->version) - $this->runUpgrade(); - } - - /* - ** Get the content to ask for a backup of the database - */ - private function askForBackup($href) - { - return 'return PS_MRObject.uninstall(\''.$href.'\');'; - } - - /* - ** OnClick for input fields under the module list fields action - */ - public function onclickOption($type, $href = false) - { - $content = ''; - - switch ($type) - { - case 'desactive': - break; - case 'reset': - break; - case 'delete': - break; - case 'uninstall': - $content = $this->askForBackup($href); - break; - default: - } - return $content; - } - - /** - * Init the account_shop variable with the account detail for this shop - */ - public function initAccount() - { - /* Get default value */ - $id_order_state = $this->account_shop['MR_ORDER_STATE']; - - if (($account_shop_stored = MondialRelay::getAccountDetail())) - $this->account_shop = $account_shop_stored; - - /* Normally, this can't happen... */ - if (empty($this->account_shop['MR_ORDER_STATE'])) - $this->account_shop['MR_ORDER_STATE'] = $id_order_state; - } - - /* - ** Init the access directory module for URL and file system - ** Allow a compatibility for Presta < 1.4 - */ - public static function initModuleAccess() - { - MondialRelay::$modulePath = _PS_MODULE_DIR_.'mondialrelay/'; - MondialRelay::$MRFrontToken = sha1('mr'._COOKIE_KEY_.'Front'); - MondialRelay::$MRBackToken = sha1('mr'._COOKIE_KEY_.'Back'); - MondialRelay::$moduleURL = __PS_BASE_URI__.'modules/mondialrelay/'; - } - - public function fetchTemplate($path, $name) - { - if (version_compare(_PS_VERSION_, '1.4', '<')) - $this->context->smarty->currentTemplate = $name; - - return $this->context->smarty->fetch(dirname(__FILE__).$path.$name.'.tpl'); - } - - public function hookNewOrder($params) - { - if (!MondialRelay::isMondialRelayCarrier($params['cart']->id_carrier)) - return; - - $order = $params['order']; - $currency = $params['currency']; - $id_lang = (int)$order->id_lang; - $customer = new Customer($order->id_customer); - $carrier = new Carrier($order->id_carrier); - $invoice = new Address((int)($order->id_address_invoice)); - $invoice_state = $invoice->id_state ? new State($invoice->id_state) : false; - if (version_compare(_PS_VERSION_, '1.5', '<')) - $order_date_text = Tools::displayDate($order->date_add, (int)($id_lang)); - else - $order_date_text = Tools::displayDate($order->date_add, null); - - DB::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'mr_selected` - SET `id_order` = '.(int)$order->id.' - WHERE `id_cart` = '.(int)$params['cart']->id); - - /*============================================ - // GET MR INFOS - //============================================*/ - $sql = 'SELECT * FROM `'._DB_PREFIX_.'mr_selected` WHERE `id_cart` = '.(int)$params['cart']->id.' AND `id_order` = '.(int)$order->id.''; - $mr_point = DB::getInstance()->getRow($sql); - - if (!$mr_point['MR_Selected_Num']) return false; - - /*============================================ - // SEND MAIL - //============================================*/ - $template = 'mr_new_order'; - $subject = $this->l('New order', false, (int)$id_lang).' - '.sprintf('%06d', $order->id); - - $mr_address = ''; - if ($mr_point['MR_Selected_LgAdr2']) - $mr_address .= $mr_point['MR_Selected_LgAdr2'].''; - if ($mr_point['MR_Selected_LgAdr3']) - $mr_address .= $mr_point['MR_Selected_LgAdr3'].''; - if ($mr_point['MR_Selected_LgAdr4']) - $mr_address .= $mr_point['MR_Selected_LgAdr4'].''; - - $templateVars = array( - '{firstname}' => $customer->firstname, - '{lastname}' => $customer->lastname, - '{email}' => $customer->email, - '{delivery_company}' => $mr_point['MR_Selected_LgAdr1'], - '{delivery_address1}' => $mr_address, - '{delivery_city}' => $mr_point['MR_Selected_Ville'], - '{delivery_postal_code}' => $mr_point['MR_Selected_CP'], - '{delivery_country}' => $mr_point['MR_Selected_Pays'], - '{invoice_company}' => $invoice->company, - '{invoice_firstname}' => $invoice->firstname, - '{invoice_lastname}' => $invoice->lastname, - '{invoice_address2}' => $invoice->address2, - '{invoice_address1}' => $invoice->address1, - '{invoice_city}' => $invoice->city, - '{invoice_postal_code}' => $invoice->postcode, - '{invoice_country}' => $invoice->country, - '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', - '{invoice_phone}' => $invoice->phone, - '{invoice_other}' => $invoice->other, - '{order_name}' => sprintf('%06d', $order->id), - '{shop_name}' => Configuration::get('PS_SHOP_NAME'), - '{date}' => $order_date_text, - '{carrier}' => (($carrier->name == '0') ? Configuration::get('PS_SHOP_NAME') : $carrier->name), - '{payment}' => Tools::substr($order->payment, 0, 32), - '{currency}' => $currency->sign, - ); - - $iso = Language::getIsoById((int)$id_lang); - if (file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.txt') && file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.html')) - Mail::Send((int)$id_lang, $template, $subject, $templateVars, $customer->email, $customer->firstname.' '.$customer->lastname, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), null, null, dirname(__FILE__).'/mails/'); - } - - public function hookBackOfficeHeader() - { - if (Tools::getValue('tab') == 'AdminMondialRelay' || Tools::getValue('module_name') == 'mondialrelay' || Tools::strtolower(Tools::getValue('controller')) == 'adminmondialrelay' || Tools::getValue('configure') == 'mondialrelay') - { - $this->context->smarty->assign(array( - 'MR_token' => MondialRelay::$MRBackToken, 'MR_local_path' => MondialRelay::$modulePath, - 'MR_account_set' => MondialRelay::isAccountSet(), 'new_base_dir' => MondialRelay::$moduleURL)); - - return $this->fetchTemplate('/views/templates/admin/', 'bo-header'); - } - } - - public function hookOrderDetailDisplayed($params) - { - if (!Mondialrelay::isMondialRelayCarrier($params['order']->id_carrier)) - return; - - $sql = ' - SELECT s.`MR_Selected_LgAdr1`, s.`MR_Selected_LgAdr2`, s.`MR_Selected_LgAdr3`, s.`MR_Selected_LgAdr4`, - s.`MR_Selected_CP`, s.`MR_Selected_Ville`, s.`MR_Selected_Pays`, s.`MR_Selected_Num`, s.`url_suivi`, s.`exp_number`, m.dlv_mode - FROM `'._DB_PREFIX_.'mr_selected` s - INNER JOIN '._DB_PREFIX_.'mr_method m ON m.id_mr_method = s.id_method - WHERE s.`id_cart` = '.(int)$params['order']->id_cart; - $res = Db::getInstance()->getRow($sql); - - if (!$res) - return; - - $point_relais = $res['MR_Selected_LgAdr1']. - ($res['MR_Selected_LgAdr1'] ? '
' : '').$res['MR_Selected_LgAdr2']. - ($res['MR_Selected_LgAdr2'] ? '
' : '').$res['MR_Selected_LgAdr3']. - ($res['MR_Selected_LgAdr3'] ? '
' : '').$res['MR_Selected_LgAdr4']. - ($res['MR_Selected_LgAdr4'] ? '
' : '').$res['MR_Selected_CP'].' '. - $res['MR_Selected_Ville'].'
'.$res['MR_Selected_Pays']; - - - if (($res['dlv_mode'] == 'LD1') || ($res['dlv_mode'] == 'LDS') || ($res['dlv_mode'] == 'HOM')) - $this->context->smarty->assign( - array( - 'mr_url' => $res['url_suivi'] - )); - else - $this->context->smarty->assign( - array( - 'mr_addr' => $point_relais, - 'mr_url' => $res['url_suivi'] - )); - - return $this->fetchTemplate('/views/templates/front/', 'order_detail'); - } - - /* - ** Update the carrier id to use the new one if changed - */ - public function hookupdateCarrier($params) - { - if ($params['id_carrier'] != $params['carrier']->id) - { - /* Get the old id_mr_method */ - $id_mr_method = Db::getInstance()->getValue(' - SELECT id_mr_method FROM `'._DB_PREFIX_.'mr_method` - WHERE id_carrier='.(int)$params['id_carrier']); - - /* Insert new entry keeping the last one linked to the id_carrier */ - $query = ' - INSERT INTO `'._DB_PREFIX_.'mr_method` - (name, country_list, col_mode, dlv_mode, insurance, id_carrier) - ( - SELECT - "'.pSQL($params['carrier']->name).'", - country_list, - col_mode, - dlv_mode, - insurance, - '.(int)$params['carrier']->id.' - FROM `'._DB_PREFIX_.'mr_method` - WHERE id_carrier ='.(int)$params['id_carrier'].')'; - Db::getInstance()->execute($query); - - /* Do the same process for the multishop table */ - $query = ' - INSERT INTO `'._DB_PREFIX_.'mr_method_shop` - (id_mr_method, id_shop) - ( - SELECT - '.(int)Db::getInstance()->INSERT_ID().', - id_shop - FROM `'._DB_PREFIX_.'mr_method_shop` - WHERE id_mr_method ='.(int)$id_mr_method.')'; - Db::getInstance()->execute($query); - - /* Set the last mr_method to delete */ - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'mr_method` SET `is_deleted` = 1 - WHERE `id_mr_method` = '.(int)$id_mr_method); - } - } - - /** - * Get a carrier list liable to the module - * - * @return array - */ - public function _getCarriers() - { - /* Query don't use the external_module_name to keep the */ - /* 1.3 compatibility */ - $query = ' - SELECT c.id_carrier, c.range_behavior, m.id_mr_method, - m.dlv_mode, cl.delay - FROM `'._DB_PREFIX_.'mr_method` m - LEFT JOIN `'._DB_PREFIX_.'carrier` c - ON c.`id_carrier` = m.`id_carrier` - LEFT JOIN `'._DB_PREFIX_.'carrier_lang` cl - ON c.`id_carrier` = cl.`id_carrier` - LEFT JOIN `'._DB_PREFIX_.'mr_method_shop` ms - ON m.`id_mr_method` = ms.`id_mr_method` - WHERE c.`deleted` = 0 - AND ms.`id_shop` = '.$this->account_shop['id_shop'].' - AND cl.id_lang = '.$this->context->language->id.' - AND c.`active` = 1 - AND m.`is_deleted` = 0'; - - $carriers = Db::getInstance()->executeS($query); - - if (!is_array($carriers)) - $carriers = array(); - return $carriers; - } - - /** - * Get a specific method entry detail by a defined id_carrier - * - * @static - * @param $id_carrier - * @return array - */ - public static function getMethodByIdCarrier($id_carrier) - { - return Db::getInstance()->getRow(' - SELECT * FROM `'._DB_PREFIX_.'mr_method` m - WHERE m.`id_carrier` = '.(int)$id_carrier); - } - - /* - ** Added to be used properly with OPC - */ - public function hookHeader($params) - { - if (version_compare(_PS_VERSION_, '1.5', '>=')) - $this->context->controller->addJquery(); - - /*Configuration::updateValue('MONDIAL_RELAY_MODE', 'widget');*/ - if (!($file = basename(Tools::getValue('controller')))) - $file = str_replace('.php', '', basename($_SERVER['SCRIPT_NAME'])); - - if (in_array($file, array('order-opc', 'order', 'orderopc'))) - { - $this->context->smarty->assign(array( - 'one_page_checkout' => (Configuration::get('PS_ORDER_PROCESS_TYPE') ? Configuration::get('PS_ORDER_PROCESS_TYPE') : 0), - 'new_base_dir' => MondialRelay::$moduleURL, - 'MR_local_path' => MondialRelay::$modulePath, - 'MRToken' => MondialRelay::$MRFrontToken, - 'MR_MONDIAL_RELAY_MODE' => Configuration::get('MONDIAL_RELAY_MODE'), - ) - ); - - if($this->canAddJSViaController()) - $this->context->controller->addJS('https://maps.google.com/maps/api/js?sensor=false'); - else - $this->context->smarty->assign(array( - 'addJsInTemplate' => true - )); - - if (Configuration::get('MONDIAL_RELAY_MODE') == 'widget') - return $this->fetchTemplate('/views/templates/front/', 'header_widget'); - else - return $this->fetchTemplate('/views/templates/front/', 'header'); - } - return ''; - } - - private function canAddJSViaController() - { - if(version_compare(_PS_VERSION_, '1.6', '>=')) - return true; - - if(Configuration::get('PS_JS_THEME_CACHE')) - return false; - - return true; - } - - public function hookExtraCarrier($params) - { - /* TODO : Makes it work with multi-shipping */ - if (!MondialRelay::isAccountSet()) - return ''; - - $id_carrier = false; - $preSelectedRelay = $this->getRelayPointSelected($this->context->cart->id); - $carriersList = MondialRelay::_getCarriers(); - $address = new Address($this->context->cart->id_address_delivery); - $country = new Country($address->id_country); - $id_zone = Address::getZoneById((int)($address->id)); - - /* Check if the defined carrier are ok */ - foreach ($carriersList as $k => $row) - { - /* For now works only with single shipping (>= 1.5 compatibility) */ - if (method_exists($this->context->cart, 'carrierIsSelected')) - { - if ($this->context->cart->carrierIsSelected($row['id_carrier'], $params['address']->id)) - $id_carrier = $row['id_carrier']; - } - - /* Temporary carrier for some test */ - $carrier = new Carrier((int)($row['id_carrier'])); - if ((($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) && $carrier->getMaxDeliveryPriceByWeight($id_zone) === false) || (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE) && $carrier->getMaxDeliveryPriceByPrice($id_zone) === false)) - unset($carriersList[$k]); - else if ($row['range_behavior']) - { - /* Get id zone */ - $id_zone = (isset($this->context->cart->id_address_delivery) && $this->context->cart->id_address_delivery) ? - Address::getZoneById((int)$this->context->cart->id_address_delivery) : - (int)$this->context->country->id_zone; - - if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && (!Carrier::checkDeliveryPriceByWeight($row['id_carrier'], $this->context->cart->getTotalWeight(), $id_zone))) || - ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && - (!Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->context->cart->getOrderTotal(true, MondialRelay::BOTH_WITHOUT_SHIPPING), $id_zone, $this->context->cart->id_currency) || - !Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->context->cart->getOrderTotal(true, MondialRelay::BOTH_WITHOUT_SHIPPING), $id_zone, $this->context->cart->id_currency)))) - unset($carriersList[$k]); - } - } - - $carrier = null; - if ($id_carrier && ($method = MondialRelay::getMethodByIdCarrier($id_carrier))) - { - $carrier = new Carrier((int)$id_carrier); - /* Add dynamically a new field */ - $carrier->id_mr_method = $method['id_mr_method']; - $carrier->mr_dlv_mode = $method['dlv_mode']; - } - - if (Configuration::get('PS_SSL_ENABLED') || (!empty($_SERVER['HTTPS']) && Tools::strtolower($_SERVER['HTTPS']) != 'off')) - $ssl = 'true'; - else $ssl = 'false'; - - $this->context->smarty->assign(array( - 'address' => $address, - 'account_shop' => $this->account_shop, - 'country' => $country, - 'ssl' => $ssl, - 'MR_Data'=> MRTools::jsonEncode(array( - 'carrier_list' => $carriersList, - 'carrier' => $carrier, - 'PS_VERSION' => _PS_VERSION_, - 'pre_selected_relay' => isset($preSelectedRelay['MR_selected_num']) ? $preSelectedRelay['MR_selected_num'] : -1, - )) - )); - if (Configuration::get('MONDIAL_RELAY_MODE') == 'widget') - return $this->fetchTemplate('/views/templates/front/', 'checkout_process_widget'); - else - return $this->fetchTemplate('/views/templates/front/', 'checkout_process'); - } - - /** - * Return the detailed account - * - * @static - * @return mixed - */ - public static function getAccountDetail() - { - return unserialize((string)Configuration::get('MR_ACCOUNT_DETAIL')); - } - - /** - * Check if the account is set - * - * @static - * @return bool - */ - public static function isAccountSet() - { - $details = MondialRelay::getAccountDetail(); - - if (!$details || !count($details)) - return false; - - foreach ($details as $value) - if (empty($value)) - return false; - - return true; - } - - /** - * Check any submitted form - */ - private function _postValidation() - { - if (Tools::isSubmit('submit_account_detail')) - { - if (Tools::getValue('MR_enseigne_webservice') == '' || !preg_match('#^[0-9A-Z]{2}[0-9A-Z ]{6}$#', Tools::getValue('MR_enseigne_webservice'))) - $this->_postErrors[] = $this->l('Invalid Enseigne'); - if (Tools::getValue('MR_code_marque') == '' || !preg_match('#^[0-9]{2}$#', Tools::getValue('MR_code_marque'))) - $this->_postErrors[] = $this->l('Invalid Mark code'); - if (Tools::getValue('MR_webservice_key') == '' || !preg_match("#^[0-9A-Za-z_\'., /\-]{2,32}$#", Tools::getValue('MR_webservice_key'))) - $this->_postErrors[] = $this->l('Invalid Webservice Key'); - if (Tools::getValue('MR_language') == '' || !preg_match('#^[A-Z]{2}$#', Tools::getValue('MR_language'))) - $this->_postErrors[] = $this->l('Invalid Language'); - if (!Tools::getValue('MR_weight_coefficient') || !Validate::isInt(Tools::getValue('MR_weight_coefficient'))) - $this->_postErrors[] = $this->l('Invalid Weight Coefficient'); - } - - /* Shipping form validation */ - else if (Tools::isSubmit('submitMethod')) - { - if (!preg_match("#^[0-9A-Za-z_\'., /\-]{2,32}$#", Tools::getValue('mr_Name'))) - $this->_postErrors[] = $this->l('Invalid carrier name'); - if (Tools::getValue('mr_ModeCol') != 'CCC') - $this->_postErrors[] = $this->l('Invalid Col mode'); - if (!preg_match('#^REL|24R|ESP|DRI|LDS|LDR|LD1|HOM$#', Tools::getValue('mr_ModeLiv'))) - $this->_postErrors[] = $this->l('Invalid delivery mode'); - if (!Validate::isInt(Tools::getValue('mr_ModeAss')) || Tools::getValue('mr_ModeAss') > 5 || Tools::getValue('mr_ModeAss') < 0) - $this->_postErrors[] = $this->l('Invalid Assurance mode'); - if (!Tools::getValue('mr_Pays_list')) - $this->_postErrors[] = $this->l('You must choose at least one delivery country.'); - } - - /* Order state form validation */ - else if (Tools::isSubmit('submit_order_state')) - { - if (!Validate::isUnsignedInt(Tools::getValue('id_order_state'))) - $this->_postErrors[] = $this->l('Invalid order state'); - } - - - } - - /** - * Update account shop - * - * @return bool - */ - public function updateAccountShop() - { - return Configuration::updateValue('MR_ACCOUNT_DETAIL', serialize($this->account_shop)); - } - - /** - * Post process - * - * @return array - */ - private function _postProcess() - { - $post_action = array( - 'type' =>Tools::safeOutput(Tools::getValue('MR_tab_name')), - 'message_success' => $this->l('Action Succeed'), - 'had_errors' => false - ); - - if (Tools::isSubmit('submitAdvancedSettings')) - Configuration::updateValue('MONDIAL_RELAY_MODE', Tools::getValue('mode', 'widget')); - else if (Tools::isSubmit('submit_account_detail')) - { - $this->account_shop = array( - 'MR_ENSEIGNE_WEBSERVICE' => Tools::safeOutput(Tools::getValue('MR_enseigne_webservice')), - 'MR_CODE_MARQUE' => Tools::safeOutput(Tools::getValue('MR_code_marque')), - 'MR_KEY_WEBSERVICE' =>Tools::safeOutput(Tools::getValue('MR_webservice_key')), - 'MR_LANGUAGE' => Tools::safeOutput(Tools::getValue('MR_language')), - 'MR_ORDER_STATE' => Tools::safeOutput($this->account_shop['MR_ORDER_STATE']), - 'MR_WEIGHT_COEFFICIENT' => Tools::safeOutput(Tools::getValue('MR_weight_coefficient')), - 'id_shop' => $this->context->shop->id - ); - if ($this->updateAccountShop()) - $post_action['message_success'] = $this->l('Account detail has been updated'); - else - $this->_postErrors[] = $this->l('Cannot Update the account shop'); - } - else if (Tools::isSubmit('submit_add_shipping')) - { - if (($this->addShippingMethod())) - $post_action['message_success'] = $this->l('Shipping method has been added'); - } - else if (Tools::isSubmit('submit_order_state')) - { - Configuration::updateValue('MONDIAL_RELAY_ORDER_STATE', Tools::getValue('id_order_state')); - $post_action['message_success'] = $this->l('Order state properly changed'); - } - else if (($id_mr_method = Tools::getValue('delete_mr')) && $this->disableCarrier((int)$id_mr_method)) - $post_action['message_success'] = $this->l('Carrier has been deleted (disabled under the prestashop database)'); - - if (count($this->_postErrors)) - $post_action['had_errors'] = true; - - return $post_action; - } - - public function getContent() - { - $post_action = null; - if (!empty($_POST)) - { - $this->_postValidation(); - if (!count($this->_postErrors)) - $post_action = $this->_postProcess(); - } - $carriers_list = Db::getInstance()->executeS(' - SELECT m.* - FROM `'._DB_PREFIX_.'mr_method` m - LEFT JOIN `'._DB_PREFIX_.'carrier` c - ON (c.`id_carrier` = m.`id_carrier`) - LEFT JOIN `'._DB_PREFIX_.'mr_method_shop` ms - ON ms.`id_mr_method` = m.`id_mr_method` - WHERE c.`deleted` = 0 AND ms.`id_shop` = '.(int)$this->account_shop['id_shop']); - - $controller = version_compare(_PS_VERSION_, '1.5', '<') ? 'AdminContact' : 'AdminStores'; - - /* TODO : Fill an array with admi controller name */ - $this->context->smarty->assign(array( - 'MR_token_admin_performance' => Tools::getAdminToken('AdminPerformance'.(int)(Tab::getIdFromClassName('AdminPerformance')).(int)($this->context->cookie->id_employee)), - 'MR_token_admin_carriers' => (version_compare(_PS_VERSION_, '1.6', '<') ? Tools::getAdminToken('AdminCarriers'.(int)(Tab::getIdFromClassName('AdminCarriers')).(int)$this->context->employee->id) : Tools::getAdminToken('AdminCarrierWizard'.(int)(Tab::getIdFromClassName('AdminCarrierWizard')).(int)$this->context->employee->id)), - 'MR_token_admin_contact' => array( - 'controller_name' => $controller, - 'token' => Tools::getAdminToken($controller.(int)(Tab::getIdFromClassName($controller)).(int)$this->context->employee->id)), - 'MR_token_admin_mondialrelay' => Tools::getAdminToken('AdminMondialRelay'.(int)(Tab::getIdFromClassName('AdminMondialRelay')).(int)$this->context->employee->id), - 'MR_token_admin_module' => Tools::getAdminToken('AdminModules'.(int)(Tab::getIdFromClassName('AdminModules')).(int)$this->context->employee->id), - 'MR_enseigne_webservice' => Tools::getValue('MR_enseigne_webservice') ? Tools::getValue('MR_enseigne_webservice') : $this->account_shop['MR_ENSEIGNE_WEBSERVICE'], - 'MR_code_marque' => Tools::getValue('MR_code_marque') ? Tools::getValue('MR_code_marque') : $this->account_shop['MR_CODE_MARQUE'], - 'MR_webservice_key' => Tools::getValue('MR_webservice_key') ? Tools::getValue('MR_webservice_key') : $this->account_shop['MR_KEY_WEBSERVICE'], - 'MR_available_languages' => Language::getLanguages(), - 'MR_selected_language' => $this->account_shop['MR_LANGUAGE'], - 'MR_weight_coefficient' => Tools::getValue('MR_weight_coefficient') ? Tools::getValue('MR_weight_coefficient') : $this->account_shop['MR_WEIGHT_COEFFICIENT'], - 'MR_PS_WEIGHT_UNIT' => Configuration::get('PS_WEIGHT_UNIT'), - 'MR_order_states_list' => OrderState::getOrderStates($this->context->language->id), - 'MR_MONDIAL_RELAY_ORDER_STATE' => Configuration::get('MONDIAL_RELAY_ORDER_STATE'), - 'MR_CRON_URL' => Tools::getHttpHost(true, true)._MODULE_DIR_.$this->name.'/cron.php?secure_key='.Configuration::get('MONDIAL_RELAY_SECURE_KEY'), - 'MR_name' => Tools::getValue('MR_name') ? Tools::getValue('MR_name') : '', - 'MR_carriers_list' => $carriers_list, - 'MR_error_list' => $this->_postErrors, - 'MR_form_action' => $post_action, - 'MR_PS_ADMIN_IMG_' => _PS_ADMIN_IMG_, - 'MR_tab_selected' => Tools::getValue('MR_tab_name') ? Tools::getValue('MR_tab_name') : (MondialRelay::isAccountSet() ? 'account_form' : 'info_form'), - 'MR_delay' => Tools::getValue('MR_delay') ? Tools::getValue('MR_delay') : '', - 'MR_local_path' => MondialRelay::$modulePath, - 'MR_upgrade_detail' => $this->upgrade_detail, - 'MR_unit_weight_used' => Configuration::get('PS_WEIGHT_UNIT'), - 'MR_account_set' => MondialRelay::isAccountSet(), - 'MR_base_dir' => MondialRelay::$moduleURL, - 'MR_MONDIAL_RELAY_MODE' => Configuration::get('MONDIAL_RELAY_MODE'), - ) - ); - return $this->fetchTemplate('/views/templates/admin/', 'configuration'); - } - - /** - * Add new carrier - * - * @param $name - * @param $delay - * @return bool|int - */ - private function addCarrier($name, $delay) - { - $ret = false; - - if (($carrier = new Carrier())) - { - $delay_lang = array(); - foreach (Language::getLanguages(false) as $lang) - $delay_lang[$lang['id_lang']] = $delay; - $carrier->name = $name; - $carrier->active = 1; - $carrier->range_behavior = 1; - $carrier->need_range = 1; - $carrier->external_module_name = 'mondialrelay'; - $carrier->shipping_method = 1; - $carrier->delay = $delay_lang; - $carrier->is_module = version_compare(_PS_VERSION_, '1.4', '<') ? 0 : 1; - - $ret = $carrier->add(); - } - return $ret ? $carrier->id : false; - } - - public function displayInfoByCart($id_cart) - { - $html = '

'; - $simpleresul = Db::getInstance()->executeS(' - SELECT * FROM '._DB_PREFIX_.'mr_selected - WHERE id_cart='.(int)($id_cart)); - - if (isset($simpleresul[0])) - { - if (trim($simpleresul[0]['exp_number']) != '') - $html .= $this->l('Nb expedition:').$simpleresul[0]['exp_number'].'
'; - if (trim($simpleresul[0]['url_etiquette']) != '') - $html .= ''.$this->l('Label URL').'
'; - if (trim($simpleresul[0]['url_suivi']) != '') - $html .= ''.$this->l('Follow-up URL').'
'; - if (trim($simpleresul[0]['MR_Selected_Num']) != '') - $html .= $this->l('Nb Point Relay :').$simpleresul[0]['MR_Selected_Num'].'
'; - if (trim($simpleresul[0]['MR_Selected_LgAdr1']) != '') - $html .= $simpleresul[0]['MR_Selected_LgAdr1'].'
'; - if (trim($simpleresul[0]['MR_Selected_LgAdr2']) != '') - $html .= $simpleresul[0]['MR_Selected_LgAdr2'].'
'; - if (trim($simpleresul[0]['MR_Selected_LgAdr3']) != '') - $html .= $simpleresul[0]['MR_Selected_LgAdr3'].'
'; - if (trim($simpleresul[0]['MR_Selected_LgAdr4']) != '') - $html .= $simpleresul[0]['MR_Selected_LgAdr4'].'
'; - if (trim($simpleresul[0]['MR_Selected_CP']) != '') - $html .= $simpleresul[0]['MR_Selected_CP'].' '; - if (trim($simpleresul[0]['MR_Selected_Ville']) != '') - $html .= $simpleresul[0]['MR_Selected_Ville'].'
'; - if (trim($simpleresul[0]['MR_Selected_Pays']) != '') - $html .= $simpleresul[0]['MR_Selected_Pays'].'
'; - } - $html .= '

'; - return $html; - } - - /** - * Set necessaries values to the created carrier - * - * @param $id_carrier - * @param $dlv_mode - * @return bool - */ - private function addDefaultCarrierValue($id_carrier, $dlv_mode) - { - $weight_coef = $this->account_shop['MR_WEIGHT_COEFFICIENT']; - - /* Default Range value depending of the delivery mode */ - $range_weight = array( - '24R' => array(0, 30000 / $weight_coef), - 'DRI' => array(30000 / $weight_coef, 130000 / $weight_coef), - 'LD1' => array(0, 60000 / $weight_coef), - 'HOM' => array(0, 60000 / $weight_coef), - 'LDS' => array(30000 / $weight_coef, 130000 / $weight_coef) - ); - - /* Set range weight for a dlv_mode */ - if (!Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'range_weight` - (`id_carrier`, `delimiter1`, `delimiter2`) - VALUES ('.(int)$id_carrier.', '.$range_weight[$dlv_mode][0].', '.$range_weight[$dlv_mode][1].')')) - { - $this->_postErrors[] = $this->l('Range weight can\'t be added'); - return false; - } - - $range_weight_id = Db::getInstance()->Insert_ID(); - - /* Set a range price */ - if (!Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'range_price` - (`id_carrier`, `delimiter1`, `delimiter2`) - VALUES ('.(int)$id_carrier.', 0.000000, 10000.000000)')) - { - $this->_postErrors[] = $this->l('Range price can\'t be added'); - return false; - } - - $range_price_id = Db::getInstance()->Insert_ID(); - - $groups = Group::getGroups(Configuration::get('PS_LANG_DEFAULT')); - foreach ($groups as $group) - if (!Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'carrier_group` - (id_carrier, id_group) - VALUES('.(int)$id_carrier.', '.(int)($group['id_group']).')')) - { - $this->_postErrors[] = $this->l('Default zone can\'t be added'); - return false; - } - - /* Set default zone */ - $zones = Zone::getZones(); - foreach ($zones as $zone) - { - if (!Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'carrier_zone` - (id_carrier, id_zone) - VALUES('.(int)$id_carrier.', '.(int)($zone['id_zone']).')') || - !Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'delivery` - (id_carrier, id_range_price, id_range_weight, id_zone, price) - VALUES('.(int)$id_carrier.', '.(int)($range_price_id).', NULL,'.(int)($zone['id_zone']).', 0.00)') || - !Db::getInstance()->execute( - 'INSERT INTO `'._DB_PREFIX_.'delivery` - (id_carrier, id_range_price, id_range_weight, id_zone, price) - VALUES('.(int)$id_carrier.', NULL, '.(int)($range_weight_id).','.(int)($zone['id_zone']).', 0.00)')) - { - $this->_postErrors[] = $this->l('Carrier zone or delivery data can\'t be added'); - return false; - } - } - return true; - } - - /** - * Add new shipping method - * - * @return bool - */ - private function addShippingMethod() - { - /* Insert new carrier for under Prestashop */ - if (!($id_carrier = $this->addCarrier(Tools::getValue('MR_name'), Tools::getValue('MR_delay')))) - { - $this->_postErrors[] = $this->l('Carrier can\'t be created in PrestaShop'); - return false; - } - - $fields = $_POST; - - unset($fields['submit_add_shipping'], $fields['MR_tab_name'], $fields['tab'], $fields['MR_delay']); - - /* Force col mod to CCC */ - $fields['col_mode'] = 'CCC'; - $fields['id_carrier'] = $id_carrier; - - $query = 'INSERT INTO `'._DB_PREFIX_.'mr_method` (%s) VALUES(%s)'; - - $keys = array(); - $values = array(); - foreach ($fields as $key => $value) - { - $keys[] = '`'.str_replace('MR_', '', $key).'`'; - $values[] = '\''.(is_array($value) ? pSQL(implode(',', $value)) : pSQL($value)).'\''; - } - $query = sprintf($query, implode(',', $keys), implode(',', $values)); - - if (!Db::getInstance()->execute($query) || !Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'mr_method_shop` - (id_mr_method, id_shop) VALUES('.(int)Db::getInstance()->INSERT_ID().', '.(int)$this->account_shop['id_shop'].')')) - { - $this->l('Carrier method can\'t be added for the module'); - Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'carrier` WHERE id_carrier='.(int)$id_carrier); - return false; - } - return $this->addDefaultCarrierValue($id_carrier, $fields['MR_dlv_mode']); - } - - /** - * Disable carrier instead of delete to keep history - * - * @param $id_mr_method - */ - public function disableCarrier($id_mr_method) - { - if (($id_carrier = Db::getInstance()->getValue( - 'SELECT `id_carrier` - FROM `'._DB_PREFIX_.'mr_method` - WHERE `id_mr_method` = '.(int)($id_mr_method))) && - Db::getInstance()->execute( - 'UPDATE `'._DB_PREFIX_.'carrier` c, `'._DB_PREFIX_.'mr_method` m - SET c.`active` = 0, c.`deleted` = 1, m.`is_deleted` = 1 - WHERE - c.`id_carrier` = m.`id_carrier` - AND c.`id_carrier` = '.(int)$id_carrier)) - return true; - - $this->_postErrors = $this->l('Carrier can\'t be deleted yet'); - return false; - } - - /** - * Get the followup url - * - * @param $exp_number - * @return mixed - */ - public function get_followup($exp_number) - { - $query = ' - SELECT url_suivi - FROM '._DB_PREFIX_.'mr_selected - WHERE exp_number='.(int)$exp_number; - - return Db::getInstance()->getValue($query); - } - - /** - * Get the SQL query to fetch order with mr carrier - * - * @static - * @param $id_order_state - * @param $weight_coefficient - * @return string - */ - public static function getBaseOrdersSQLQuery($id_order_state, $weight_coefficient = 0) - { - return 'SELECT o.`id_address_delivery` as id_address_delivery, - o.`id_order` as id_order, - o.`id_customer` as id_customer, - o.`id_cart` as id_cart, - o.`id_lang` as id_lang, - mrs.`id_mr_selected` as id_mr_selected, - CONCAT(c.`firstname`, " ", c.`lastname`) AS `customer`, - o.`total_paid_real` as total, o.`total_shipping` as shipping, - o.`date_add` as date, o.`id_currency` as id_currency, o.`id_lang` as id_lang, - mrs.`MR_poids` as mr_weight, - mrs.`MR_insurance` as mr_insurance, - mr.`name` as mr_Name, mrs.`MR_Selected_Num` as MR_Selected_Num, - mrs.`MR_Selected_Pays` as MR_Selected_Pays, mrs.`exp_number` as exp_number, - mr.`col_mode` as mr_ModeCol, mr.`dlv_mode` as mr_ModeLiv, mr.`insurance` as mr_ModeAss, - ROUND(SUM(odt.`product_weight` * odt.`product_quantity`) * '.(int)$weight_coefficient.') AS "order_weight" - FROM - `'._DB_PREFIX_.'orders` o - LEFT JOIN - `'._DB_PREFIX_.'carrier` ca - ON (ca.`id_carrier` = o.`id_carrier`) - LEFT JOIN - `'._DB_PREFIX_.'mr_selected` mrs - ON (mrs.`id_cart` = o.`id_cart`) - LEFT JOIN - `'._DB_PREFIX_.'mr_method` mr - ON (mr.`id_carrier` = ca.`id_carrier`) - LEFT JOIN - `'._DB_PREFIX_.'customer` c - ON (c.`id_customer` = o.`id_customer`) - LEFT JOIN - `'._DB_PREFIX_.'order_detail` odt - ON odt.`id_order` = o.`id_order` - WHERE ( - SELECT moh.`id_order_state` - FROM `'._DB_PREFIX_.'order_history` moh - WHERE moh.`id_order` = o.`id_order` - ORDER BY moh.`date_add` DESC LIMIT 1) = '.(int)($id_order_state).' - AND o.`id_order` = mrs.`id_order`'; - } - - /** - * Get orders details to create Tickets - * - * @static - * @param array $orderIdList - * @param int $filterEntries - * @param int $weight_coefficient - * @return array - */ - public static function getOrders($orderIdList = array(), $filterEntries = MondialRelay::NO_FILTER, $weight_coefficient = 0) - { - $account_shop = MondialRelay::getAccountDetail(); - $id_order_state = $account_shop['MR_ORDER_STATE']; - $sql = MondialRelay::getBaseOrdersSQLQuery($id_order_state, $weight_coefficient); - - if (count($orderIdList)) - { - $sql .= ' AND o.id_order IN ('; - foreach ($orderIdList as $id_order) - $sql .= (int)$id_order.', '; - $sql = rtrim($sql, ', ').')'; - } - switch ($filterEntries) - { - case MondialRelay::WITHOUT_HOME_DELIVERY: - $sql .= 'AND mr.mr_ModeLiv != "LD1" AND mr.mr_ModeLiv != "LDS" AND mr.mr_ModeLiv != "HOM"'; - break; - } - $sql .= ' - GROUP BY o.`id_order` - ORDER BY o.`date_add` ASC'; - return Db::getInstance()->executeS($sql); - } - - /** - * Get Mondialrelay error code - * - * @param $code - * @return string - */ - public function getErrorCodeDetail($code) - { - include(dirname(__FILE__).'/errorCode.php'); - if (isset($statCode[$code])) - return $statCode[$code]; - return $this->l('This error isn\'t referred : ').$code; - } - - /** - * @param $id_cart - * @return mixed - */ - public function getRelayPointSelected($id_cart) - { - return Db::getInstance()->getRow(' - SELECT s.`MR_selected_num` - FROM `'._DB_PREFIX_.'mr_selected` s - WHERE s.`id_cart` = '.(int)$id_cart); - } - - /** - * @param $id_carrier - * @return mixed - */ - public function isMondialRelayCarrier($id_carrier) - { - return Db::getInstance()->getRow(' - SELECT m.`id_mr_method` - FROM `'._DB_PREFIX_.'mr_method` m - WHERE `id_carrier` = '.(int)$id_carrier); - } - - public function getMethodValueByIdCarier($id_carrier, $key) - { - $content = Db::getInstance()->executeS( - 'SELECT * - FROM `'._DB_PREFIX_.'mr_method` m - WHERE `id_carrier` = '.(int)$id_carrier); - - return isset($content[$key]) ? $content[$key] : ''; - } - - public function checkAJAX() - { - /* Avoid loop inclusion */ - static $is_included = false; - - /* TODO : Find a way to use it on front */ - if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && Tools::strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') - if (Tools::getValue('mrtoken') && !$is_included) - { - $is_included = true; - include(dirname(__FILE__).'/ajax.php'); - exit(0); - } - } + const INSTALL_SQL_FILE = 'sql/mrInstall.sql'; + + private $_postErrors; + + public static $modulePath = ''; + public static $moduleURL = ''; + static public $MRFrontToken = ''; + static public $MRBackToken = ''; + + /* Added for 1.3 compatibility */ + const ONLY_PRODUCTS = 1; + const ONLY_DISCOUNTS = 2; + const BOTH = 3; + const BOTH_WITHOUT_SHIPPING = 4; + const ONLY_SHIPPING = 5; + const ONLY_WRAPPING = 6; + const ONLY_PRODUCTS_WITHOUT_SHIPPING = 7; + + /* SQL FILTER ORDER */ + const NO_FILTER = 0; + const WITHOUT_HOME_DELIVERY = 1; + + const MR_URL = 'http://www.mondialrelay.fr/'; + + /* Contains the details of the current shop used */ + public $account_shop = array( + 'MR_ENSEIGNE_WEBSERVICE' => '', + 'MR_CODE_MARQUE' => '', + 'MR_KEY_WEBSERVICE' => '', + 'MR_LANGUAGE' => '', + 'MR_WEIGHT_COEFFICIENT' => '', + 'MR_ORDER_STATE' => 3, + 'id_shop' => 1 + ); + + public $upgrade_detail = array(); + + public function __construct() + { + $this->name = 'mondialrelay'; + $this->tab = 'shipping_logistics'; + $this->version = '2.1.7'; + $this->installed_version = ''; + $this->module_key = '366584e511d311cfaa899fc2d9ec1bd0'; + $this->author = 'PrestaShop'; + + parent::__construct(); + + $this->displayName = $this->l('Mondial Relay'); + $this->description = $this->l('Deliver in Relay points'); + + /** Backward compatibility */ + require(_PS_MODULE_DIR_.'/mondialrelay/backward_compatibility/backward.php'); + + self::initModuleAccess(); + + /* Call everytime to prevent the change of the module by a recent one */ + $this->_updateProcess(); + $this->initAccount(); + + /* Check if it's a mondialrelay ajax query */ + $this->checkAJAX(); + } + + public function install() + { + if (!function_exists('curl_version') || !extension_loaded('soap')) + { + $this->_errors[] = $this->l('Mondial Relay needs SOAP & cURL to be installed on your server.'); + return false; + } + + if (!parent::install()) + return false; + + if (!$this->registerHookByVersion()) + return false; + + if ((!file_exists(MondialRelay::$modulePath.MondialRelay::INSTALL_SQL_FILE)) || + (!$sql = Tools::file_get_contents(MondialRelay::$modulePath.MondialRelay::INSTALL_SQL_FILE))) + return false; + + $sql = str_replace('PREFIX_', _DB_PREFIX_, $sql); + $sql = preg_split("/;\s*[\r\n]+/", $sql); + foreach ($sql as $query) + if (!empty($query)) + Db::getInstance()->execute(trim($query)); + + $result = Db::getInstance()->getRow(' + SELECT id_tab + FROM `'._DB_PREFIX_.'tab` + WHERE class_name="AdminMondialRelay"'); + + if (!$result) + { + $tab = new Tab(); + $languages = Language::getLanguages(false); + foreach ($languages as $language) + $tab->name[$language['id_lang']] = 'Mondial Relay'; + $tab->class_name = 'AdminMondialRelay'; + $tab->module = 'mondialrelay'; + $tab->id_parent = Tab::getIdFromClassName('AdminOrders'); + + if (!$tab->add()) + return false; + + if (is_dir(_PS_MODULE_DIR_.'mondialrelay/')) + @copy(_PS_MODULE_DIR_.'mondialrelay/img/AdminMondialRelay.gif', _PS_IMG_DIR_.'/AdminMondialRelay.gif'); + } + + /* If module isn't installed, set default value */ + if (!Configuration::get('MONDIAL_RELAY')) + { + Configuration::updateValue('MONDIAL_RELAY', $this->version); + Configuration::updateValue('MONDIAL_RELAY_SECURE_KEY', md5( _COOKIE_KEY_.time())); + Configuration::updateValue('MONDIAL_RELAY_MODE', 'widget'); + } + else + { + $query = 'UPDATE `'._DB_PREFIX_.'carrier` c, `'._DB_PREFIX_.'mr_method` m + SET c.`deleted` = 0 + WHERE c.`id_carrier` = m.`id_carrier` + AND m.`is_deleted` = 0'; + + /* Reactive transport if database wasn't remove at the last uninstall */ + Db::getInstance()->execute($query); + } + return true; + } + + /* + ** Return the token depend of the type + */ + public static function getToken($type = 'front') + { + return ($type == 'front') ? MondialRelay::$MRFrontToken : (($type == 'back') ? + MondialRelay::$MRBackToken : null); + } + + /* + ** Register hook depending of the Prestashop version used + */ + private function registerHookByVersion() + { + if (version_compare(_PS_VERSION_, '1.3', '>=') && (!$this->registerHook('extraCarrier') || !$this->registerHook('updateCarrier') || !$this->registerHook('newOrder') || !$this->registerHook('BackOfficeHeader') || !$this->registerHook('header'))) + return false; + if (version_compare(_PS_VERSION_, '1.4', '>=') && (!$this->registerHook('processCarrier') || !$this->registerHook('orderDetailDisplayed'))) + return false; + if (version_compare(_PS_VERSION_, '1.6', '>=') && (!$this->registerHook('displayBackOfficeHeader'))) + return false; + return true; + } + + public function uninstallCommonData() + { + /* Tab uninstall */ + if (($id_tab = Tab::getIdFromClassName('AdminMondialRelay'))) + { + $tab = new Tab($id_tab); + $tab->delete(); + } + + if (!Db::getInstance()->execute(' + UPDATE '._DB_PREFIX_.'carrier c, '._DB_PREFIX_.'mr_method m + SET c.`deleted` = 1 + WHERE c.`id_carrier` = m.`id_carrier`')) + return false; + + return true; + } + + public function uninstall() + { + if (!parent::uninstall()) + return false; + + /* Uninstall data that doesn't need to be keep */ + if (!$this->uninstallCommonData()) + return false; + + if (Tools::getValue('keepDatabase')) + return true; + + Configuration::deleteByName('MR_ACCOUNT_DETAIL'); + + /* Drop databases */ + if (!Db::getInstance()->execute(' + DROP TABLE + '._DB_PREFIX_.'mr_history, + '._DB_PREFIX_.'mr_method, + '._DB_PREFIX_.'mr_selected, + '._DB_PREFIX_.'mr_method_shop')) + { + /* If drop failed, try to turn off the carriers */ + Db::getInstance()->execute(' + UPDATE '._DB_PREFIX_.'carrier c, '._DB_PREFIX_.'mr_method m + SET c.`deleted` = 1 + WHERE c.`id_carrier` = m.`id_carrier`'); + return false; + } + return true; + } + + /** + * Launch upgrade process for 1.3 and 1.4 + */ + public function runUpgrade() + { + /* List of upgraded version existing */ + $files_version = array('1.8.0', '1.8.3', '2.0.6'); + + $upgrade_path = dirname(__FILE__).'/upgrade/'; + + if (version_compare(_PS_VERSION_, '1.5', '<')) + { + foreach ($files_version as $version) + { + $file = $upgrade_path.'install-'.$version.'.php'; + if ($this->installed_version < $version && file_exists($file)) + { + include_once($file); + call_user_func('upgrade_module_'.str_replace('.', '_', $version), $this); + } + } + } + } + + /* + ** UpdateProcess if merchant update the module without a + ** normal installation + */ + private function _updateProcess() + { + if (Module::isInstalled('mondialrelay') && + (($this->installed_version = Configuration::get('MONDIAL_RELAY')) || + $this->installed_version = Configuration::get('MONDIAL_RELAY_1_4')) + && $this->installed_version < $this->version) + $this->runUpgrade(); + } + + /* + ** Get the content to ask for a backup of the database + */ + private function askForBackup($href) + { + return 'return PS_MRObject.uninstall(\''.$href.'\');'; + } + + /* + ** OnClick for input fields under the module list fields action + */ + public function onclickOption($type, $href = false) + { + $content = ''; + + switch ($type) + { + case 'desactive': + break; + case 'reset': + break; + case 'delete': + break; + case 'uninstall': + $content = $this->askForBackup($href); + break; + default: + } + return $content; + } + + /** + * Init the account_shop variable with the account detail for this shop + */ + public function initAccount() + { + /* Get default value */ + $id_order_state = $this->account_shop['MR_ORDER_STATE']; + + if (($account_shop_stored = MondialRelay::getAccountDetail())) + $this->account_shop = $account_shop_stored; + + /* Normally, this can't happen... */ + if (empty($this->account_shop['MR_ORDER_STATE'])) + $this->account_shop['MR_ORDER_STATE'] = $id_order_state; + } + + /* + ** Init the access directory module for URL and file system + ** Allow a compatibility for Presta < 1.4 + */ + public static function initModuleAccess() + { + MondialRelay::$modulePath = _PS_MODULE_DIR_.'mondialrelay/'; + MondialRelay::$MRFrontToken = sha1('mr'._COOKIE_KEY_.'Front'); + MondialRelay::$MRBackToken = sha1('mr'._COOKIE_KEY_.'Back'); + MondialRelay::$moduleURL = __PS_BASE_URI__.'modules/mondialrelay/'; + } + + public function fetchTemplate($path, $name) + { + if (version_compare(_PS_VERSION_, '1.4', '<')) + $this->context->smarty->currentTemplate = $name; + + return $this->context->smarty->fetch(dirname(__FILE__).$path.$name.'.tpl'); + } + + public function hookNewOrder($params) + { + if (!MondialRelay::isMondialRelayCarrier($params['cart']->id_carrier)) + return; + + $order = $params['order']; + $currency = $params['currency']; + $id_lang = (int)$order->id_lang; + $customer = new Customer($order->id_customer); + $carrier = new Carrier($order->id_carrier); + $invoice = new Address((int)($order->id_address_invoice)); + $invoice_state = $invoice->id_state ? new State($invoice->id_state) : false; + if (version_compare(_PS_VERSION_, '1.5', '<')) + $order_date_text = Tools::displayDate($order->date_add, (int)($id_lang)); + else + $order_date_text = Tools::displayDate($order->date_add, null); + + DB::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'mr_selected` + SET `id_order` = '.(int)$order->id.' + WHERE `id_cart` = '.(int)$params['cart']->id); + + /*============================================ + // GET MR INFOS + //============================================*/ + $sql = 'SELECT * FROM `'._DB_PREFIX_.'mr_selected` WHERE `id_cart` = '.(int)$params['cart']->id.' AND `id_order` = '.(int)$order->id.''; + $mr_point = DB::getInstance()->getRow($sql); + + if (!$mr_point['MR_Selected_Num']) return false; + + /*============================================ + // SEND MAIL + //============================================*/ + $template = 'mr_new_order'; + $subject = $this->l('New order', false, (int)$id_lang).' - '.sprintf('%06d', $order->id); + + $mr_address = ''; + if ($mr_point['MR_Selected_LgAdr2']) + $mr_address .= $mr_point['MR_Selected_LgAdr2'].''; + if ($mr_point['MR_Selected_LgAdr3']) + $mr_address .= $mr_point['MR_Selected_LgAdr3'].''; + if ($mr_point['MR_Selected_LgAdr4']) + $mr_address .= $mr_point['MR_Selected_LgAdr4'].''; + + $templateVars = array( + '{firstname}' => $customer->firstname, + '{lastname}' => $customer->lastname, + '{email}' => $customer->email, + '{delivery_company}' => $mr_point['MR_Selected_LgAdr1'], + '{delivery_address1}' => $mr_address, + '{delivery_city}' => $mr_point['MR_Selected_Ville'], + '{delivery_postal_code}' => $mr_point['MR_Selected_CP'], + '{delivery_country}' => $mr_point['MR_Selected_Pays'], + '{invoice_company}' => $invoice->company, + '{invoice_firstname}' => $invoice->firstname, + '{invoice_lastname}' => $invoice->lastname, + '{invoice_address2}' => $invoice->address2, + '{invoice_address1}' => $invoice->address1, + '{invoice_city}' => $invoice->city, + '{invoice_postal_code}' => $invoice->postcode, + '{invoice_country}' => $invoice->country, + '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', + '{invoice_phone}' => $invoice->phone, + '{invoice_other}' => $invoice->other, + '{order_name}' => sprintf('%06d', $order->id), + '{shop_name}' => Configuration::get('PS_SHOP_NAME'), + '{date}' => $order_date_text, + '{carrier}' => (($carrier->name == '0') ? Configuration::get('PS_SHOP_NAME') : $carrier->name), + '{payment}' => Tools::substr($order->payment, 0, 32), + '{currency}' => $currency->sign, + ); + + $iso = Language::getIsoById((int)$id_lang); + if (file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.txt') && file_exists(dirname(__FILE__).'/mails/'.$iso.'/'.$template.'.html')) + Mail::Send((int)$id_lang, $template, $subject, $templateVars, $customer->email, $customer->firstname.' '.$customer->lastname, Configuration::get('PS_SHOP_EMAIL'), Configuration::get('PS_SHOP_NAME'), null, null, dirname(__FILE__).'/mails/'); + } + + public function hookBackOfficeHeader() + { + if (Tools::getValue('tab') == 'AdminMondialRelay' || Tools::getValue('module_name') == 'mondialrelay' || Tools::strtolower(Tools::getValue('controller')) == 'adminmondialrelay' || Tools::getValue('configure') == 'mondialrelay') + { + $this->context->smarty->assign(array( + 'MR_token' => MondialRelay::$MRBackToken, 'MR_local_path' => MondialRelay::$modulePath, + 'MR_account_set' => MondialRelay::isAccountSet(), 'new_base_dir' => MondialRelay::$moduleURL)); + + return $this->fetchTemplate('/views/templates/admin/', 'bo-header'); + } + } + + public function hookOrderDetailDisplayed($params) + { + if (!Mondialrelay::isMondialRelayCarrier($params['order']->id_carrier)) + return; + + $sql = ' + SELECT s.`MR_Selected_LgAdr1`, s.`MR_Selected_LgAdr2`, s.`MR_Selected_LgAdr3`, s.`MR_Selected_LgAdr4`, + s.`MR_Selected_CP`, s.`MR_Selected_Ville`, s.`MR_Selected_Pays`, s.`MR_Selected_Num`, s.`url_suivi`, s.`exp_number`, m.dlv_mode + FROM `'._DB_PREFIX_.'mr_selected` s + INNER JOIN '._DB_PREFIX_.'mr_method m ON m.id_mr_method = s.id_method + WHERE s.`id_cart` = '.(int)$params['order']->id_cart; + $res = Db::getInstance()->getRow($sql); + + if (!$res) + return; + + $point_relais = $res['MR_Selected_LgAdr1']. + ($res['MR_Selected_LgAdr1'] ? '
' : '').$res['MR_Selected_LgAdr2']. + ($res['MR_Selected_LgAdr2'] ? '
' : '').$res['MR_Selected_LgAdr3']. + ($res['MR_Selected_LgAdr3'] ? '
' : '').$res['MR_Selected_LgAdr4']. + ($res['MR_Selected_LgAdr4'] ? '
' : '').$res['MR_Selected_CP'].' '. + $res['MR_Selected_Ville'].'
'.$res['MR_Selected_Pays']; + + + if (($res['dlv_mode'] == 'LD1') || ($res['dlv_mode'] == 'LDS') || ($res['dlv_mode'] == 'HOM')) + $this->context->smarty->assign( + array( + 'mr_url' => $res['url_suivi'] + )); + else + $this->context->smarty->assign( + array( + 'mr_addr' => $point_relais, + 'mr_url' => $res['url_suivi'] + )); + + return $this->fetchTemplate('/views/templates/front/', 'order_detail'); + } + + /* + ** Update the carrier id to use the new one if changed + */ + public function hookupdateCarrier($params) + { + if ($params['id_carrier'] != $params['carrier']->id) + { + /* Get the old id_mr_method */ + $id_mr_method = Db::getInstance()->getValue(' + SELECT id_mr_method FROM `'._DB_PREFIX_.'mr_method` + WHERE id_carrier='.(int)$params['id_carrier']); + + /* Insert new entry keeping the last one linked to the id_carrier */ + $query = ' + INSERT INTO `'._DB_PREFIX_.'mr_method` + (name, country_list, col_mode, dlv_mode, insurance, id_carrier, is_deleted) + ( + SELECT + "'.pSQL($params['carrier']->name).'", + country_list, + col_mode, + dlv_mode, + insurance, + '.(int)$params['carrier']->id.', + 0 + FROM `'._DB_PREFIX_.'mr_method` + WHERE id_carrier ='.(int)$params['id_carrier'].')'; + Db::getInstance()->execute($query); + + /* Do the same process for the multishop table */ + $query = ' + INSERT INTO `'._DB_PREFIX_.'mr_method_shop` + (id_mr_method, id_shop) + ( + SELECT + '.(int)Db::getInstance()->INSERT_ID().', + id_shop + FROM `'._DB_PREFIX_.'mr_method_shop` + WHERE id_mr_method ='.(int)$id_mr_method.')'; + Db::getInstance()->execute($query); + + /* Set the last mr_method to delete */ + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'mr_method` SET `is_deleted` = 1 + WHERE `id_mr_method` = '.(int)$id_mr_method); + } + } + + /** + * Get a carrier list liable to the module + * + * @return array + */ + public function _getCarriers() + { + /* Query don't use the external_module_name to keep the */ + /* 1.3 compatibility */ + $query = ' + SELECT c.id_carrier, c.range_behavior, m.id_mr_method, + m.dlv_mode, cl.delay + FROM `'._DB_PREFIX_.'mr_method` m + LEFT JOIN `'._DB_PREFIX_.'carrier` c + ON c.`id_carrier` = m.`id_carrier` + LEFT JOIN `'._DB_PREFIX_.'carrier_lang` cl + ON c.`id_carrier` = cl.`id_carrier` + LEFT JOIN `'._DB_PREFIX_.'mr_method_shop` ms + ON m.`id_mr_method` = ms.`id_mr_method` + WHERE c.`deleted` = 0 + AND ms.`id_shop` = '.$this->account_shop['id_shop'].' + AND cl.id_lang = '.$this->context->language->id.' + AND c.`active` = 1 + AND m.`is_deleted` = 0'; + + $carriers = Db::getInstance()->executeS($query); + + if (!is_array($carriers)) + $carriers = array(); + return $carriers; + } + + /** + * Get a specific method entry detail by a defined id_carrier + * + * @static + * @param $id_carrier + * @return array + */ + public static function getMethodByIdCarrier($id_carrier) + { + return Db::getInstance()->getRow(' + SELECT * FROM `'._DB_PREFIX_.'mr_method` m + WHERE m.`id_carrier` = '.(int)$id_carrier); + } + + /* + ** Added to be used properly with OPC + */ + public function hookHeader($params) + { + if (version_compare(_PS_VERSION_, '1.5', '>=')) + $this->context->controller->addJquery(); + + /*Configuration::updateValue('MONDIAL_RELAY_MODE', 'widget');*/ + if (!($file = basename(Tools::getValue('controller')))) + $file = str_replace('.php', '', basename($_SERVER['SCRIPT_NAME'])); + + if (in_array($file, array('order-opc', 'order', 'orderopc'))) + { + $this->context->smarty->assign(array( + 'one_page_checkout' => (Configuration::get('PS_ORDER_PROCESS_TYPE') ? Configuration::get('PS_ORDER_PROCESS_TYPE') : 0), + 'new_base_dir' => MondialRelay::$moduleURL, + 'MR_local_path' => MondialRelay::$modulePath, + 'MRToken' => MondialRelay::$MRFrontToken, + 'MR_MONDIAL_RELAY_MODE' => Configuration::get('MONDIAL_RELAY_MODE'), + ) + ); + + if($this->canAddJSViaController()) + $this->context->controller->addJS('https://maps.google.com/maps/api/js?sensor=false'); + else + $this->context->smarty->assign(array( + 'addJsInTemplate' => true + )); + + if (Configuration::get('MONDIAL_RELAY_MODE') == 'widget') + return $this->fetchTemplate('/views/templates/front/', 'header_widget'); + else + return $this->fetchTemplate('/views/templates/front/', 'header'); + } + return ''; + } + + private function canAddJSViaController() + { + if(version_compare(_PS_VERSION_, '1.5', '<')) + return false; + + if(Configuration::get('PS_JS_THEME_CACHE')) + return false; + + return true; + } + + public function hookExtraCarrier($params) + { + /* TODO : Makes it work with multi-shipping */ + if (!MondialRelay::isAccountSet()) + return ''; + + $id_carrier = false; + $preSelectedRelay = $this->getRelayPointSelected($this->context->cart->id); + $carriersList = MondialRelay::_getCarriers(); + $address = new Address($this->context->cart->id_address_delivery); + $country = new Country($address->id_country); + $id_zone = Address::getZoneById((int)($address->id)); + + /* Check if the defined carrier are ok */ + foreach ($carriersList as $k => $row) + { + /* For now works only with single shipping (>= 1.5 compatibility) */ + if (method_exists($this->context->cart, 'carrierIsSelected')) + { + if ($this->context->cart->carrierIsSelected($row['id_carrier'], $params['address']->id)) + $id_carrier = $row['id_carrier']; + } + + /* Temporary carrier for some test */ + $carrier = new Carrier((int)($row['id_carrier'])); + if ((($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) && $carrier->getMaxDeliveryPriceByWeight($id_zone) === false) || (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE) && $carrier->getMaxDeliveryPriceByPrice($id_zone) === false)) + unset($carriersList[$k]); + else if ($row['range_behavior']) + { + /* Get id zone */ + $id_zone = (isset($this->context->cart->id_address_delivery) && $this->context->cart->id_address_delivery) ? + Address::getZoneById((int)$this->context->cart->id_address_delivery) : + (int)$this->context->country->id_zone; + + if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && (!Carrier::checkDeliveryPriceByWeight($row['id_carrier'], $this->context->cart->getTotalWeight(), $id_zone))) || + ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && + (!Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->context->cart->getOrderTotal(true, MondialRelay::BOTH_WITHOUT_SHIPPING), $id_zone, $this->context->cart->id_currency) || + !Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $this->context->cart->getOrderTotal(true, MondialRelay::BOTH_WITHOUT_SHIPPING), $id_zone, $this->context->cart->id_currency)))) + unset($carriersList[$k]); + } + } + + $carrier = null; + if ($id_carrier && ($method = MondialRelay::getMethodByIdCarrier($id_carrier))) + { + $carrier = new Carrier((int)$id_carrier); + /* Add dynamically a new field */ + $carrier->id_mr_method = $method['id_mr_method']; + $carrier->mr_dlv_mode = $method['dlv_mode']; + } + + if (Configuration::get('PS_SSL_ENABLED') || (!empty($_SERVER['HTTPS']) && Tools::strtolower($_SERVER['HTTPS']) != 'off')) + $ssl = 'true'; + else $ssl = 'false'; + + $this->context->smarty->assign(array( + 'address' => $address, + 'account_shop' => $this->account_shop, + 'country' => $country, + 'ssl' => $ssl, + 'MR_Data'=> MRTools::jsonEncode(array( + 'carrier_list' => $carriersList, + 'carrier' => $carrier, + 'PS_VERSION' => _PS_VERSION_, + 'pre_selected_relay' => isset($preSelectedRelay['MR_selected_num']) ? $preSelectedRelay['MR_selected_num'] : -1, + )) + )); + if (Configuration::get('MONDIAL_RELAY_MODE') == 'widget') + return $this->fetchTemplate('/views/templates/front/', 'checkout_process_widget'); + else + return $this->fetchTemplate('/views/templates/front/', 'checkout_process'); + } + + /** + * Return the detailed account + * + * @static + * @return mixed + */ + public static function getAccountDetail() + { + return unserialize((string)Configuration::get('MR_ACCOUNT_DETAIL')); + } + + /** + * Check if the account is set + * + * @static + * @return bool + */ + public static function isAccountSet() + { + $details = MondialRelay::getAccountDetail(); + + if (!$details || !count($details)) + return false; + + foreach ($details as $value) + if (empty($value)) + return false; + + return true; + } + + /** + * Check any submitted form + */ + private function _postValidation() + { + if (Tools::isSubmit('submit_account_detail')) + { + if (Tools::getValue('MR_enseigne_webservice') == '' || !preg_match('#^[0-9A-Z]{2}[0-9A-Z ]{6}$#', Tools::getValue('MR_enseigne_webservice'))) + $this->_postErrors[] = $this->l('Invalid Enseigne'); + if (Tools::getValue('MR_code_marque') == '' || !preg_match('#^[0-9]{2}$#', Tools::getValue('MR_code_marque'))) + $this->_postErrors[] = $this->l('Invalid Mark code'); + if (Tools::getValue('MR_webservice_key') == '' || !preg_match("#^[0-9A-Za-z_\'., /\-]{2,32}$#", Tools::getValue('MR_webservice_key'))) + $this->_postErrors[] = $this->l('Invalid Webservice Key'); + if (Tools::getValue('MR_language') == '' || !preg_match('#^[A-Z]{2}$#', Tools::getValue('MR_language'))) + $this->_postErrors[] = $this->l('Invalid Language'); + if (!Tools::getValue('MR_weight_coefficient') || !Validate::isInt(Tools::getValue('MR_weight_coefficient'))) + $this->_postErrors[] = $this->l('Invalid Weight Coefficient'); + } + + /* Shipping form validation */ + else if (Tools::isSubmit('submitMethod')) + { + if (!preg_match("#^[0-9A-Za-z_\'., /\-]{2,32}$#", Tools::getValue('mr_Name'))) + $this->_postErrors[] = $this->l('Invalid carrier name'); + if (Tools::getValue('mr_ModeCol') != 'CCC') + $this->_postErrors[] = $this->l('Invalid Col mode'); + if (!preg_match('#^REL|24R|ESP|DRI|LDS|LDR|LD1|HOM$#', Tools::getValue('mr_ModeLiv'))) + $this->_postErrors[] = $this->l('Invalid delivery mode'); + if (!Validate::isInt(Tools::getValue('mr_ModeAss')) || Tools::getValue('mr_ModeAss') > 5 || Tools::getValue('mr_ModeAss') < 0) + $this->_postErrors[] = $this->l('Invalid Assurance mode'); + if (!Tools::getValue('mr_Pays_list')) + $this->_postErrors[] = $this->l('You must choose at least one delivery country.'); + } + + /* Order state form validation */ + else if (Tools::isSubmit('submit_order_state')) + { + if (!Validate::isUnsignedInt(Tools::getValue('id_order_state'))) + $this->_postErrors[] = $this->l('Invalid order state'); + } + + + } + + /** + * Update account shop + * + * @return bool + */ + public function updateAccountShop() + { + return Configuration::updateValue('MR_ACCOUNT_DETAIL', serialize($this->account_shop)); + } + + /** + * Post process + * + * @return array + */ + private function _postProcess() + { + $post_action = array( + 'type' =>Tools::safeOutput(Tools::getValue('MR_tab_name')), + 'message_success' => $this->l('Action Succeed'), + 'had_errors' => false + ); + + if (Tools::isSubmit('submitAdvancedSettings')) + Configuration::updateValue('MONDIAL_RELAY_MODE', Tools::getValue('mode', 'widget')); + else if (Tools::isSubmit('submit_account_detail')) + { + $this->account_shop = array( + 'MR_ENSEIGNE_WEBSERVICE' => Tools::safeOutput(Tools::getValue('MR_enseigne_webservice')), + 'MR_CODE_MARQUE' => Tools::safeOutput(Tools::getValue('MR_code_marque')), + 'MR_KEY_WEBSERVICE' =>Tools::safeOutput(Tools::getValue('MR_webservice_key')), + 'MR_LANGUAGE' => Tools::safeOutput(Tools::getValue('MR_language')), + 'MR_ORDER_STATE' => Tools::safeOutput($this->account_shop['MR_ORDER_STATE']), + 'MR_WEIGHT_COEFFICIENT' => Tools::safeOutput(Tools::getValue('MR_weight_coefficient')), + 'id_shop' => $this->context->shop->id + ); + if ($this->updateAccountShop()) + $post_action['message_success'] = $this->l('Account detail has been updated'); + else + $this->_postErrors[] = $this->l('Cannot Update the account shop'); + } + else if (Tools::isSubmit('submit_add_shipping')) + { + if (($this->addShippingMethod())) + $post_action['message_success'] = $this->l('Shipping method has been added'); + } + else if (Tools::isSubmit('submit_order_state')) + { + Configuration::updateValue('MONDIAL_RELAY_ORDER_STATE', Tools::getValue('id_order_state')); + $post_action['message_success'] = $this->l('Order state properly changed'); + } + else if (($id_mr_method = Tools::getValue('delete_mr')) && $this->disableCarrier((int)$id_mr_method)) + $post_action['message_success'] = $this->l('Carrier has been deleted (disabled under the prestashop database)'); + + if (count($this->_postErrors)) + $post_action['had_errors'] = true; + + return $post_action; + } + + public function getContent() + { + $post_action = null; + if (!empty($_POST)) + { + $this->_postValidation(); + if (!count($this->_postErrors)) + $post_action = $this->_postProcess(); + } + $carriers_list = Db::getInstance()->executeS(' + SELECT m.* + FROM `'._DB_PREFIX_.'mr_method` m + LEFT JOIN `'._DB_PREFIX_.'carrier` c + ON (c.`id_carrier` = m.`id_carrier`) + LEFT JOIN `'._DB_PREFIX_.'mr_method_shop` ms + ON ms.`id_mr_method` = m.`id_mr_method` + WHERE c.`deleted` = 0 AND ms.`id_shop` = '.(int)$this->account_shop['id_shop']); + + $controller = version_compare(_PS_VERSION_, '1.5', '<') ? 'AdminContact' : 'AdminStores'; + + /* TODO : Fill an array with admi controller name */ + $this->context->smarty->assign(array( + 'MR_token_admin_performance' => Tools::getAdminToken('AdminPerformance'.(int)(Tab::getIdFromClassName('AdminPerformance')).(int)($this->context->cookie->id_employee)), + 'MR_token_admin_carriers' => (version_compare(_PS_VERSION_, '1.6', '<') ? Tools::getAdminToken('AdminCarriers'.(int)(Tab::getIdFromClassName('AdminCarriers')).(int)$this->context->employee->id) : Tools::getAdminToken('AdminCarrierWizard'.(int)(Tab::getIdFromClassName('AdminCarrierWizard')).(int)$this->context->employee->id)), + 'MR_token_admin_contact' => array( + 'controller_name' => $controller, + 'token' => Tools::getAdminToken($controller.(int)(Tab::getIdFromClassName($controller)).(int)$this->context->employee->id)), + 'MR_token_admin_mondialrelay' => Tools::getAdminToken('AdminMondialRelay'.(int)(Tab::getIdFromClassName('AdminMondialRelay')).(int)$this->context->employee->id), + 'MR_token_admin_module' => Tools::getAdminToken('AdminModules'.(int)(Tab::getIdFromClassName('AdminModules')).(int)$this->context->employee->id), + 'MR_enseigne_webservice' => Tools::getValue('MR_enseigne_webservice') ? Tools::getValue('MR_enseigne_webservice') : $this->account_shop['MR_ENSEIGNE_WEBSERVICE'], + 'MR_code_marque' => Tools::getValue('MR_code_marque') ? Tools::getValue('MR_code_marque') : $this->account_shop['MR_CODE_MARQUE'], + 'MR_webservice_key' => Tools::getValue('MR_webservice_key') ? Tools::getValue('MR_webservice_key') : $this->account_shop['MR_KEY_WEBSERVICE'], + 'MR_available_languages' => Language::getLanguages(), + 'MR_selected_language' => $this->account_shop['MR_LANGUAGE'], + 'MR_weight_coefficient' => Tools::getValue('MR_weight_coefficient') ? Tools::getValue('MR_weight_coefficient') : $this->account_shop['MR_WEIGHT_COEFFICIENT'], + 'MR_PS_WEIGHT_UNIT' => Configuration::get('PS_WEIGHT_UNIT'), + 'MR_order_states_list' => OrderState::getOrderStates($this->context->language->id), + 'MR_MONDIAL_RELAY_ORDER_STATE' => Configuration::get('MONDIAL_RELAY_ORDER_STATE'), + 'MR_CRON_URL' => Tools::getHttpHost(true, true)._MODULE_DIR_.$this->name.'/cron.php?secure_key='.Configuration::get('MONDIAL_RELAY_SECURE_KEY'), + 'MR_name' => Tools::getValue('MR_name') ? Tools::getValue('MR_name') : '', + 'MR_carriers_list' => $carriers_list, + 'MR_error_list' => $this->_postErrors, + 'MR_form_action' => $post_action, + 'MR_PS_ADMIN_IMG_' => _PS_ADMIN_IMG_, + 'MR_tab_selected' => Tools::getValue('MR_tab_name') ? Tools::getValue('MR_tab_name') : (MondialRelay::isAccountSet() ? 'account_form' : 'info_form'), + 'MR_delay' => Tools::getValue('MR_delay') ? Tools::getValue('MR_delay') : '', + 'MR_local_path' => MondialRelay::$modulePath, + 'MR_upgrade_detail' => $this->upgrade_detail, + 'MR_unit_weight_used' => Configuration::get('PS_WEIGHT_UNIT'), + 'MR_account_set' => MondialRelay::isAccountSet(), + 'MR_base_dir' => MondialRelay::$moduleURL, + 'MR_MONDIAL_RELAY_MODE' => Configuration::get('MONDIAL_RELAY_MODE'), + ) + ); + return $this->fetchTemplate('/views/templates/admin/', 'configuration'); + } + + /** + * Add new carrier + * + * @param $name + * @param $delay + * @return bool|int + */ + private function addCarrier($name, $delay) + { + $ret = false; + + if(empty($name)){ + return false; + } + if(empty($delay)){ + return false; + } + + if (($carrier = new Carrier())) + { + $delay_lang = array(); + foreach (Language::getLanguages(false) as $lang) + $delay_lang[$lang['id_lang']] = $delay; + $carrier->name = $name; + $carrier->active = 1; + $carrier->range_behavior = 1; + $carrier->need_range = 1; + $carrier->external_module_name = 'mondialrelay'; + $carrier->shipping_method = 1; + $carrier->delay = $delay_lang; + $carrier->is_module = version_compare(_PS_VERSION_, '1.4', '<') ? 0 : 1; + + $ret = $carrier->add(); + } + return $ret ? $carrier->id : false; + } + + public function displayInfoByCart($id_cart) + { + $html = '

'; + $simpleresul = Db::getInstance()->executeS(' + SELECT * FROM '._DB_PREFIX_.'mr_selected + WHERE id_cart='.(int)($id_cart)); + + if (isset($simpleresul[0])) + { + if (trim($simpleresul[0]['exp_number']) != '') + $html .= $this->l('Nb expedition:').$simpleresul[0]['exp_number'].'
'; + if (trim($simpleresul[0]['url_etiquette']) != '') + $html .= ''.$this->l('Label URL').'
'; + if (trim($simpleresul[0]['url_suivi']) != '') + $html .= ''.$this->l('Follow-up URL').'
'; + if (trim($simpleresul[0]['MR_Selected_Num']) != '') + $html .= $this->l('Nb Point Relay :').$simpleresul[0]['MR_Selected_Num'].'
'; + if (trim($simpleresul[0]['MR_Selected_LgAdr1']) != '') + $html .= $simpleresul[0]['MR_Selected_LgAdr1'].'
'; + if (trim($simpleresul[0]['MR_Selected_LgAdr2']) != '') + $html .= $simpleresul[0]['MR_Selected_LgAdr2'].'
'; + if (trim($simpleresul[0]['MR_Selected_LgAdr3']) != '') + $html .= $simpleresul[0]['MR_Selected_LgAdr3'].'
'; + if (trim($simpleresul[0]['MR_Selected_LgAdr4']) != '') + $html .= $simpleresul[0]['MR_Selected_LgAdr4'].'
'; + if (trim($simpleresul[0]['MR_Selected_CP']) != '') + $html .= $simpleresul[0]['MR_Selected_CP'].' '; + if (trim($simpleresul[0]['MR_Selected_Ville']) != '') + $html .= $simpleresul[0]['MR_Selected_Ville'].'
'; + if (trim($simpleresul[0]['MR_Selected_Pays']) != '') + $html .= $simpleresul[0]['MR_Selected_Pays'].'
'; + } + $html .= '

'; + return $html; + } + + /** + * Set necessaries values to the created carrier + * + * @param $id_carrier + * @param $dlv_mode + * @return bool + */ + private function addDefaultCarrierValue($id_carrier, $dlv_mode) + { + $weight_coef = $this->account_shop['MR_WEIGHT_COEFFICIENT']; + + /* Default Range value depending of the delivery mode */ + $range_weight = array( + '24R' => array(0, 30000 / $weight_coef), + 'DRI' => array(30000 / $weight_coef, 130000 / $weight_coef), + 'LD1' => array(0, 60000 / $weight_coef), + 'HOM' => array(0, 60000 / $weight_coef), + 'LDS' => array(30000 / $weight_coef, 130000 / $weight_coef) + ); + + /* Set range weight for a dlv_mode */ + if (!Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'range_weight` + (`id_carrier`, `delimiter1`, `delimiter2`) + VALUES ('.(int)$id_carrier.', '.$range_weight[$dlv_mode][0].', '.$range_weight[$dlv_mode][1].')')) + { + $this->_postErrors[] = $this->l('Range weight can\'t be added'); + return false; + } + + $range_weight_id = Db::getInstance()->Insert_ID(); + + /* Set a range price */ + if (!Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'range_price` + (`id_carrier`, `delimiter1`, `delimiter2`) + VALUES ('.(int)$id_carrier.', 0.000000, 10000.000000)')) + { + $this->_postErrors[] = $this->l('Range price can\'t be added'); + return false; + } + + $range_price_id = Db::getInstance()->Insert_ID(); + + $groups = Group::getGroups(Configuration::get('PS_LANG_DEFAULT')); + foreach ($groups as $group) + if (!Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'carrier_group` + (id_carrier, id_group) + VALUES('.(int)$id_carrier.', '.(int)($group['id_group']).')')) + { + $this->_postErrors[] = $this->l('Default zone can\'t be added'); + return false; + } + + /* Set default zone */ + $zones = Zone::getZones(); + foreach ($zones as $zone) + { + if (!Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'carrier_zone` + (id_carrier, id_zone) + VALUES('.(int)$id_carrier.', '.(int)($zone['id_zone']).')') || + !Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'delivery` + (id_carrier, id_range_price, id_range_weight, id_zone, price) + VALUES('.(int)$id_carrier.', '.(int)($range_price_id).', NULL,'.(int)($zone['id_zone']).', 0.00)') || + !Db::getInstance()->execute( + 'INSERT INTO `'._DB_PREFIX_.'delivery` + (id_carrier, id_range_price, id_range_weight, id_zone, price) + VALUES('.(int)$id_carrier.', NULL, '.(int)($range_weight_id).','.(int)($zone['id_zone']).', 0.00)')) + { + $this->_postErrors[] = $this->l('Carrier zone or delivery data can\'t be added'); + return false; + } + } + return true; + } + + /** + * Add new shipping method + * + * @return bool + */ + private function addShippingMethod() + { + /* Insert new carrier for under Prestashop */ + if (!($id_carrier = $this->addCarrier(Tools::getValue('MR_name'), Tools::getValue('MR_delay')))) + { + $this->_postErrors[] = $this->l('Carrier can\'t be created in PrestaShop'); + return false; + } + + $fields = $_POST; + + unset($fields['submit_add_shipping'], $fields['MR_tab_name'], $fields['tab'], $fields['MR_delay']); + + /* Force col mod to CCC */ + $fields['col_mode'] = 'CCC'; + $fields['id_carrier'] = $id_carrier; + $fields['is_deleted'] = 0; + + $query = 'INSERT INTO `'._DB_PREFIX_.'mr_method` (%s) VALUES(%s)'; + + $keys = array(); + $values = array(); + foreach ($fields as $key => $value) + { + $keys[] = '`'.str_replace('MR_', '', $key).'`'; + $values[] = '\''.(is_array($value) ? pSQL(implode(',', $value)) : pSQL($value)).'\''; + } + $query = sprintf($query, implode(',', $keys), implode(',', $values)); + + if (!Db::getInstance()->execute($query) || !Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'mr_method_shop` + (id_mr_method, id_shop) VALUES('.(int)Db::getInstance()->INSERT_ID().', '.(int)$this->account_shop['id_shop'].')')) + { + $this->l('Carrier method can\'t be added for the module'); + Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'carrier` WHERE id_carrier='.(int)$id_carrier); + return false; + } + return $this->addDefaultCarrierValue($id_carrier, $fields['MR_dlv_mode']); + } + + /** + * Disable carrier instead of delete to keep history + * + * @param $id_mr_method + */ + public function disableCarrier($id_mr_method) + { + if (($id_carrier = Db::getInstance()->getValue( + 'SELECT `id_carrier` + FROM `'._DB_PREFIX_.'mr_method` + WHERE `id_mr_method` = '.(int)($id_mr_method))) && + Db::getInstance()->execute( + 'UPDATE `'._DB_PREFIX_.'carrier` c, `'._DB_PREFIX_.'mr_method` m + SET c.`active` = 0, c.`deleted` = 1, m.`is_deleted` = 1 + WHERE + c.`id_carrier` = m.`id_carrier` + AND c.`id_carrier` = '.(int)$id_carrier)) + return true; + + $this->_postErrors = $this->l('Carrier can\'t be deleted yet'); + return false; + } + + /** + * Get the followup url + * + * @param $exp_number + * @return mixed + */ + public function get_followup($exp_number) + { + $query = ' + SELECT url_suivi + FROM '._DB_PREFIX_.'mr_selected + WHERE exp_number='.(int)$exp_number; + + return Db::getInstance()->getValue($query); + } + + /** + * Get the SQL query to fetch order with mr carrier + * + * @static + * @param $id_order_state + * @param $weight_coefficient + * @return string + */ + public static function getBaseOrdersSQLQuery($id_order_state, $weight_coefficient = 0) + { + return 'SELECT o.`id_address_delivery` as id_address_delivery, + o.`id_order` as id_order, + o.`id_customer` as id_customer, + o.`id_cart` as id_cart, + o.`id_lang` as id_lang, + mrs.`id_mr_selected` as id_mr_selected, + CONCAT(c.`firstname`, " ", c.`lastname`) AS `customer`, + o.`total_paid_real` as total, o.`total_shipping` as shipping, + o.`date_add` as date, o.`id_currency` as id_currency, o.`id_lang` as id_lang, + mrs.`MR_poids` as mr_weight, + mrs.`MR_insurance` as mr_insurance, + mr.`name` as mr_Name, mrs.`MR_Selected_Num` as MR_Selected_Num, + mrs.`MR_Selected_Pays` as MR_Selected_Pays, mrs.`exp_number` as exp_number, + mr.`col_mode` as mr_ModeCol, mr.`dlv_mode` as mr_ModeLiv, mr.`insurance` as mr_ModeAss, + ROUND(SUM(odt.`product_weight` * odt.`product_quantity`) * '.(int)$weight_coefficient.') AS "order_weight" + FROM + `'._DB_PREFIX_.'orders` o + LEFT JOIN + `'._DB_PREFIX_.'carrier` ca + ON (ca.`id_carrier` = o.`id_carrier`) + LEFT JOIN + `'._DB_PREFIX_.'mr_selected` mrs + ON (mrs.`id_cart` = o.`id_cart`) + LEFT JOIN + `'._DB_PREFIX_.'mr_method` mr + ON (mr.`id_carrier` = ca.`id_carrier`) + LEFT JOIN + `'._DB_PREFIX_.'customer` c + ON (c.`id_customer` = o.`id_customer`) + LEFT JOIN + `'._DB_PREFIX_.'order_detail` odt + ON odt.`id_order` = o.`id_order` + WHERE ( + SELECT moh.`id_order_state` + FROM `'._DB_PREFIX_.'order_history` moh + WHERE moh.`id_order` = o.`id_order` + ORDER BY moh.`date_add` DESC LIMIT 1) = '.(int)($id_order_state).' + AND o.`id_order` = mrs.`id_order`'; + } + + /** + * Get orders details to create Tickets + * + * @static + * @param array $orderIdList + * @param int $filterEntries + * @param int $weight_coefficient + * @return array + */ + public static function getOrders($orderIdList = array(), $filterEntries = MondialRelay::NO_FILTER, $weight_coefficient = 0) + { + $account_shop = MondialRelay::getAccountDetail(); + $id_order_state = $account_shop['MR_ORDER_STATE']; + $sql = MondialRelay::getBaseOrdersSQLQuery($id_order_state, $weight_coefficient); + + if (count($orderIdList)) + { + $sql .= ' AND o.id_order IN ('; + foreach ($orderIdList as $id_order) + $sql .= (int)$id_order.', '; + $sql = rtrim($sql, ', ').')'; + } + switch ($filterEntries) + { + case MondialRelay::WITHOUT_HOME_DELIVERY: + $sql .= 'AND mr.mr_ModeLiv != "LD1" AND mr.mr_ModeLiv != "LDS" AND mr.mr_ModeLiv != "HOM"'; + break; + } + $sql .= ' + GROUP BY o.`id_order` + ORDER BY o.`date_add` ASC'; + return Db::getInstance()->executeS($sql); + } + + /** + * Get Mondialrelay error code + * + * @param $code + * @return string + */ + public function getErrorCodeDetail($code) + { + include(dirname(__FILE__).'/errorCode.php'); + if (isset($statCode[$code])) + return $statCode[$code]; + return $this->l('This error isn\'t referred : ').$code; + } + + /** + * @param $id_cart + * @return mixed + */ + public function getRelayPointSelected($id_cart) + { + return Db::getInstance()->getRow(' + SELECT s.`MR_selected_num` + FROM `'._DB_PREFIX_.'mr_selected` s + WHERE s.`id_cart` = '.(int)$id_cart); + } + + /** + * @param $id_carrier + * @return mixed + */ + public function isMondialRelayCarrier($id_carrier) + { + return Db::getInstance()->getRow(' + SELECT m.`id_mr_method` + FROM `'._DB_PREFIX_.'mr_method` m + WHERE `id_carrier` = '.(int)$id_carrier); + } + + public function getMethodValueByIdCarier($id_carrier, $key) + { + $content = Db::getInstance()->executeS( + 'SELECT * + FROM `'._DB_PREFIX_.'mr_method` m + WHERE `id_carrier` = '.(int)$id_carrier); + + return isset($content[$key]) ? $content[$key] : ''; + } + + public function checkAJAX() + { + /* Avoid loop inclusion */ + static $is_included = false; + + /* TODO : Find a way to use it on front */ + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && Tools::strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') + if (Tools::getValue('mrtoken') && !$is_included) + { + $is_included = true; + include(dirname(__FILE__).'/ajax.php'); + exit(0); + } + } } diff --git a/sql/index.php b/sql/index.php index fc4110f..087b026 100644 --- a/sql/index.php +++ b/sql/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/sql/mrInstall.sql b/sql/mrInstall.sql index 0d3cb5b..b63ebe0 100644 --- a/sql/mrInstall.sql +++ b/sql/mrInstall.sql @@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS `PREFIX_mr_method` ( `dlv_mode` varchar(3) NOT NULL, `insurance` varchar(3) NOT NULL DEFAULT '0', `id_carrier` int(10) NOT NULL, - `is_deleted` int(10) NOT NULL, + `is_deleted` int(10) NOT NULL DEFAULT '0', PRIMARY KEY (`id_mr_method`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; diff --git a/upgrade/index.php b/upgrade/index.php index fc4110f..087b026 100644 --- a/upgrade/index.php +++ b/upgrade/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/upgrade/install-1.8.0.php b/upgrade/install-1.8.0.php index 7d99093..3d16016 100644 --- a/upgrade/install-1.8.0.php +++ b/upgrade/install-1.8.0.php @@ -28,103 +28,103 @@ /* File Example for upgrade */ if (!defined('_PS_VERSION_')) - exit; + exit; /* object module ($this) available */ function upgrade_module_1_8_0($object) { - $upgrade_version = '1.8.0'; - - $object->upgrade_detail[$upgrade_version] = array(); - - /* Add new table to handle multi-shop for a carrier */ - $query = ' - CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mr_method_shop` ( - `id_mr_method_shop` int(10) unsigned NOT NULL auto_increment, - `id_mr_method` int(10) unsigned NOT NULL, - `id_shop` int(10) unsigned NOT NULL, - PRIMARY KEY (`id_mr_method_shop`) - ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;'; - - if (!Db::getInstance()->execute($query)) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t create method shop table'); - - /* Refacto name */ - $query = ' - ALTER TABLE `'._DB_PREFIX_.'mr_method` CHANGE `id_mr_method` `id_mr_method` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT , - CHANGE `mr_Name` `name` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , - CHANGE `mr_Pays_list` `country_list` VARCHAR( 1000 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , - CHANGE `mr_ModeCol` `col_mode` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , - CHANGE `mr_ModeLiv` `dlv_mode` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , - CHANGE `mr_ModeAss` `insurance` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT \'0\', - CHANGE `id_carrier` `id_carrier` INT( 10 ) NOT NULL'; - - if (!Db::getInstance()->execute($query)) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t change name of the method table'); - - $query = 'RENAME TABLE `'._DB_PREFIX_.'mr_historique` TO `'._DB_PREFIX_.'mr_history`'; - - if (!Db::getInstance()->execute($query)) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t rename the history table'); - - $object->account_shop['MR_ENSEIGNE_WEBSERVICE'] = Configuration::get('MR_ENSEIGNE_WEBSERVICE'); - $object->account_shop['MR_CODE_MARQUE'] = Configuration::get('MR_CODE_MARQUE'); - $object->account_shop['MR_KEY_WEBSERVICE'] = Configuration::get('MR_KEY_WEBSERVICE'); - $object->account_shop['MR_LANGUAGE'] = Configuration::get('MR_LANGUAGE'); - $object->account_shop['MR_WEIGHT_COEFFICIENT'] = Configuration::get('MR_WEIGHT_COEF'); - $object->account_shop['MR_ORDER_STATE'] = Configuration::get('MONDIAL_RELAY_ORDER_STATE'); - $object->updateAccountShop(); - - Configuration::deleteByName('MONDIAL_RELAY_INSTALL_UPDATE'); - Configuration::deleteByName('MONDIAL_RELAY_ORDER_STATE'); - Configuration::deleteByName('MR_ENSEIGNE_WEBSERVICE'); - Configuration::deleteByName('MR_CODE_MARQUE'); - Configuration::deleteByName('MR_KEY_WEBSERVICE'); - Configuration::deleteByName('MR_WEIGHT_COEF'); - Configuration::deleteByName('MR_LANGUAGE'); - Configuration::deleteByName('MONDIAL_RELAY_1_4'); - Configuration::deleteByName('MONDIAL_RELAY_INSTALL_UPDATE_1'); - - Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); - - $methods = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'mr_method`'); - if (count($methods)) - { - $query = ' - INSERT INTO `'._DB_PREFIX_.'mr_method_shop` - (id_mr_method, id_shop) VALUES '; - - foreach ($methods as $method) - $query .= '('.(int)$method['id_mr_method'].', '.(int)$object->account_shop['id_shop'].'),'; - $query = trim($query, ','); - if (!Db::getInstance()->execute($query)) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t update table mr_method_shop'); - - } - - if (!empty($object->installed_version)) - { - if ($object->installed_version < '1.4') - Db::getInstance()->execute(' - UPDATE `'._DB_PREFIX_.'carrier` - SET - `shipping_external` = 0, - `need_range` = 1, - `external_module_name` = "mondialrelay", - `shipping_method` = 1 - WHERE `id_carrier` - IN (SELECT `id_carrier` - FROM `'._DB_PREFIX_.'mr_method`)'); - } - - /* Try to register the new hook since 1.7 */ - if (!$object->isRegisteredInHook('newOrder')) - $object->registerHook('newOrder'); - if (!$object->isRegisteredInHook('BackOfficeHeader')) - $object->registerHook('BackOfficeHeader'); - - if (!$object->isRegisteredInHook('header')) - $object->registerHook('header'); - - return (bool)count($object->upgrade_detail[$upgrade_version]); + $upgrade_version = '1.8.0'; + + $object->upgrade_detail[$upgrade_version] = array(); + + /* Add new table to handle multi-shop for a carrier */ + $query = ' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mr_method_shop` ( + `id_mr_method_shop` int(10) unsigned NOT NULL auto_increment, + `id_mr_method` int(10) unsigned NOT NULL, + `id_shop` int(10) unsigned NOT NULL, + PRIMARY KEY (`id_mr_method_shop`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;'; + + if (!Db::getInstance()->execute($query)) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t create method shop table'); + + /* Refacto name */ + $query = ' + ALTER TABLE `'._DB_PREFIX_.'mr_method` CHANGE `id_mr_method` `id_mr_method` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT , + CHANGE `mr_Name` `name` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , + CHANGE `mr_Pays_list` `country_list` VARCHAR( 1000 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , + CHANGE `mr_ModeCol` `col_mode` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , + CHANGE `mr_ModeLiv` `dlv_mode` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , + CHANGE `mr_ModeAss` `insurance` VARCHAR( 3 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT \'0\', + CHANGE `id_carrier` `id_carrier` INT( 10 ) NOT NULL'; + + if (!Db::getInstance()->execute($query)) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t change name of the method table'); + + $query = 'RENAME TABLE `'._DB_PREFIX_.'mr_historique` TO `'._DB_PREFIX_.'mr_history`'; + + if (!Db::getInstance()->execute($query)) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t rename the history table'); + + $object->account_shop['MR_ENSEIGNE_WEBSERVICE'] = Configuration::get('MR_ENSEIGNE_WEBSERVICE'); + $object->account_shop['MR_CODE_MARQUE'] = Configuration::get('MR_CODE_MARQUE'); + $object->account_shop['MR_KEY_WEBSERVICE'] = Configuration::get('MR_KEY_WEBSERVICE'); + $object->account_shop['MR_LANGUAGE'] = Configuration::get('MR_LANGUAGE'); + $object->account_shop['MR_WEIGHT_COEFFICIENT'] = Configuration::get('MR_WEIGHT_COEF'); + $object->account_shop['MR_ORDER_STATE'] = Configuration::get('MONDIAL_RELAY_ORDER_STATE'); + $object->updateAccountShop(); + + Configuration::deleteByName('MONDIAL_RELAY_INSTALL_UPDATE'); + Configuration::deleteByName('MONDIAL_RELAY_ORDER_STATE'); + Configuration::deleteByName('MR_ENSEIGNE_WEBSERVICE'); + Configuration::deleteByName('MR_CODE_MARQUE'); + Configuration::deleteByName('MR_KEY_WEBSERVICE'); + Configuration::deleteByName('MR_WEIGHT_COEF'); + Configuration::deleteByName('MR_LANGUAGE'); + Configuration::deleteByName('MONDIAL_RELAY_1_4'); + Configuration::deleteByName('MONDIAL_RELAY_INSTALL_UPDATE_1'); + + Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); + + $methods = Db::getInstance()->executeS('SELECT * FROM `'._DB_PREFIX_.'mr_method`'); + if (count($methods)) + { + $query = ' + INSERT INTO `'._DB_PREFIX_.'mr_method_shop` + (id_mr_method, id_shop) VALUES '; + + foreach ($methods as $method) + $query .= '('.(int)$method['id_mr_method'].', '.(int)$object->account_shop['id_shop'].'),'; + $query = trim($query, ','); + if (!Db::getInstance()->execute($query)) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t update table mr_method_shop'); + + } + + if (!empty($object->installed_version)) + { + if ($object->installed_version < '1.4') + Db::getInstance()->execute(' + UPDATE `'._DB_PREFIX_.'carrier` + SET + `shipping_external` = 0, + `need_range` = 1, + `external_module_name` = "mondialrelay", + `shipping_method` = 1 + WHERE `id_carrier` + IN (SELECT `id_carrier` + FROM `'._DB_PREFIX_.'mr_method`)'); + } + + /* Try to register the new hook since 1.7 */ + if (!$object->isRegisteredInHook('newOrder')) + $object->registerHook('newOrder'); + if (!$object->isRegisteredInHook('BackOfficeHeader')) + $object->registerHook('BackOfficeHeader'); + + if (!$object->isRegisteredInHook('header')) + $object->registerHook('header'); + + return (bool)count($object->upgrade_detail[$upgrade_version]); } \ No newline at end of file diff --git a/upgrade/install-1.8.3.php b/upgrade/install-1.8.3.php index d416e1c..6d500a6 100644 --- a/upgrade/install-1.8.3.php +++ b/upgrade/install-1.8.3.php @@ -26,20 +26,20 @@ */ if (!defined('_PS_VERSION_')) - exit; + exit; /** * object module available */ function upgrade_module_1_8_3($object) { - $upgrade_version = '1.8.3'; + $upgrade_version = '1.8.3'; - $object->upgrade_detail[$upgrade_version] = array(); + $object->upgrade_detail[$upgrade_version] = array(); - if (!Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.'mr_method` ADD `is_deleted` INT NOT NULL')) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t add new field in methodtable'); + if (!Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.'mr_method` ADD `is_deleted` INT NOT NULL')) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t add new field in methodtable'); - Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); - return (bool)count($object->upgrade_detail[$upgrade_version]); + Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); + return (bool)count($object->upgrade_detail[$upgrade_version]); } \ No newline at end of file diff --git a/upgrade/install-2.0.6.php b/upgrade/install-2.0.6.php index ff4f9fd..cb4050a 100644 --- a/upgrade/install-2.0.6.php +++ b/upgrade/install-2.0.6.php @@ -26,23 +26,23 @@ */ if (!defined('_PS_VERSION_')) - exit; + exit; /** * object module available */ function upgrade_module_2_0_6($object) { - $upgrade_version = '2.0.6'; + $upgrade_version = '2.0.6'; - $object->upgrade_detail[$upgrade_version] = array(); + $object->upgrade_detail[$upgrade_version] = array(); - try { - if (!Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.'mr_selected` ADD `MR_insurance` INT( 11 ) NOT NULL AFTER `MR_poids`')) - $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t add new field in methodtable'); - } - catch (Exception $e) { } - - Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); - return (bool)count($object->upgrade_detail[$upgrade_version]); + try { + if (!Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.'mr_selected` ADD `MR_insurance` INT( 11 ) NOT NULL AFTER `MR_poids`')) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t add new field in methodtable'); + } + catch (Exception $e) { } + + Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); + return (bool)count($object->upgrade_detail[$upgrade_version]); } \ No newline at end of file diff --git a/upgrade/install-2.1.7.php b/upgrade/install-2.1.7.php new file mode 100644 index 0000000..99e7099 --- /dev/null +++ b/upgrade/install-2.1.7.php @@ -0,0 +1,31 @@ +upgrade_detail[$upgrade_version] = array(); + + try { + // return false si erreur + if (!Db::getInstance()->execute('ALTER TABLE `'._DB_PREFIX_.'mr_method` ALTER `is_deleted` SET DEFAULT "0"')) + $object->upgrade_detail[$upgrade_version][] = $object->l('Can\'t add new field in methodtable'); + + return Configuration::updateValue('MONDIAL_RELAY', $upgrade_version); + } + catch (Exception $e) { + return false; + } +} \ No newline at end of file diff --git a/views/index.php b/views/index.php index 0bfbc8e..fa0395b 100644 --- a/views/index.php +++ b/views/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/views/templates/admin/index.php b/views/templates/admin/index.php index 4deda52..9cf60e3 100644 --- a/views/templates/admin/index.php +++ b/views/templates/admin/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/views/templates/front/checkout_process.tpl b/views/templates/front/checkout_process.tpl index e221b9e..384c326 100644 --- a/views/templates/front/checkout_process.tpl +++ b/views/templates/front/checkout_process.tpl @@ -24,15 +24,22 @@ * International Registered Trademark & Property of PrestaShop SA *} + + {*if $MR_Data.PS_VERSION >= '1.5' && !$MR_Data.carrier} {l s='Mondial relay can\'t fetch any replay point due to prestashop error' mod='mondialrelay'} -{/if*} \ No newline at end of file +{/if*} diff --git a/views/templates/front/checkout_process_widget.tpl b/views/templates/front/checkout_process_widget.tpl index 7eaee98..ef98f71 100644 --- a/views/templates/front/checkout_process_widget.tpl +++ b/views/templates/front/checkout_process_widget.tpl @@ -23,12 +23,17 @@ * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA *} + + + @@ -49,5 +49,6 @@ + diff --git a/views/templates/front/header_widget.tpl b/views/templates/front/header_widget.tpl index 040c1bd..95f8ecc 100644 --- a/views/templates/front/header_widget.tpl +++ b/views/templates/front/header_widget.tpl @@ -29,6 +29,7 @@ {/if} + @@ -48,4 +49,4 @@ PS_MRTranslationList['errorSelection'] = "{l s='Please choose a relay point' mod='mondialrelay' js=1}"; PS_MRTranslationList['openingRelay'] = "{l s='Opening hours' mod='mondialrelay' js=1}"; PS_MRTranslationList['moreDetails'] = "{l s='More details' mod='mondialrelay' js=1}"; - \ No newline at end of file + diff --git a/views/templates/front/index.php b/views/templates/front/index.php index 7752dc5..21075f4 100644 --- a/views/templates/front/index.php +++ b/views/templates/front/index.php @@ -26,10 +26,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file diff --git a/views/templates/index.php b/views/templates/index.php index fc4110f..087b026 100644 --- a/views/templates/index.php +++ b/views/templates/index.php @@ -27,10 +27,10 @@ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); - + header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); - + header("Location: ../"); exit; \ No newline at end of file