Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
suite_manager.inc
1 <?php
18 require_once SQ_CORE_PACKAGE_PATH.'/page/page.inc';
19 
31 class Suite_Manager extends Page
32 {
33 
39  private $_userAgentPrefix = 'SquizSuite-';
40 
46  private $_suiteSystemUser = FALSE;
47 
53  private $_productCache = Array();
54 
60  private $_opensslEncryptionAlgorithm = 'blowfish';
61 
67  private $_responseCode = Array(
68  200 => 'OK',
69  304 => 'Not Modified',
70  400 => 'Bad Request',
71  401 => 'Unauthorized',
72  403 => 'Forbidden',
73  404 => 'Not Found',
74  405 => 'Method Not Allowed',
75  406 => 'Not Acceptable',
76  420 => 'Enhance Your Calm',
77  500 => 'Internal Server Error',
78  502 => 'Bad Gateway',
79  503 => 'Service Unavailable',
80  );
81 
82 
89  function __construct($assetid=0)
90  {
91  $this->_ser_attrs = TRUE;
92  parent::__construct($assetid);
93 
94  }//end constructor
95 
96 
106  function create(Array &$link)
107  {
108  require_once SQ_CORE_PACKAGE_PATH.'/system/system_asset_fns.inc';
109  if (!system_asset_fns_create_pre_check($this)) {
110  return FALSE;
111  }
112  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
113  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
114 
115  if ($linkid = parent::create($link)) {
116  if (!system_asset_fns_create_cleanup($this)) {
117  $linkid = FALSE;
118  }
119  }
120 
121  if ($linkid) {
122  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
123  } else {
124  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
125  }
126 
127  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
128 
129  // Create the data directory, ready for use
130  if (!create_directory($this->data_path)) {
131  trigger_localised_error('SYS0148', E_USER_WARNING, $this->name);
132  return FALSE;
133  }
134  return $linkid;
135 
136  }//end create()
137 
138 
148  function _getName($short_name=FALSE)
149  {
150  return 'Squiz Suite Manager';
151 
152  }//end _getName()
153 
154 
161  function canDelete()
162  {
163  return FALSE;
164 
165  }//end canDelete()
166 
167 
174  function canClone()
175  {
176  return FALSE;
177 
178  }//end canClone()
179 
180 
192  public function getProducts($includeCurrent=TRUE, $includeDeleted=TRUE, $excludeTypes=array(), $includeStatus=array())
193  {
194  $products = MatrixDAL::executeAssoc('squiz_suite_package', 'getAllProducts');
195  foreach ($products as &$p) {
196  $p['connection'] = unserialize($p['connection']);
197 
198  // Adding to product cache.
199  $suiteid = $p['suiteid'];
200  if (array_key_exists($suiteid, $this->_productCache) === FALSE) {
201  $this->_productCache[$suiteid] = $p;
202  }//end if
203  }//end foreach
204 
205  $results = array();
206  foreach ($products as $i => $product) {
207  if ($includeCurrent === FALSE && $product['is_current'] == TRUE) {
208  continue;
209  }
210 
211  if ($includeDeleted === FALSE && $product['sync_status'] === 'D') {
212  continue;
213  }
214 
215  if (empty($excludeTypes) === FALSE && in_array($product['type'], $excludeTypes) === TRUE) {
216  continue;
217  }
218 
219  if (empty($includeStatus) === FALSE && in_array($product['status'], $includeStatus) === FALSE) {
220  continue;
221  }
222 
223  $results[] = $product;
224  }//end foreach
225 
226  return $results;
227 
228  }//end getProducts()
229 
230 
237  public function getLiveProducts()
238  {
239  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getAllProducts');
240  foreach ($results as &$p) {
241  $p['connection'] = unserialize($p['connection']);
242 
243  // Adding to product cache.
244  $suiteid = $p['suiteid'];
245  if (array_key_exists($suiteid, $this->_productCache) === FALSE) {
246  $this->_productCache[$suiteid] = $p;
247  }//end if
248  }//end foreach
249 
250  return $results;
251 
252  }//end getLiveProducts()
253 
254 
263  public function getProduct($suiteid=NULL)
264  {
265  if ($suiteid === NULL || is_numeric($suiteid) === FALSE) {
266  return Array();
267  }//end if
268 
269  if (array_key_exists($suiteid, $this->_productCache) === TRUE) {
270  return $this->_productCache[$suiteid];
271  }
272 
273  try {
274  $bind_vars = Array('suiteid' => $suiteid);
275  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getProduct', $bind_vars);
276  } catch (PDOException $e) {
277  trigger_localised_error('SQS0030', E_USER_ERROR);
278  return Array();
279  }//end try-catch
280 
281  if (empty($results) === TRUE) {
282  return $results;
283  }
284 
285  $product = array_pop($results);
286  $product['connection'] = unserialize($product['connection']);
287 
288  // Cache the result
289  $this->_productCache[$suiteid] = $product;
290  return $product;
291 
292  }//end getProduct()
293 
294 
303  public function getProductBySystemid($systemid)
304  {
305  try {
306  $bind_vars = Array('systemid' => $systemid);
307  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getProductBySystemid', $bind_vars);
308  } catch (PDOException $e) {
309  trigger_localised_error('SQS0030', E_USER_ERROR);
310  return Array();
311  }//end try-catch
312 
313  foreach ($results as &$p) {
314  $p['connection'] = unserialize($p['connection']);
315  }
316 
317  return $results;
318 
319  }//end getProductBySystemid()
320 
321 
331  public function getProductBySystemidURL($systemid, $url)
332  {
333  try {
334  $bind_vars = Array('systemid' => $systemid, 'url' => $url);
335  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getProductBySystemidURL', $bind_vars);
336  } catch (PDOException $e) {
337  trigger_localised_error('SQS0030', E_USER_ERROR);
338  return Array();
339  }//end try-catch
340 
341  if (empty($results) === TRUE) {
342  return $results;
343  } else {
344  $product = array_pop($results);
345  $product['connection'] = unserialize($product['connection']);
346  }
347 
348  return $product;
349 
350  }//end getProductBySystemidURL()
351 
352 
361  public function getProductByToken($token)
362  {
363  $product = NULL;
364  try {
365  $bind_vars = Array('token' => $token);
366  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getProductByToken', $bind_vars);
367  } catch (PDOException $e) {
368  trigger_localised_error('SQS0030', E_USER_ERROR);
369  return Array();
370  }//end try-catch
371 
372  if (empty($results) === TRUE) {
373  return $product;
374  } else {
375  $product = array_pop($results);
376  $product['connection'] = unserialize($product['connection']);
377  }
378 
379  return $product;
380 
381  }//end getProductBySystemid()
382 
383 
389  public function getSystemId()
390  {
391  $currProduct = $this->getCurrentProduct();
392  return $currProduct['systemid'];
393 
394  }//end getSystemId()
395 
396 
406  public function getProductAttribute($suiteid, $attrName)
407  {
408  $product = $this->getProduct($suiteid);
409  if (empty($product) === FALSE) {
410  if (array_key_exists($attrName, $product) === TRUE) {
411  return $product[$attrName];
412  } else if (array_key_exists($attrName, $product['connection']) === TRUE) {
413  return $product['connection'][$attrName];
414  }
415  }
416 
417  return NULL;
418 
419  }//end getProductAttribute()
420 
421 
428  public function getCurrentProduct()
429  {
430  try {
431  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getCurrentProduct', Array('is_current'=>'1'));
432  } catch (PDOException $e) {
433  trigger_localised_error('SQS0030', E_USER_ERROR);
434  return Array();
435  }//end try-catch
436 
437  if (empty($results) === TRUE) {
438  return $results;
439  }
440 
441  $product = array_pop($results);
442  $product['connection'] = unserialize($product['connection']);
443 
444  return $product;
445 
446  }//end getCurrentProduct()
447 
448 
456  public function getProductsByType($type)
457  {
458  $results = MatrixDAL::executeAssoc('squiz_suite_package', 'getProductsByType', Array('type'=>$type));
459 
460  foreach ($result as &$product) {
461  $product['connection'] = unserialize($product['connection']);
462  $this->_productCache[$product['suiteid']] = $product;
463  }
464 
465  return $result;
466 
467  }//end getProductsByType()
468 
469 
479  public function updateProduct($suiteid, $attrName, $attrValue)
480  {
481  if ($attrName === 'connection' && is_array($attrValue) === TRUE) {
482  $attrValue = serialize($attrValue);
483  }
484 
485  if ($attrName == 'is_current' && is_bool($attrValue) === TRUE) {
486  $attrValue = ($attrValue) ? '1' : '0';
487  }
488 
489  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
490  try {
491  $sql = 'UPDATE sq_suite_product SET '.$attrName.'=:'.$attrName.' WHERE suiteid=:suiteid';
492  $query = MatrixDAL::preparePDOQuery($sql);
493  MatrixDAL::bindValueToPdo($query, 'suiteid', $suiteid);
494  MatrixDAL::bindValueToPdo($query, $attrName, $attrValue);
495  MatrixDAL::execPdoQuery($query);
496  } catch (PDOException $e) {
497  trigger_localised_error('SQS0031', E_USER_ERROR);
498  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
499  return FALSE;
500  }//end try-catch
501  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
502 
503  // Remove the old value from cache
504  if (isset($this->_productCache[$suiteid]) === TRUE) {
505  unset($this->_productCache[$suiteid]);
506  }
507 
508  return TRUE;
509 
510  }//end updateProduct()
511 
512 
524  public function registerProduct($systemid, $type, $url, $connection)
525  {
526  if (is_array($connection) === TRUE) {
527  $connection = serialize($connection);
528  }
529 
530  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
531  try {
532  $suiteid = MatrixDAL::executeOne('core', 'seqNextVal', Array('seqName' => 'sq_suite_seq'));
533  $bind_vars = Array(
534  'suiteid' => $suiteid,
535  'systemid' => $systemid,
536  'type' => $type,
537  'url' => $url,
538  'connection' => $connection,
539  );
540  MatrixDAL::executeQuery('squiz_suite_package', 'registerProduct', $bind_vars);
541  } catch (PDOException $e) {
542  trigger_localised_error('SQS0031', E_USER_ERROR);
543  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
544  return '';
545  }//end try-catch
546  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
547 
548  return $suiteid;
549 
550  }//end registerProduct()
551 
552 
562  public function removeProduct($suiteid, $current=FALSE)
563  {
564  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
565  try {
566  $bind_vars = Array('suiteid' => $suiteid, 'is_current'=>(int) $current);
567  MatrixDAL::executeQuery('squiz_suite_package', 'removeProduct', $bind_vars);
568  } catch (PDOException $e) {
569  trigger_localised_error('SQS0031', E_USER_ERROR);
570  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
571  return FALSE;
572  }//end try-catch
573  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
574 
575  return TRUE;
576 
577  }//end removeProduct()
578 
579 
586  public function printFrontend()
587  {
588  $responseContent = Array();
589 
590  // Only allow communications if live.
591  if ($this->status != SQ_STATUS_LIVE) {
592  $responseContent['exception'] = 'Forbidden';
593  $this->_sendHeader(403);
594  $this->_sendResponse($responseContent);
595  exit;
596  }//end if
597 
598  // Only support POST method.
599  $requestMethod = strtolower($_SERVER['REQUEST_METHOD']);
600  if ($requestMethod !== 'post') {
601  $responseContent['exception'] = 'The request method is not supported.';
602  $this->_sendHeader(405);
603  header('Allow: POST');
604  $this->_sendResponse($responseContent);
605  exit;
606  }
607 
608  // Don't forget to handle with the encryption on incoming connections
609  $this->decryptAPIData();
610 
611  // Get the requested service details from POST request.
612  $request = Array();
613  $request['_enc'] = array_get_index($_POST, '_enc', NULL);
614  $request['system'] = array_get_index($_POST, '_system', NULL);
615  $request['action'] = array_get_index($_POST, '_action', NULL);
616  $request['format'] = array_get_index($_POST, '_format', 'xml');
617  $request['assetid'] = NULL;
618  if ($request['system'] === NULL && $request['_enc'] !== NULL) {
619  // Encrypted request found, load 'em up
620  $request['system'] = array_get_index($request['_enc'], '_system', NULL);
621  $request['action'] = array_get_index($request['_enc'], '_action', NULL);
622  $request['format'] = array_get_index($request['_enc'], '_format', 'xml');
623  }//end if
624 
625  if ($request['system'] === NULL || $request['action'] === NULL) {
626  $responseContent['exception'] = 'Failed to get system and action from the request';
627  $this->_sendHeader(400);
628  $this->_sendResponse($responseContent, $request['format']);
629  exit;
630  }//end if
631 
632  // Permission check
633  if ($this->_checkPermission($request) === FALSE) {
634  $responseContent['exception'] = 'Forbidden';
635  $this->_sendHeader(403);
636  $this->_sendResponse($responseContent, $request['format']);
637  exit;
638  }//end if
639 
640  // Is the requested service available?
641  $api = $this->getAPI($request['system'], $request['action']);
642  $responseContent['system'] = $request['system'];
643  $responseContent['action'] = $request['action'];
644  if ($api === FALSE) {
645  // Requested service is not available. Not Found.
646  $responseContent['exception'] = 'Requested service is not found.';
647  $this->_sendHeader(404);
648  $this->_sendResponse($responseContent, $request['format']);
649  exit;
650  }
651 
652  // Note that we set system and action name again here with
653  $responseContent['system'] = $api['system'];
654  $responseContent['action'] = $api['action'];
655 
656  // What parameters are required for the action?
657  $params = $this->_getActionParameters($requestMethod,$api['system'],$api['action'],$request['assetid']);
658 
659  if ($params === FALSE) {
660  // Insufficient parameters are provided.
661  $responseContent['exception'] = 'Provided parameters are not sufficient';
662  $this->_sendHeader(400);
663  $this->_sendResponse($responseContent, $request['format']);
664  exit;
665  }
666 
667  $system = $api['system'];
668  $action = $api['action'];
669  $assetid = $request['assetid'];
670 
671  try {
672  if ($system != 'squizsuite') {
673  $class = 'Squiz_Suite_System_'.$system;
674  } else {
675  $class = 'Squiz_Suite_System_Suite';
676  }
677  $GLOBALS['SQ_SYSTEM']->am->includeAsset($class);
678  $callback = Array(
679  $class,
680  $action,
681  );
682  $responseContent['result'] = call_user_func_array($callback, $params);
683  $response = 200;
684  } catch (Exception $e) {
685  $responseContent['exception'] = $e->getMessage();
686  $response = 500;
687  }
688 
689  $this->_sendHeader($response);
690  $this->_sendResponse($responseContent, $request['format']);
691  exit;
692 
693  }//end printFrontend()
694 
695 
704  private function _checkPermission($request)
705  {
706  $check = FALSE;
707  if (isset($request['system']) && isset($request['action'])) {
708  if ($request['system'] === 'SquizSuite' && strpos($request['action'], 'suiteConnect') === 0) {
709  // suiteConnect methods are allowed past
710  $check = TRUE;
711  } else if ($this->_suiteSystemUser === TRUE) {
712  if (isset($request['_enc']['matrix']['username']) && isset($request['_enc']['matrix']['password'])) {
713  $username = trim($request['_enc']['matrix']['username']);
714  $password = trim($request['_enc']['matrix']['password']);
715 
716  $auth_folder = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('authentication_folder');
717  $auth_systems = $auth_folder->getAuthSystems();
718 
719  $user = NULL;
720  foreach ($auth_systems as $systemid) {
721  $system = $GLOBALS['SQ_SYSTEM']->am->getAsset($systemid);
722  if (is_null($system)) continue;
723  $user = $system->authenticateUser($username, $password);
724  if (!is_null($user)) {
725  $GLOBALS['SQ_SYSTEM']->loginUser($user);
726  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($system, TRUE);
727  if ($this->_suiteSystemUser === TRUE) {
728  $check = TRUE;
729  }
730  break;
731  }//end if
732  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($system, TRUE);
733  }//end foreach
734  }//end if
735  }//end if
736  }//end if
737 
738  return $check;
739 
740  }//end _checkPermission()
741 
742 
752  public function getAPI($system, $method)
753  {
754  $system = strtolower($system);
755  $api = FALSE;
756  $systemName = (($system == 'squizsuite') ? 'suite' : $system);
757 
758  // Now see if the system and action is valid
759  $class = 'Squiz_Suite_System_'.ucwords($systemName);
760  $path = SQ_PACKAGES_PATH.'/squiz_suite/systems/'.strtolower($class).'/'.strtolower($class).'.inc';
761  if (file_exists($path)) {
762  include_once $path;
763  if (method_exists($class, $method)) {
764  $api['system'] = $system;
765  $api['action'] = $method;
766  }//end if
767  }//end if
768 
769  return $api;
770 
771  }//end getAPI()
772 
773 
782  public function encodeJson($data)
783  {
784  if (!function_exists('json_encode')) {
785  require_once 'Services/JSON.php';
786  $json = new Services_JSON();
787  $output = $json->encode($data);
788  } else {
789  $output = json_encode($data);
790  }//end else
791 
792  return $output;
793 
794  }//end encodeJson()
795 
796 
805  public function decodeJson($data)
806  {
807  if (strtolower($data) === 'null') {
808  return NULL;
809  } else {
810  if (!function_exists('json_decode')) {
811  require_once 'Services/JSON.php';
812  $json = new Services_JSON(SERVICES_JSON_LOOSE_TYPE);
813  $output = $json->decode($data);
814  } else {
815  $output = json_decode($data, TRUE);
816  }
817 
818  // If not a JSON string, then return the string instead of NULL
819  if ($output === NULL) {
820  $output = $data;
821  }
822 
823  return $output;
824 
825  }//end if
826 
827  }//end decodeJson()
828 
829 
845  private function _getActionParameters($requestMethod, $system, $action, $assetid=NULL)
846  {
847  $requiredParams = Array();
848  $system = strtolower($system);
849 
850  if ($system == 'squizsuite') {
851  $systemName = 'suite';
852  } else {
853  $systemName = $system;
854  }//end if
855 
856  // Include asset.
857  $system = 'Squiz_Suite_System_'.ucwords($systemName);
858  $GLOBALS['SQ_SYSTEM']->am->includeAsset($system);
859  $method = new ReflectionMethod($system, $action);
860  $parameters = $method->getParameters();
861  foreach ($parameters as $parameter) {
862  $requiredParams[] = Array(
863  'name' => $parameter->getName(),
864  'optional' => $parameter->isOptional(),
865  );
866  }
867 
868  $params = Array();
869  $encrypted = array_get_index($_POST, '_enc', NULL);
870  foreach ($requiredParams as $param) {
871  $value = array_get_index($_POST, $param['name'], NULL);
872  $value = $this->decodeJson($value);
873  $value = htmlentities_array($value);
874 
875  if ($value === NULL && $encrypted !== NULL && array_key_exists($param['name'], $encrypted) === TRUE) {
876  $value = $this->decodeJson($encrypted[$param['name']]);
877  }
878 
879  if ($value === NULL && $param['optional'] === FALSE) {
880  return FALSE;
881  } else {
882  $params[] = $value;
883  }
884  }//end foreach
885 
886  return $params;
887 
888  }//end _getActionParameters()
889 
890 
901  public function generateSystemKeyPair()
902  {
903  // Get the current product information.
904  $currProduct = $this->getCurrentProduct();
905 
906  // Generate public/private key pairs.
907  $keyInfo = Array(
908  'private_key_bits' => 2048,
909  'private_key_type' => OPENSSL_KEYTYPE_RSA,
910  );
911 
912  $keyGenerated = openssl_pkey_new($keyInfo);
913 
914  // Keys will be stored in SquizSuite data directory.
915  $dataDir = $this->data_path;
916  openssl_pkey_export_to_file($keyGenerated, $dataDir.'/privatekey.pem');
917 
918  $info = Array(
919  'countryName' => 'AU',
920  'stateOrProvinceName' => 'NSW',
921  'localityName' => 'Sydney',
922  'organizationName' => 'Squiz Suite',
923  'organizationalUnitName' => $currProduct['type'],
924  'commonName' => $currProduct['systemid'],
925  'emailAddress' => $currProduct['systemid'].'@squizsuite.com',
926  );
927  $csr = openssl_csr_new($info, $keyGenerated);
928  openssl_csr_export_to_file($csr, $dataDir.'/system.csr');
929 
930  $cert = openssl_csr_sign($csr, NULL, $keyGenerated, (365 * 10));
931  openssl_x509_export_to_file($cert, $dataDir.'/system.crt');
932  openssl_x509_export($cert, $certStr);
933 
934  // Also update the current product connection information with public key in it.
935  $currProduct['connection']['cert'] = $certStr;
936  $this->updateProduct($currProduct['suiteid'], 'connection', $currProduct['connection']);
937 
938  }//end generateSystemKeyPair()
939 
940 
951  public function sendMessage($targetid, $msgType, array $options=array())
952  {
953  if ($msgType !== 'suiteConnect') {
954  $productInfo = $this->getProduct($targetid);
955  if (empty($productInfo) === TRUE) {
956  $errMsg = $targetid.' is not known. Cannot send '.$msgType.' to it.';
957  throw new Exception($errMsg);
958  }
959  }//end if
960 
961  $dom = $this->createSuiteMessageDOMTemplate($targetid);
962  $this->_createSuiteMessageDOM($dom, $msgType, $options);
963  $xml = $dom->saveXML();
964 
965  $msg = Array();
966  $format = 'xml';
967  $system = 'SquizSuite';
968  $action = $msgType;
969 
970  // Do not encrypt suiteConnect message. In fact, it can not encrypt as
971  // the current system does not know the public key of the destined system.
972  if ($msgType === 'suiteConnect') {
973  $msg['xml'] = $xml;
974  $msg['con_request'] = 1;
975  } else {
976  $msg['_enc'] = array('xml' => $xml);
977  $msg['_pubKeySystemid'] = $targetid;
978  }
979 
980  $result = $this->sendRequest(
981  $targetid,
982  $format,
983  $system,
984  $action,
985  $msg
986  );
987 
988  if ($result['curlInfo']['http_code'] !== 200) {
989  $errMsg = 'HTTP Response '.$result['curlInfo']['http_code']."\n";
990  $errMsg .= var_export($result, 1)."\n";
991  $this->logErrorMessage($errMsg);
992  }
993 
994  return $result;
995 
996  }//end sendMessage()
997 
998 
1006  private function _isMatrixSystem($targetid)
1007  {
1008  $isMatrix = FALSE;
1009 
1010  $product = $this->getProduct($targetid);
1011  if (empty($product) === FALSE) {
1012  if ($product['type'] === 'Squiz Matrix') {
1013  // Aye, its Matrix!
1014  $isMatrix = TRUE;
1015  }
1016  } else {
1017  // An Unknown system, maybe it is a system trying to register
1018  // Attempt to determine if it is Matrix from the URL
1019  if (strpos($targetid, 'http') === 0 && strpos($targetid, '__api') === FALSE) {
1020  $isMatrix = TRUE;
1021  }
1022  }
1023 
1024  return $isMatrix;
1025 
1026  }//end _isMatrixSystem()
1027 
1028 
1041  public function sendRequest($targetid, $format, $system, $action, $message=Array())
1042  {
1043  $currProductInfo = $this->getCurrentProduct();
1044  $userAgentStr = $this->_userAgentPrefix.array_get_index($currProductInfo, 'type', 'Squiz Matrix').'/4.0';
1045  $productInfo = array();
1046 
1047  $apiURL = '';
1048  if ($action === 'suiteConnect') {
1049  $apiURL = $targetid;
1050  } else {
1051  $productInfo = $this->getProduct($targetid);
1052  $apiURL = $productInfo['url'];
1053  }
1054 
1055  if ($this->_isMatrixSystem($targetid)) {
1056  $message['_format'] = $format;
1057  $message['_system'] = $system;
1058  $message['_action'] = $action;
1059  if ($action !== 'suiteConnect') {
1060  // Only set the username/password if the _enc array is set.
1061  if (!isset($message['_enc']) || is_array($message['_enc'])) {
1062  if (!isset($message['_pubKeySystemid'])) {
1063  $message['_pubKeySystemid'] = $targetid;
1064  }
1065 
1066  if (isset($productInfo['connection']['username'])) {
1067  if (!isset($message['_enc']['matrix'])) {
1068  $message['_enc']['matrix'] = array();
1069  }
1070 
1071  $message['_enc']['matrix']['username'] = $productInfo['connection']['username'];
1072  }
1073 
1074  if (isset($productInfo['connection']['password'])) {
1075  $message['_enc']['matrix']['password'] = $productInfo['connection']['password'];
1076  }
1077  }//end if
1078  }//end if
1079  } else {
1080  $apiURL = rtrim($apiURL, '/');
1081  $getVars = '';
1082  if (strpos($apiURL, '?') !== FALSE) {
1083  $getVars = substr($apiURL, strpos($apiURL, '?'));
1084  $apiURL = substr($apiURL, 0, strpos($apiURL, '?'));
1085  $getVars = str_replace('/__api', '', $getVars);
1086  }
1087 
1088  $apiURL .= '/'.$format;
1089  $apiURL .= '/'.$system;
1090  $apiURL .= '/'.$action;
1091  $apiURL .= $getVars;
1092  if ($action !== 'suiteConnect') {
1093  // Set the encryption for all requests except suiteConnect,
1094  // so the target system treats this request as a superuser.
1095  if (!isset($message['_enc'])) {
1096  $message['_enc'] = array();
1097  $message['_pubKeySystemid'] = $targetid;
1098  }
1099 
1100  if (!isset($message['_enc']['_systemid'])) {
1101  $message['_enc']['_systemid'] = $currProductInfo['systemid'];
1102  }
1103  }//end if
1104  }//end if
1105 
1106  $response = $this->sendCURLAPIRequest(
1107  $apiURL,
1108  $message,
1109  $userAgentStr
1110  );
1111 
1112  return $response;
1113 
1114  }//end sendRequest()
1115 
1116 
1122  public function createConnectionToken()
1123  {
1124  $prefix = '';
1125  $moreEntropy = TRUE;
1126  $uniqid = uniqid($prefix, $moreEntropy);
1127 
1128  return $uniqid;
1129 
1130  }//end createConnectionToken()
1131 
1132 
1142  public function syncWithLiveProducts()
1143  {
1144  $all_products = $this->getProducts(FALSE, TRUE, array('Squiz Update'));
1145 
1146  // First collect the systems marked for deletion and addition.
1147  // Once it pulls from all live systems, and if everyone has the same
1148  // addition/deletion, then we can remove the sync_status. If any of the
1149  // systems have not got the message, then we will turn the flag to be
1150  // FALSE, and sync_status will remain.
1151  $markedForDeletion = array();
1152  $markedForAddition = array();
1153  foreach ($all_products as $product) {
1154  if ($product['sync_status'] === 'D') {
1155  $markedForDeletion[$product['suiteid']] = TRUE;
1156  }
1157 
1158  if ($product['sync_status'] === 'A') {
1159  $markedForAddition[$product['suiteid']] = TRUE;
1160  }
1161  }//end foreach
1162 
1163  // Let's loop through each connected live system and pull their information.
1164  $current_product = $this->getCurrentProduct();
1165  $current_prod_info = $this->getSuiteProductInfo($current_product);
1166  $connection_failed = FALSE;
1167 
1168  foreach ($all_products as $product) {
1169  if ($product['status'] !== 'live' || $product['sync_status'] === 'D') {
1170  continue;
1171  }
1172 
1173  // Send the connection token together.
1174  $current_prod_info['token'] = $product['token'];
1175 
1176  // Exclude the target product from the connected list.
1177  $target_excluded = array();
1178  foreach ($current_prod_info['connected'] as $p) {
1179  if ($p['suiteid'] !== $product['suiteid']) {
1180  $target_excluded[] = $p;
1181  }
1182  }
1183 
1184  $current_prod_info['connected'] = $target_excluded;
1185 
1186  $response = $this->sendMessage($product['suiteid'], 'suiteSyncProduct', $current_prod_info);
1187  $response = $this->getSuiteMessageResult($response);
1188 
1189  if ($response === FALSE) {
1190  $connection_failed = TRUE;
1191  continue;
1192  }
1193 
1194  // Passing deleted/added products via reference, and each function will modify directly
1195  $this->syncProductDetails($product, $response);
1196  $this->syncDeletedProducts($response['deleted'], $markedForDeletion, $markedForAddition);
1197  $this->syncConnectedProducts($product['suiteid'], $response['connected'], $markedForDeletion, $markedForAddition);
1198  }//end foreach
1199 
1200  // EVERY system has been processed successfully, move on to process sync status.
1201  if ($connection_failed === FALSE) {
1202  foreach ($markedForDeletion as $suiteid => $canBeRemoved) {
1203  if ($canBeRemoved === TRUE) {
1204  $this->removeProduct($suiteid);
1205  }
1206  }
1207 
1208  foreach ($markedForAddition as $suiteid => $canBeAdded) {
1209  if ($canBeAdded === TRUE) {
1210  $this->updateProduct($suiteid, 'sync_status', '');
1211  }
1212  }
1213  }
1214 
1215  }//end syncWithLiveProducts()
1216 
1217 
1227  public function syncProductDetails(array $product_info, array $new_product_info)
1228  {
1229  // Certificate or Name changes.
1230  if ($product_info['connection']['cert'] !== $new_product_info['cert'] || $product_info['connection']['name'] !== $new_product_info['name']) {
1231  $product_info['connection']['cert'] = $new_product_info['cert'];
1232  $product_info['connection']['name'] = $new_product_info['name'];
1233  $this->updateProduct($product_info['suiteid'], 'connection', $product_info['connection']);
1234  }
1235 
1236  // URL changes.
1237  if ($product_info['url'] !== $new_product_info['apiurl']) {
1238  $product_info['url'] = $new_product_info['apiurl'];
1239  $this->updateProduct($product_info['suiteid'], 'url', $product_info['url']);
1240  }
1241 
1242  }//end syncProductDetails()
1243 
1244 
1255  public function syncDeletedProducts(array $deleted, array &$markedForDeletion, array &$markedForAddition)
1256  {
1257  foreach ($deleted as $p) {
1258  $res = $this->getProductBySystemidURL($p['systemid'], $p['url']);
1259  if (empty($res) === TRUE) {
1260  // Deleted.
1261  continue;
1262  } else if ($res['sync_status'] === 'D') {
1263  // Also good, going to remove this one.
1264  continue;
1265  } else if ($res['status'] === 'live') {
1266  // Deleting this product.
1267  $this->removeProduct($res['suiteid']);
1268 
1269  // If just added, but marked for deleted.
1270  if (empty($markedForAddition) === FALSE && isset($markedForAddition[$p['suiteid']]) === TRUE) {
1271  unset($markedForAddition[$p['suiteid']]);
1272  }
1273  }
1274  }//end foreach
1275 
1276  }//end syncDeletedProducts()
1277 
1278 
1290  public function syncConnectedProducts($requesterSuiteid, array $connected, array &$markedForDeletion, array &$markedForAddtion)
1291  {
1292  $connectedIds = array();
1293  foreach ($connected as $p) {
1294 
1295  // Backward compatability code. For systems without the connection token.
1296  $url = '';
1297  if (isset($p['url']) === TRUE) {
1298  $url = $p['url'];
1299  } else if (isset($p['connection']['url']) === TRUE) {
1300  $url = $p['connection']['url'];
1301  }
1302 
1303  $res = $this->getProductBySystemidURL($p['systemid'], $url);
1304  if (empty($res) === TRUE) {
1305  // Hmmm ... I think I need to add this one.
1306  $suiteid = $this->registerProduct($p['systemid'], $p['type'], $url, $p['connection']);
1307  $this->updateProduct($suiteid, 'status', 'live');
1308  } else {
1309  $suiteid = $res['suiteid'];
1310  if ($res['sync_status'] === 'D') {
1311  // Added something, but system is marked for deletion.
1312  // Cannot remove the entry just yet.
1313  $markedForDeletion[$suiteid] = FALSE;
1314  } else if ($res['status'] !== 'live') {
1315  // Have the same system, the status is not live.
1316  $this->updateProduct($suiteid, 'status', 'live');
1317  }//end if
1318  }//end if
1319 
1320  $connectedIds[] = $suiteid;
1321  }//end foreach
1322 
1323  // Get all the systemids which have been added to this system,
1324  // but not existing in the other one.
1325  if (empty($markedForAddition) === FALSE) {
1326  $notAddedYet = array_diff(array_keys($markedForAddition), $connectedIds);
1327  foreach ($notAddedYet as $notAddedId) {
1328  if ((int) $requesterSuiteid !== (int) $notAddedId) {
1329  $markedForAddition[$notAddedId] = FALSE;
1330  }
1331  }
1332  }
1333 
1334  }//end syncConnectedProducts()
1335 
1336 
1345  public function getSuiteProductInfo(array $current_product)
1346  {
1347  $result = array(
1348  'systemid' => $current_product['systemid'],
1349  'type' => $current_product['type'],
1350  'apiurl' => $current_product['url'],
1351  'name' => $current_product['connection']['name'],
1352  'cert' => $current_product['connection']['cert'],
1353  'added' => array(),
1354  'deleted' => array(),
1355  'connected' => array(),
1356  );
1357 
1358  // Get the information about the connected systems.
1359  $suite_manager = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('suite_manager');
1360  $products = $suite_manager->getProducts(FALSE, TRUE, array('Squiz Update'));
1361  foreach ($products as $p) {
1362  if ($p['status'] !== 'live') {
1363  // Only send the message about the live systems.
1364  continue;
1365  }
1366 
1367  if ($p['sync_status'] === 'D') {
1368  $result['deleted'][] = $p;
1369  } else {
1370  $result['connected'][] = $p;
1371  }
1372  }
1373 
1374  return $result;
1375 
1376  }//end getSuiteProductInfo()
1377 
1378 
1387  public function getSuiteMessageResult($response)
1388  {
1389  if ($response['curlInfo']['http_code'] !== 200) {
1390  return FALSE;
1391  }
1392 
1393  $result = $response['result'];
1394  if (strpos($result, '<?xml') !== 0) {
1395  return FALSE;
1396  } else {
1397  $dom = new DOMDocument('1.0');
1398  $dom->loadXML($result);
1399 
1400  $responseDOM = $dom->getElementsByTagName('result')->item(0);
1401  $response = Suite_Manager::getResponseFromXMLDom($responseDOM);
1402  return $response;
1403  }
1404 
1405  }//end getSuiteMessageResult()
1406 
1407 
1420  public function sendCURLAPIRequest($url, $msg, $userAgent='')
1421  {
1422  // Handle encyption
1423  $msg = $this->encryptAPIData($url, $msg, $userAgent);
1424 
1425  $options = array(
1426  'POST' => 1,
1427  'POSTFIELDS' => $msg,
1428  'RETURNTRANSFER' => 1,
1429  );
1430  if (empty($userAgent) === FALSE) {
1431  $options['USERAGENT'] = $userAgent;
1432  }
1433 
1434  $headers = array('Expect:');
1435  $url = rtrim($url, '/');
1436  $details = fetch_url($url, $options, $headers, FALSE);
1437  if ($details['response'] === FALSE) {
1438  $errMsg = 'cURL failed:'.$details['errorstring'];
1439  log_dump($errMsg);
1440  }
1441 
1442  $result = Array(
1443  'result' => $details['response'],
1444  'curlInfo' => $details['curlinfo'],
1445  );
1446  return $result;
1447 
1448  }//end sendCURLAPIRequest()
1449 
1450 
1462  public function createSuiteMessageDOMTemplate($targetid)
1463  {
1464  $dom = new DomDocument('1.0', 'utf-8');
1465  $msgElem = $dom->createElement('message');
1466  $dom->appendChild($msgElem);
1467 
1468  // Get the product information about itself.
1469  $productInfo = $this->getCurrentProduct();
1470 
1471  // System and Action requested.
1472  $sysidEl = $dom->createElement('system_id');
1473  $systypeEl = $dom->createElement('system_type');
1474  $sysurlEl = $dom->createElement('system_url');
1475  $sysnameEl = $dom->createElement('system_name');
1476 
1477  if (!empty($productInfo)) {
1478  // Note that this system is identifying itself as the target system thinks who I am!
1479  $this->buildResponseXMLDom($sysidEl, $productInfo['systemid']);
1480  $this->buildResponseXMLDom($systypeEl, $productInfo['type']);
1481  $this->buildResponseXMLDom($sysurlEl, $productInfo['url']);
1482  $this->buildResponseXMLDom($sysnameEl, $productInfo['connection']['name']);
1483  }//end if
1484 
1485  $msgElem->appendChild($sysidEl);
1486  $msgElem->appendChild($systypeEl);
1487  $msgElem->appendChild($sysurlEl);
1488  $msgElem->appendChild($sysnameEl);
1489 
1490  // Add in the token.
1491  if (is_numeric($targetid)) {
1492  $product = $this->getProduct($targetid);
1493  $conTokenEl = $dom->createElement('conn_token');
1494  $this->buildResponseXMLDom($conTokenEl, $product['token']);
1495  $msgElem->appendChild($conTokenEl);
1496  }//end if
1497 
1498  return $dom;
1499 
1500  }//end createSuiteMessageDOMTemplate()
1501 
1502 
1513  private function _createSuiteMessageDOM(&$dom, $msgType, array $options=array())
1514  {
1515  $msgEl = $dom->getElementsByTagName('message')->item(0);
1516  $msgTypeEl = $dom->createElement('message_type');
1517 
1518  // Append message_type element.
1519  $this->buildResponseXMLDom($msgTypeEl, $msgType);
1520  $msgEl->appendChild($msgTypeEl);
1521 
1522  switch ($msgType) {
1523  case 'suiteConnect':
1524  case 'suiteConnectAck':
1525  case 'suiteConnectAckAck':
1526  // Both of CON_REQUEST and CON_REQUEST_ACK will send
1527  // the system's public key together.
1528  $currProduct = $this->getCurrentProduct();
1529  if (!empty($currProduct)) {
1530  $pubKey = $currProduct['connection']['cert'];
1531  $pubkeyEl = $dom->createElement('cert');
1532  $this->buildResponseXMLDom($pubkeyEl, $pubKey);
1533  $msgEl->appendChild($pubkeyEl);
1534  }//end if
1535  break;
1536 
1537  default:
1538  // No default behaviour.
1539  break;
1540  }//end switch
1541 
1542  if (empty($options) === FALSE) {
1543  $optionsEl = $dom->createElement('options');
1544  $this->buildResponseXMLDom($optionsEl, $options);
1545  $msgEl->appendChild($optionsEl);
1546  }
1547 
1548  return $dom;
1549 
1550  }//end _createSuiteMessageDOM()
1551 
1552 
1562  public function parseSuiteXML($xml, $fields=array())
1563  {
1564  $doc = new DomDocument();
1565  $result = $doc->loadXML($xml);
1566  if ($result === FALSE) {
1567  $errMsg = 'Failed to load the message XML.';
1568  throw new Exception($errMsg);
1569  }
1570 
1571  $result = Array();
1572  if (empty($fields)) {
1573  $fields = Array(
1574  'system_id',
1575  'system_url',
1576  'system_type',
1577  'system_name',
1578  'cert',
1579  );
1580  }
1581 
1582  foreach ($fields as $field) {
1583  $fieldEl = $doc->getElementsByTagName($field)->item(0);
1584  if ($fieldEl === NULL) {
1585  $result[$field] = NULL;
1586  } else {
1587  if ($fieldEl->nodeType === XML_ELEMENT_NODE) {
1588  $callback = Array(
1589  'Suite_Manager',
1590  'getResponseFromXMLDom',
1591  );
1592  $params = Array($fieldEl);
1593 
1594  $result[$field] = Suite_Manager::getResponseFromXMLDom($fieldEl);
1595  } else {
1596  $result[$field] = $fieldEl->nodeValue;
1597  }
1598  }
1599  }
1600 
1601  return $result;
1602 
1603  }//end parseSuiteXML()
1604 
1605 
1615  public function logReceivedMessage($systemid, $msgType)
1616  {
1617  /*
1618  E.g)
1619  [RECEIVED 02:23:18] CON_REQUEST from search (http://squiz-search.net) (172.293.283.281)
1620  [SENT 02:23:18] CON_REQUEST to search (http://squiz-search.net) (172.293.283.281)
1621  */
1622 
1623  include_once SQ_FUDGE_PATH.'/general/datetime.inc';
1624  $log = '[RECEIVED '.readable_datetime(time()).'] '.$msgType;
1625  $log .= ' from '.$systemid;
1626 
1627  $productInfo = $this->getProductBySystemid($systemid);
1628  if (empty($productInfo) === FALSE) {
1629  $log .= ' ('.$productInfo[0]['url'].')';
1630  if (isset($_SERVER['REMOTE_ADDR']) === TRUE) {
1631  $log .= ' ('.$_SERVER['REMOTE_ADDR'].')';
1632  }
1633  }
1634 
1635  $log .= "\n";
1636 
1637  file_put_contents($this->_getLogFilePath(), $log, FILE_APPEND);
1638 
1639  }//end logReceivedMessage()
1640 
1641 
1651  public function logSentMessage($systemid, $msgType)
1652  {
1653  /*
1654  E.g)
1655  [RECEIVED 02:23:18] CON_REQUEST from search (http://squiz-search.net) (172.293.283.281)
1656  [SENT 02:23:18] CON_REQUEST to search (http://squiz-search.net) (172.293.283.281)
1657  */
1658 
1659  include_once SQ_FUDGE_PATH.'/general/datetime.inc';
1660  $log = '[SENT '.readable_datetime(time()).'] '.$msgType;
1661  $log .= ' to '.$systemid;
1662  $productInfo = $this->getProductBySystemid($systemid);
1663  if (empty($productInfo) === FALSE) {
1664  $log .= ' ('.$productInfo[0]['url'].')';
1665  }
1666 
1667  $log .= "\n";
1668 
1669  file_put_contents($this->_getLogFilePath(), $log, FILE_APPEND);
1670 
1671  }//end logSentMessage()
1672 
1673 
1682  public function logErrorMessage($errorMessage='')
1683  {
1684  /*
1685  E.g)
1686  [RECEIVED 02:23:18] CON_REQUEST from search (http://squiz-search.net) (172.293.283.281)
1687  [SENT 02:23:18] CON_REQUEST to search (http://squiz-search.net) (172.293.283.281)
1688  */
1689 
1690  include_once SQ_FUDGE_PATH.'/general/datetime.inc';
1691  $log = '[ERROR '.readable_datetime(time()).'] '.$errorMessage;
1692  $log .= "\n";
1693 
1694  file_put_contents($this->_getLogFilePath(), $log, FILE_APPEND);
1695 
1696  }//end logErrorMessage()
1697 
1698 
1705  private function _getLogFilePath()
1706  {
1707  $logFilePath = $this->data_path;
1708  $logFilePath .= '/message_log.txt';
1709  return $logFilePath;
1710 
1711  }//end _getLogFilePath()
1712 
1713 
1728  public function encryptAPIData($url, array $msg, $userAgent)
1729  {
1730  if (isset($msg['_enc']) === TRUE && isset($msg['_pubKeySystemid']) === TRUE) {
1731  $destProduct = $this->getProduct($msg['_pubKeySystemid']);
1732  if (empty($destProduct) === TRUE) {
1733  return $msg;
1734  }
1735 
1736  if ($destProduct['status'] === 'live' && $destProduct['sync_status'] !== 'D' && empty($destProduct['token']) === FALSE) {
1737  // Token exists, send with encrypted message.
1738  $msg['_enc'] = array(
1739  '_token' => $destProduct['token'],
1740  '_data' => $msg['_enc'],
1741  );
1742  }//end if
1743 
1744  // JSON encode data, before encryption
1745  $msg['_enc'] = $this->encodeJson($msg['_enc']);
1746  $enc = $this->encryptData($msg['_pubKeySystemid'], $msg['_enc']);
1747 
1748  $msg['_enc'] = $enc['encrypted'];
1749  $msg['_password'] = $enc['password'];
1750  }
1751 
1752  return $msg;
1753 
1754  }//end encryptAPIData()
1755 
1756 
1763  public function decryptAPIData()
1764  {
1765  $encrypted = array_get_index($_POST, '_enc', NULL);
1766  $password = array_get_index($_POST, '_password', NULL);
1767  if ($encrypted === NULL || $password === NULL) {
1768  return;
1769  }
1770 
1771  $encrypted = $this->decryptData($encrypted, $password);
1772  if ($encrypted === FALSE) {
1773  $_POST['_enc'] = FALSE;
1774  } else {
1775  $token = NULL;
1776  $tokenFound = FALSE;
1777  $product = NULL;
1778  $encrypted = $this->decodeJson($encrypted);
1779  if (array_key_exists('_token', $encrypted) === TRUE && array_key_exists('_data', $encrypted) === TRUE) {
1780  $_POST['_enc'] = $encrypted['_data'];
1781  $token = $encrypted['_token'];
1782  $tokenFound = TRUE;
1783  } else {
1784  $_POST['_enc'] = $encrypted;
1785  }
1786 
1787  if ($tokenFound === TRUE && empty($token) === FALSE) {
1788  // Token based product. Way easy to find.
1789  $product = $this->getProductByToken($token);
1790  } else {
1791  // Old code found. Handle older versions.
1792  $systemid = NULL;
1793  if (is_array($encrypted) === TRUE) {
1794  $systemid = $encrypted['_systemid'];
1795  } else if (strpos($encrypted, '<?xml') === 0) {
1796  $systemid = $this->_getSystemidFromXML($xml);
1797  }//end if
1798 
1799  if ($systemid !== NULL) {
1800  $products = $this->getProductBySystemid($systemid);
1801  if (isset($products[0]) === TRUE) {
1802  $product = array_pop($products);
1803  }
1804  }
1805  }//end if
1806 
1807  if (empty($product) === FALSE && $product['status'] === 'live' && $product['sync_status'] !== 'D') {
1808  // This system is live and valid, therefore we can allow the system to act as superuser.
1809  $this->_suiteSystemUser = TRUE;
1810  }
1811  }//end if
1812 
1813  }//end decryptAPIData()
1814 
1815 
1823  private function _getSystemidFromXML($xml)
1824  {
1825  $systemid = NULL;
1826  if (strpos($xml, '<?xml') === 0) {
1827  $dom = new DomDocument();
1828  $dom->loadXML($xml);
1829  $systemidDom = $dom->getElementsByTagName('system_id')->item(0);
1830  $systemid = Suite_Manager::getResponseFromXMLDom($systemidDom);
1831  }//end if
1832 
1833  return $systemid;
1834 
1835  }//end _getSystemidFromXML()
1836 
1837 
1852  public function encryptData($suiteid, $data)
1853  {
1854  $encrypted = NULL;
1855  $product = $this->getProduct($suiteid);
1856  if (isset($product['connection']['cert']) === TRUE) {
1857  $certStr = $product['connection']['cert'];
1858  $pubKey = openssl_pkey_get_public($certStr);
1859  $password = sha1(microtime(TRUE));
1860  $encryptedMsg = $this->_opensslEncrypt($data, $this->_opensslEncryptionAlgorithm, $password);
1861  if ($encryptedMsg === FALSE) {
1862  return FALSE;
1863  }
1864 
1865  $success = openssl_public_encrypt($password, $encryptedPass, $pubKey);
1866  $encryptedPass = base64_encode($encryptedPass);
1867  if ($success === FALSE) {
1868  return FALSE;
1869  }
1870 
1871  $result = Array(
1872  'encrypted' => $encryptedMsg,
1873  'password' => $encryptedPass,
1874  );
1875  return $result;
1876  }//end if
1877 
1878  return FALSE;
1879 
1880  }//end encryptData()
1881 
1882 
1896  public function decryptData($data, $password)
1897  {
1898  // Get the current product information.
1899  $currProduct = $this->getCurrentProduct();
1900  $priKeyPath = $this->data_path.'/privatekey.pem';
1901  $priKey = openssl_pkey_get_private('file://'.$priKeyPath);
1902  $password = base64_decode($password);
1903 
1904  $decrypted = '';
1905  $result = openssl_private_decrypt(
1906  $password,
1907  $passdec,
1908  $priKey
1909  );
1910 
1911  if ($result === FALSE) {
1912  return FALSE;
1913  }
1914 
1915  $decrypted = $this->_opensslDecrypt($data, $this->_opensslEncryptionAlgorithm, $passdec);
1916  if ($decrypted === FALSE) {
1917  return FALSE;
1918  }
1919 
1920  return $decrypted;
1921 
1922  }//end decryptData()
1923 
1924 
1941  private function _opensslEncrypt($data, $algorithm, $password)
1942  {
1943  $filename = uniqid().'.txt';
1944  $filePath = $this->data_path.'/'.$filename;
1945  file_put_contents($filePath, $data);
1946 
1947  $command = '/usr/bin/openssl enc -'.$algorithm.' -a -salt -in '.$filePath;
1948  $command .= ' -out '.$filePath.'.enc -pass pass:'.$password;
1949  exec($command, $output, $returnVal);
1950  if ($returnVal === 0 && file_exists($filePath.'.enc') === TRUE) {
1951  $encrypted = file_get_contents($filePath.'.enc');
1952  unlink($filePath);
1953  unlink($filePath.'.enc');
1954  return $encrypted;
1955  }
1956 
1957  return FALSE;
1958 
1959  }//end _opensslEncrypt()
1960 
1961 
1978  private function _opensslDecrypt($data, $algorithm, $password)
1979  {
1980  $filename = uniqid().'.txt';
1981  $filePath = $this->data_path.'/'.$filename;
1982  file_put_contents($filePath, $data);
1983 
1984  $command = '/usr/bin/openssl enc -d -'.$algorithm.' -a -salt -in '.$filePath;
1985  $command .= ' -out '.$filePath.'.dec -pass pass:'.$password;
1986  exec($command, $output, $returnVal);
1987  if ($returnVal === 0 && file_exists($filePath.'.dec') === TRUE) {
1988  $encrypted = file_get_contents($filePath.'.dec');
1989  unlink($filePath);
1990  unlink($filePath.'.dec');
1991  return $encrypted;
1992  }
1993 
1994  return FALSE;
1995 
1996  }//end _opensslDecrypt()
1997 
1998 
2011  public function buildResponseXMLDom(&$parentNode, $content)
2012  {
2013  $type = '';
2014  if (is_scalar($content) === TRUE) {
2015  if (is_integer($content) === TRUE) {
2016  $type = 'integer';
2017  $textVal = $content.'';
2018  } else if (is_bool($content) === TRUE) {
2019  $type = 'boolean';
2020  $textVal = json_encode($content);
2021  } else if (is_string($content) === TRUE) {
2022  $type = 'string';
2023  $textVal = $content;
2024  } else if (is_float($content) === TRUE) {
2025  $type = 'float';
2026  $textVal = $content.'';
2027  }
2028 
2029  $parentNode->setAttribute('type', $type);
2030  $this->_addTextToNode($parentNode, $textVal);
2031  } else if (is_array($content) === TRUE) {
2032  $type = 'array';
2033 
2034  $keys = array_keys($content);
2035  $diff = array_diff_key($content, $keys);
2036  if (count($diff) !== 0) {
2037  $type = 'struct';
2038  }
2039 
2040  // Special case for base 64 code
2041  if (isset($content['_base64']) === TRUE) {
2042  $type = 'base64';
2043  }
2044 
2045  $parentNode->setAttribute('type', $type);
2046  switch ($type) {
2047  case 'array':
2048  foreach ($content as $val) {
2049  $valueElem = $parentNode->ownerDocument->createElement('value');
2050  $parentNode->appendChild($valueElem);
2051  $this->buildResponseXMLDom($valueElem, $val);
2052  }
2053  break;
2054 
2055  case 'struct':
2056  foreach ($content as $key => $val) {
2057  $keyElemTag = $key;
2058  if (is_numeric($key) === TRUE) {
2059  $keyElemTag = '_'.$key;
2060  }
2061 
2062  $keyElem = $parentNode->ownerDocument->createElement($keyElemTag);
2063  $parentNode->appendChild($keyElem);
2064  $this->buildResponseXMLDom($keyElem, $val);
2065  }
2066  break;
2067 
2068  case 'base64':
2069  foreach ($content as $key => $val) {
2070  if ($key === '_base64') {
2071  continue;
2072  }
2073 
2074  $parentNode->setAttribute($key, $val);
2075  }//end foreach
2076  $this->_addTextToNode($parentNode, $content['_base64'], TRUE);
2077  break;
2078 
2079  default:
2080  // No default behaviour.
2081  break;
2082  }//end switch
2083  }//end if
2084 
2085  }//end buildResponseXMLDom()
2086 
2087 
2095  public static function getResponseFromXMLDom($parentNode)
2096  {
2097  $response = '';
2098  if ($parentNode->hasAttribute('type') === TRUE) {
2099  $type = $parentNode->getAttribute('type');
2100  $func = '_get'.ucwords($type).'Response';
2101  $callback = Array(
2102  'Suite_Manager',
2103  $func,
2104  );
2105  if (is_callable($callback) === TRUE) {
2106  $params = Array($parentNode);
2107  $response = call_user_func_array($callback, $params);
2108  } else {
2109  $response = $parentNode->nodeValue;
2110  settype($response, $type);
2111  }
2112  }//end if
2113 
2114  return $response;
2115 
2116  }//end getResponseFromXMLDom()
2117 
2118 
2126  private static function _getArrayResponse($parentNode)
2127  {
2128  $response = Array();
2129  if ($parentNode->hasChildNodes() === TRUE) {
2130  foreach ($parentNode->childNodes as $child) {
2131  if ($child->nodeType === XML_ELEMENT_NODE) {
2132  $response[] = Suite_Manager::getResponseFromXMLDom($child);
2133  }
2134  }
2135  }
2136 
2137  return $response;
2138 
2139  }//end _getArrayResponse()
2140 
2141 
2149  private static function _getStructResponse($parentNode)
2150  {
2151  $response = Array();
2152  if ($parentNode->hasChildNodes() === TRUE) {
2153  foreach ($parentNode->childNodes as $child) {
2154  $key = $child->nodeName;
2155  if ($child->nodeType === XML_ELEMENT_NODE) {
2156  $response[$key] = Suite_Manager::getResponseFromXMLDom($child);
2157  }
2158  }
2159  }
2160 
2161  ksort($response);
2162 
2163  return $response;
2164 
2165  }//end _getStructResponse()
2166 
2167 
2175  private static function _getBase64Response($parentNode)
2176  {
2177  $response = Array();
2178  if ($parentNode->hasAttributes() === TRUE) {
2179  foreach ($parentNode->attributes as $attribute) {
2180  $name = $attribute->name;
2181  $value = $attribute->value;
2182  if ($name !== 'type') {
2183  $response[$name] = $value;
2184  }
2185  }
2186  }
2187 
2188  // Finally, add in the base64 content.
2189  $response['_base64'] = $parentNode->nodeValue;
2190  ksort($response);
2191 
2192  return $response;
2193 
2194  }//end _getBase64Response()
2195 
2196 
2210  private function _addTextToNode(&$node, $text, $force=FALSE)
2211  {
2212  if ($force) {
2213  $node->appendChild($node->ownerDocument->createCDATASection($text));
2214  } else {
2215  $invalidXMLChars = preg_match('/[&<>"\']/', $text);
2216  if ($invalidXMLChars === 1) {
2217  $node->appendChild($node->ownerDocument->createCDATASection($text));
2218  } else {
2219  $node->appendChild($node->ownerDocument->createTextNode($text));
2220  }
2221  }//end if
2222 
2223  }//end _addTextToNode()
2224 
2225 
2233  private function _sendHeader($response)
2234  {
2235  $headerString = 'HTTP/1.0 '.$response.' '.$this->_responseCode[$response];
2236  $replace = TRUE;
2237  header($headerString, $replace, $response);
2238 
2239  }//end _sendHeader()
2240 
2241 
2254  private function _sendResponse(array $responseContent, $outputFormat='xml')
2255  {
2256  $outputFormat = (string) $outputFormat;
2257  switch ($outputFormat) {
2258  case 'json':
2259  header('Content-type: application/json');
2260  $json = $this->_buildJsonString($responseContent);
2261 
2262  echo $json;
2263  break;
2264 
2265  case 'xml':
2266  default:
2267  header('Content-Type: text/xml; charset=utf-8');
2268  $dom = new DomDocument('1.0', 'utf-8');
2269  $rspElem = $dom->createElement('rsp');
2270  $dom->appendChild($rspElem);
2271 
2272  // System and Action requested.
2273  $fields = Array(
2274  'system',
2275  'action',
2276  'assetid',
2277  'new_token',
2278  'next_token',
2279  'exception',
2280  );
2281  foreach ($fields as $field) {
2282  if (array_key_exists($field, $responseContent) === TRUE) {
2283  $fieldElem = $dom->createElement($field);
2284  $this->buildResponseXMLDom($fieldElem, $responseContent[$field]);
2285  $rspElem->appendChild($fieldElem);
2286  }
2287  }
2288 
2289  if (array_key_exists('result', $responseContent) === TRUE) {
2290  $resultElem = $dom->createElement('result');
2291  $this->buildResponseXMLDom($resultElem, $responseContent['result']);
2292  $rspElem->appendChild($resultElem);
2293  }
2294 
2295  $xml = $dom->saveXML();
2296  echo $xml;
2297  break;
2298  }//end switch
2299 
2300  }//end _sendResponse()
2301 
2302 
2311  private function _buildJsonString($content)
2312  {
2313  $type = '';
2314  $result = '';
2315  if (is_scalar($content) === TRUE) {
2316  $result = $this->encodeJson($content);
2317  } else if ($content === NULL) {
2318  $result = 'null';
2319  } else if (is_array($content) === TRUE) {
2320  $type = 'array';
2321  $keys = array_keys($content);
2322  $diff = array_diff_key($content, $keys);
2323  if (count($diff) !== 0) {
2324  $type = 'struct';
2325  }
2326 
2327  $arr = array();
2328  switch ($type) {
2329  case 'struct':
2330  foreach ($content as $key => $val) {
2331  $arr[] = $this->encodeJson((string) $key).':'.$this->_buildJsonString($val);
2332  }//end foreach
2333 
2334  $result = '{'.implode(',', $arr).'}';
2335  break;
2336 
2337  case 'array':
2338  default:
2339  foreach ($content as $val) {
2340  $arr[] = $this->_buildJsonString($val);
2341  }//end foreach
2342 
2343  $result = '['.implode(',', $arr).']';
2344  break;
2345  }//end switch
2346  }//end if
2347 
2348  return $result;
2349 
2350  }//end _buildJsonString()
2351 
2352 
2353 }//end class
2354 
2355 ?>