Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
http_request.inc
1 <?php
17 require_once SQ_ATTRIBUTES_PATH.'/serialise/serialise.inc';
18 require_once SQ_ATTRIBUTES_PATH.'/option_list/option_list.inc';
19 
32 {
33  // Options.
34  private $_methods = Array(
35  'GET' => 'GET',
36  'POST' => 'POST',
37  'HEAD' => 'HEAD',
38  'PUT' => 'PUT',
39  'DELETE' => 'DELETE'
40  );
41 
42  private $_authentication_types = Array(
43  'NONE' => 'None',
44  'HTTP_Basic' => 'HTTP Basic',
45  'Matrix' => 'Matrix Cookie Passthrough',
46  'OAuth' => 'OAuth',
47  );
48 
49  private $_cache_options = Array(
50  'NEVER' => 'Never Cache',
51  'DEFAULT' => 'Use Default Expiry',
52  'HTTP' => 'Use HTTP Expiry Headers',
53  );
54 
55  private $_document_encoding = Array(
56  'Auto' => 'Auto Detect',
57  'ISO-8859-1' => 'ISO-8859-1',
58  'ISO-8859-15' => 'ISO-8859-15',
59  'CP866' => 'CP866',
60  'CP1251' => 'CP1251',
61  'CP1252' => 'CP1252',
62  'KOI8-R' => 'KOI8-R',
63  'BIG5' => 'BIG5',
64  'GB2312' => 'GB2312',
65  'BIG5-HKSCS' => 'BIG5-HKSCS',
66  'Shift_JIS' => 'Shift_JIS',
67  'EUC-JP' => 'EUC-JP',
68  'UTF-8' => 'UTF-8',
69  );
70 
71 
72  // Data.
73  protected $_res = Array(); // This is where everything lives.
74  protected $_config = Array(); // The initial config.
75  private $_keyword_replacements = Array(); // For replacements set from outside, to be replaced into URLs, headers, post body etc.
76  private $_urls_to_request = Array(); // The URLs that need fetching. Some indexes may be blank.
77  private $_headers = Array(); // The latest reponse headers (cURL callback).
78  private $_last_header = FALSE; // Flag to deal with separating sets of headers when following redirects.
79 
80 
87  function __construct($attribute = 0, $value = NULL)
88  {
89  parent::__construct($attribute, $value);
90 
91  }//end constructor
92 
93 
103  public function paint($prefix, $read_only = FALSE)
104  {
105  $prefix = str_replace(':', '_', $prefix);
106  $current_value = @unserialize($this->value);
107 
108  // Set default values.
109  if (!is_array($current_value)) $current_value = Array();
110  if (!isset($current_value['urls'])) $current_value['urls'] = Array();
111  if (!isset($current_value['method'])) $current_value['method'] = 'GET';
112  if (!isset($current_value['timeout'])) $current_value['timeout'] = 10;
113  if (!isset($current_value['follow_redirect'])) $current_value['follow_redirect'] = TRUE;
114  if (!isset($current_value['disable_ssl_verify'])) $current_value['disable_ssl_verify'] = FALSE;
115  if (!isset($current_value['cache_options'])) $current_value['cache_options'] = 'HTTP';
116  if (!isset($current_value['cache_post_requests'])) $current_value['cache_post_requests'] = FALSE;
117  if (!isset($current_value['default_cache_expiry'])) $current_value['default_cache_expiry'] = 60;
118  if (!isset($current_value['authentication_type'])) $current_value['authentication_type'] = 'NONE';
119  if (!isset($current_value['http_auth_options'])) $current_value['http_auth_options'] = Array('username' => '', 'password' => '');
120  if (!isset($current_value['request_headers'])) $current_value['request_headers'] = Array();
121  if (!isset($current_value['request_body'])) $current_value['request_body'] = '';
122  if (!isset($current_value['convert_to_utf8'])) $current_value['convert_to_utf8'] = FALSE;
123  if (!isset($current_value['append_get_vars'])) $current_value['append_get_vars'] = FALSE;
124  if (!isset($current_value['forward_user_keys'])) $current_value['forward_user_keys'] = FALSE;
125  if (!isset($current_value['document_encoding'])) $current_value['document_encoding'] = 'Auto';
126  if (!isset($current_value['run_test'])) $current_value['run_test'] = FALSE;
127  if (!isset($current_value['oauth_asset']['assetid'])) $current_value['oauth_asset'] = Array('assetid' => '0', 'url' => '', 'linkid' => '');
128  ?>
129 
130  <table border="0" class="sq-backend-table">
131  <tr>
132  <th width="20%"><?php echo translate('http_request_method'); ?></th>
133  <td>
134  <?php
135  if ($read_only) {
136  echo htmlspecialchars($current_value['method']);
137  } else {
138  combo_box($prefix.'_method', $this->_methods, FALSE, $current_value['method']);
139  }
140  ?>
141  </td>
142  </tr>
143  <?php
144  if ($current_value['method'] == 'POST') {
145  ?>
146  <tr>
147  <th><?php echo translate('http_request_cache_post_requests'); ?></th>
148  <td>
149  <?php
150  if ($read_only) {
151  echo htmlspecialchars($current_value['cache_post_requests'] ? 'Yes' : 'No');
152  } else {
153  check_box($prefix.'_cache_post_requests', 1, $current_value['cache_post_requests']);
154  }
155  ?>
156  </td>
157  </tr>
158  <?php
159  }
160  ?>
161  <tr>
162  <th><?php echo translate('http_request_urls'); ?></th>
163  <td>
164  <?php
165  $ol = new Asset_Attribute_Option_List();
166  $ol->value = implode($ol->delimiter, $current_value['urls']);
167  $ol->setEditParam('width', '70');
168  $ol->paint($prefix.'_urls', $read_only);
169  ?>
170  </td>
171  </tr>
172  <tr>
173  <th><?php echo translate('http_request_timeout'); ?></th>
174  <td>
175  <?php
176  if ($read_only) {
177  echo htmlspecialchars($current_value['timeout']);
178  } else {
179  text_box($prefix.'_timeout', $current_value['timeout'], 4);
180  }
181  ?>
182  </td>
183  </tr>
184  <tr>
185  <th><?php echo translate('http_request_follow_redirect'); ?></th>
186  <td>
187  <?php
188  if ($read_only) {
189  echo htmlspecialchars($current_value['follow_redirect'] ? 'Yes' : 'No');
190  } else {
191  check_box($prefix.'_follow_redirect', 1, $current_value['follow_redirect']);
192  }
193  ?>
194  </td>
195  </tr>
196  <tr>
197  <th><?php echo translate('http_request_disable_ssl_verify'); ?></th>
198  <td>
199  <?php
200  if ($read_only) {
201  echo htmlspecialchars($current_value['disable_ssl_verify'] ? 'Yes' : 'No');
202  } else {
203  check_box($prefix.'_disable_ssl_verify', 1, $current_value['disable_ssl_verify']);
204  }
205  ?>
206  </td>
207  </tr>
208  <tr>
209  <tr>
210  <th><?php echo translate('http_request_cache_options'); ?></th>
211  <td>
212  <?php
213  if ($read_only) {
214  echo htmlspecialchars($this->_cache_options[$current_value['cache_options']]);
215  } else {
216  combo_box($prefix.'_cache_options', $this->_cache_options, FALSE, $current_value['cache_options']);
217  }
218 
219  ?>
220  </td>
221  </tr>
222  <tr>
223  <th><?php echo translate('http_request_default_cache_expiry'); ?></th>
224  <td>
225  <?php
226  if ($read_only) {
227  echo htmlspecialchars($current_value['default_cache_expiry']);
228  } else {
229  text_box($prefix.'_default_cache_expiry', $current_value['default_cache_expiry'], 7);
230  }
231  ?>
232  </td>
233  </tr>
234  <tr>
235  <th><?php echo translate('http_request_authentication_type'); ?></th>
236  <td>
237  <?php
238  if ($read_only) {
239  echo htmlspecialchars($this->_authentication_types[$current_value['authentication_type']]);
240  } else {
241  combo_box($prefix.'_authentication_type', $this->_authentication_types, FALSE, $current_value['authentication_type']);
242 
243  switch ($current_value['authentication_type']) {
244  case 'HTTP_Basic':
245  ?>
246  <table>
247  <tr>
248  <th><?php echo translate('username'); ?></th>
249  <td>
250  <?php
251  text_box($prefix.'_http_auth_user', $current_value['http_auth_options']['username'], '', '', 'autocomplete="off"');
252  ?>
253  </td>
254  </tr>
255  <tr>
256  <th><?php echo translate('password'); ?></th>
257  <td>
258  <?php
259  password_box($prefix.'_http_auth_pass', $current_value['http_auth_options']['password'], '', '', 'autocomplete="off"');
260  ?>
261  </td>
262  </tr>
263  </table>
264  <?php
265  break;
266  case 'Matrix':
267  ?>
268  <span>Note: This requires the global setting <strong>Allow IP Change</strong></span>
269  <?php
270  break;
271  case 'OAuth':
272  $type_codes = Array(
273  'page_rest_resource_oauth_session' => 'D',
274  'page_rest_resource_oauth_two_legged' => 'D',
275  );
276  ?>
277  <table>
278  <tr>
279  <th><?php translate('oauth_asset'); ?></th>
280  <td>
281  <?php
282  asset_finder($prefix.'_oauth_asset', $current_value['oauth_asset']['assetid'], $type_codes);
283  ?>
284  </td>
285  </tr>
286  </table>
287  <?php
288  break;
289  }
290  }
291  ?>
292  </td>
293  </tr>
294  <tr>
295  <th><?php echo translate('http_request_request_headers'); ?></th>
296  <td>
297  <?php
298  $hl = new Asset_Attribute_Option_List();
299  $hl->value = implode($hl->delimiter, $current_value['request_headers']);
300  $hl->setEditParam('width', '60');
301  $hl->paint($prefix.'_request_headers', $read_only);
302  ?>
303  </td>
304  </tr>
305  <?php
306  if (($current_value['method'] == 'POST') || ($current_value['method'] == 'PUT')) {
307  ?>
308  <tr>
309  <th><?php echo translate('http_request_request_body'); ?></th>
310  <td>
311  <?php
312  if ($read_only) {
313  echo htmlspecialchars($current_value['request_body']);
314  } else {
315  text_area($prefix.'_request_body', $current_value['request_body'], 70, 20, 0, 'style="font-family: monospace;"');
316  }
317  ?>
318  </td>
319  </tr>
320  <?php
321  } else {
322  hidden_field($prefix.'_request_body', $current_value['request_body']);
323  }
324  ?>
325  <tr>
326  <th><?php echo translate('http_request_convert_to_utf8'); ?></th>
327  <td>
328  <?php
329  if ($read_only) {
330  echo $current_value['convert_to_utf8'] ? 'Yes' : 'No';
331  } else {
332  check_box($prefix.'_convert_to_utf8', 1, $current_value['convert_to_utf8']);
333  }
334  ?>
335  </td>
336  </tr>
337  <?php
338  if ($current_value['convert_to_utf8'] == TRUE) {
339  ?>
340  <tr>
341  <th><?php echo translate('http_request_document_encoding'); ?></th>
342  <td>
343  <?php
344  if ($read_only) {
345  echo $this->_document_encoding[$current_value['document_encoding']];
346  } else {
347  echo combo_box($prefix.'_document_encoding', $this->_document_encoding, FALSE, $current_value['document_encoding']);
348  }
349  ?>
350  </td>
351  </tr>
352  <?php
353  }
354  ?>
355  <tr>
356  <th><?php echo translate('http_request_append_get_vars'); ?></th>
357  <td>
358  <?php
359  if ($read_only) {
360  echo $current_value['append_get_vars'] ? 'Yes' : 'No';
361  } else {
362  check_box($prefix.'_append_get_vars', 1, $current_value['append_get_vars']);
363  }
364  ?>
365  </td>
366  </tr>
367  <tr>
368  <th><?php echo translate('http_request_forward_user_keys'); ?></th>
369  <td>
370  <?php
371  if ($read_only) {
372  echo $current_value['forward_user_keys'] ? 'Yes' : 'No';
373  } else {
374  check_box($prefix.'_forward_user_keys', 1, $current_value['forward_user_keys']);
375  }
376  ?>
377  </td>
378  </tr
379  <tr>
380  <th><?php echo translate('http_request_run_test'); ?></th>
381  <td>
382  <?php
383  if ($read_only) {
384  echo $current_value['run_test'] ? 'Yes' : 'No';
385  } else {
386  check_box($prefix.'_run_test', 1, 0);
387 
388  // Run the test?
389  if ($current_value['run_test'] == 1) {
390  $res = $this->run();
391 
392  if (!empty($res) || $res['responses'] != '' ) {
393  // Truncate the bodies to be nice..
394  foreach ($res['responses'] as &$response) {
395  if (strlen($response['body']) > 50) {
396  $response['body'] = substr($response['body'], 0, 50) . '...';
397  }
398  }
399  }
400 
401  // This is not a debug statement.
402  pre_echo($res);
403  }
404 
405  }
406  ?>
407  </td>
408  </tr>
409  </table>
410  <?php
411 
412  }//end paint()
413 
414 
423  public function process($prefix)
424  {
425  $prefix = str_replace(':', '_', $prefix);
426 
427  $value = Array();
428 
429  if (!isset($_REQUEST[$prefix.'_method'])) return FALSE;
430 
431  // Assign values
432  $value['method'] = (isset($_REQUEST[$prefix.'_method'])) ? $_REQUEST[$prefix.'_method'] : '';
433 
434  $ol = new Asset_Attribute_Option_List();
435  $ol->process($prefix.'_urls');
436  $value['urls'] = trim($ol->value) ? explode($ol->delimiter, $ol->value) : NULL;
437 
438  $value['timeout'] = (isset($_REQUEST[$prefix.'_timeout'])) ? $_REQUEST[$prefix.'_timeout'] : 0;
439  $value['follow_redirect'] = (isset($_REQUEST[$prefix.'_follow_redirect'])) ? $_REQUEST[$prefix.'_follow_redirect'] : FALSE;
440  $value['disable_ssl_verify'] = (isset($_REQUEST[$prefix.'_disable_ssl_verify'])) ? $_REQUEST[$prefix.'_disable_ssl_verify'] : FALSE;
441  $value['cache_options'] = (isset($_REQUEST[$prefix.'_cache_options'])) ? $_REQUEST[$prefix.'_cache_options'] : 'HTTP';
442  $value['cache_post_requests'] = (isset($_REQUEST[$prefix.'_cache_post_requests'])) ? $_REQUEST[$prefix.'_cache_post_requests'] : FALSE;
443  $value['default_cache_expiry'] = (isset($_REQUEST[$prefix.'_default_cache_expiry'])) ? $_REQUEST[$prefix.'_default_cache_expiry'] : 60;
444  $value['authentication_type'] = (isset($_REQUEST[$prefix.'_authentication_type'])) ? $_REQUEST[$prefix.'_authentication_type'] : '';
445  $value['request_body'] = (isset($_REQUEST[$prefix.'_request_body'])) ? trim($_REQUEST[$prefix.'_request_body']) : '';
446 
447  $hl = new Asset_Attribute_Option_List();
448  $hl->process($prefix.'_request_headers');
449  $value['request_headers'] = trim($hl->value) ? explode($hl->delimiter, $hl->value) : NULL;
450 
451  $value['convert_to_utf8'] = (isset($_REQUEST[$prefix.'_convert_to_utf8'])) ? $_REQUEST[$prefix.'_convert_to_utf8'] : FALSE;
452  $value['append_get_vars'] = (isset($_REQUEST[$prefix.'_append_get_vars'])) ? $_REQUEST[$prefix.'_append_get_vars'] : FALSE;
453  $value['forward_user_keys'] = (isset($_REQUEST[$prefix.'_forward_user_keys'])) ? $_REQUEST[$prefix.'_forward_user_keys'] : FALSE;
454  $value['document_encoding'] = (isset($_REQUEST[$prefix.'_document_encoding'])) ? $_REQUEST[$prefix.'_document_encoding'] : 'Auto';
455  $value['run_test'] = (isset($_REQUEST[$prefix.'_run_test'])) ? trim($_REQUEST[$prefix.'_run_test']) : FALSE;
456  $value['http_auth_options']['username'] = (isset($_REQUEST[$prefix.'_http_auth_user'])) ? $_REQUEST[$prefix.'_http_auth_user'] : '';
457  $value['http_auth_options']['password'] = (isset($_REQUEST[$prefix.'_http_auth_pass'])) ? $_REQUEST[$prefix.'_http_auth_pass'] : '';
458 
459  $value['oauth_asset'] = (isset($_REQUEST[$prefix.'_oauth_asset'])) ? $_REQUEST[$prefix.'_oauth_asset'] : '';
460 
461  $this->processed = $this->setValue($value);
462 
463  }//end process()
464 
465 
474  public function setKeywordReplacements($replacements)
475  {
476  $this->_keyword_replacements = $replacements;
477 
478  }//end setKeywordReplacements()
479 
480 
487  public function run()
488  {
489  $this->_config = @unserialize($this->value);
490  if (empty($this->_config)) return;
491  if ($this->_config['authentication_type'] == 'OAuth' && $this->_config['oauth_asset']['assetid'] == 0) return;
492 
493  // Build the result data structure. This will be returned eventually, and is also used to form the cache keys.
494  $this->_res = Array(
495  'request' => Array(
496  'method' => $this->_config['method'],
497  'headers' => count($this->_config['request_headers']) ? $this->_config['request_headers'] : Array(),
498  'body' => $this->_config['request_body'],
499  'urls' => count($this->_config['urls']) ? $this->_config['urls'] : Array(),
500  'auth' => Array(
501  'type' => $this->_config['authentication_type'],
502  ),
503  ),
504  'responses' => Array(),
505  'response' => Array(),
506  );
507 
508  // Do nothing if there are no URLs.
509  if (count($this->_res['request']['urls']) < 1) return $this->_res;
510 
511  // Replace keywords in each URL.
512  foreach ($this->_res['request']['urls'] as &$url) {
513  $this->_replaceKeywords($url);
514  }
515 
516  if ($this->_config['append_get_vars']) {
517  $query_vars = $collection_var = '';
518  foreach ($_GET as $query => $param) {
519  $param = str_replace(' ', '+', $param);
520  if ($query == 'collection') {
521  $collection_var .= $query.'='.$param;
522  continue;
523  }
524  $prefix = empty($query_vars) ? '' : '&';
525  $query_vars .= $prefix.$query.'='.$param;
526 
527  }
528  if(!empty($query_vars) || !empty($collection_var)) {
529  foreach ($this->_res['request']['urls'] as $index => $url) {
530  if (strpos($url, '?') === FALSE) $url .= '?';
531  if (strpos($url, 'collection=') === FALSE) $url .= $collection_var;
532  $prefix = empty($collection_var) ? '' : '&';
533  $this->_res['request']['urls'][$index] = $url.$prefix.$query_vars;
534  }
535  }
536  }
537 
538  // if we are configured to forward the user information
539  // send the user ip address and also the user keys
540  if ($this->_config['forward_user_keys']) {
541  $user_info = Array($GLOBALS['SQ_SYSTEM']->user->id);
542  $user_info = array_merge($GLOBALS['SQ_SYSTEM']->user->getUserGroups(), $user_info);
543  $user_keys = 'userkeys='.implode(',', $user_info);
544  foreach ($this->_res['request']['urls'] as $index => $url) {
545  $url .= (strpos($url, '?') === FALSE) ? '?' : '&';
546  $this->_res['request']['urls'][$index] = $url.$user_keys;
547  }
548  // also forward the ip the user request is coming from
549  $this->_res['request']['headers'][] = 'X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'];
550  }
551 
552  // Replace keywords in each Header.
553  for ($i = 0; $i < count($this->_res['request']['headers']); $i++) {
554  $this->_replaceKeywords($this->_res['request']['headers'][$i]);
555 
556  // Discard badly formatted headers.
557  if (preg_match('/(.+):(.+)/', $this->_res['request']['headers'][$i]) == 0) {
558  unset($this->_res['request']['headers'][$i]);
559  }
560  }
561 
562  // Replace keywords in the body.
563  $this->_replaceKeywords($this->_res['request']['body']);
564 
565  // Auth options.
566  switch ($this->_res['request']['auth']['type']) {
567  case 'HTTP_Basic':
568  $this->_res['request']['auth']['http_auth_options'] = $this->_config['http_auth_options'];
569  $this->_replaceKeywords($this->_res['request']['auth']['http_auth_options']['username']);
570  $this->_replaceKeywords($this->_res['request']['auth']['http_auth_options']['password']);
571  break;
572 
573  case 'OAuth':
574  // at this point, the oauth asset needs to have access token available...otherwise no data will be received back...
575  $oauth_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($this->_config['oauth_asset']['assetid'], '', TRUE);
576  if (!is_null($oauth_asset)) {
577  $args = Array();
578  $args['method'] = $this->_res['request']['method'];
579  $args['request_headers'] = $this->_res['request']['headers'];
580  $args['request_body'] =$this->_res['request']['body'];
581  $this->_res['request']['oauth_headers'] = $oauth_asset->getUserDataRequestAuthHeaders($this->_res['request']['urls'], $args);
582  }
583 
584  // Other auth types here.
585  }
586 
587  // Go!
588  $this->_performRequests();
589 
590  // encode responses if necessary
591  if (isset($this->_config['convert_to_utf8']) && ($this->_config['convert_to_utf8'])) {
592  $doc_encoding = $this->_config['document_encoding'];
593 
594  // UTF-8 is not supported yet. this option is going to be used when Entity conversion is supported.
595  // as a temporary solution, do nothing if UTF-8 is selected
596  if ($doc_encoding != 'UTF-8') {
597  foreach ($this->_res['responses'] as $index => $response) {
598 
599  // if the document encoding option is Auto, try to find the encoding from the responses
600  if ($doc_encoding == 'Auto') {
601  $doc_encoding = $this->_findDocumentEncoding($response);
602  }
603 
604  // if the document encoding is supported and it is not UTF-8, convert the encoding
605  // ToDo: when http entity conversion is supported, UTF-8 will also need to be converted
606  if (strlen($doc_encoding) > 0 && array_get_index($this->_document_encoding, $doc_encoding) && $doc_encoding != 'UTF-8') {
607  $this->_res['responses'][$index]['body'] = iconv($doc_encoding, 'UTF-8', $response['body']);
608  }
609  }
610  }
611  }
612 
613  // Set url to the first in urls
614  $this->_res['request']['url'] = &$this->_res['request']['urls'][0];
615 
616  // Set the first in responses to response
617  $this->_res['response'] =& $this->_res['responses'][0];
618 
619  return $this->_res;
620 
621  }//end run()
622 
623 
631  protected function _findDocumentEncoding($response)
632  {
633  $charset = '';
634 
635  // try finding the document encoding from the header first
636  $content_type_info = explode(';', $response['info']['content_type']);
637  if (isset($content_type_info[0])) {
638  $content_type = $content_type_info[0];
639 
640  // look for encoding only when the content type is text/html
641  if (strcasecmp($content_type, 'text/html') == 0) {
642  // if the encoding is set in the header, great! let's use it!
643  if (isset($content_type_info[1]) && substr(trim($content_type_info[1]), 0, strlen('charset=')) == 'charset=') {
644  $charset = substr(trim($content_type_info[1]), strlen('charset='));
645  } else {
646  // if the encoding is not in the header, need to look into the body to see if there is any encoding set...
647  preg_match_all("|<meta http-equiv=[^>]+content=[\"\']text/html;[\s]?charset=[^>]+/>|U", $response['body'], $matches);
648  if (isset($matches[0][0])) {
649  $charset = preg_replace("/.*charset=/i", "", $matches[0][0]);
650  $charset = trim(str_replace(Array("/", ">", "\""), "", $charset));
651  }
652  }
653  }
654  }
655  return strtoupper($charset);
656 
657  }//end _findDocumentEncoding()
658 
659 
667  protected function _replaceKeywords(&$text)
668  {
669  if (strlen($text)) {
670  replace_keywords($text, $this->_keyword_replacements);
671  replace_global_keywords($text);
672  }
673 
674  }//end _replaceKeywords()
675 
676 
683  protected function _performRequests()
684  {
685  if ($this->_canCacheResponses()) {
686  $cm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('cache_manager');
687  $rm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('rest_manager');
688 
689  // For each URL, we must create a serialized object of the request combined with the URL
690  // to make a unique cache key for this request.
691  // This way, a URL with the same settings will be a cache hit even if it was requested somewhere
692  // else as part of a different set of requests.
693  for ($key = 0; $key < count($this->_res['request']['urls']); $key++) {
694  $url = $this->_res['request']['urls'][$key];
695 
696  // We'll need a new request structure for each url
697  $current_request = $this->_res['request'];
698  $current_request['urls'] = NULL;
699  $current_request['current_url'] = $url;
700  $cache_key = serialize($current_request);
701 
702  // Attempt to load from cache.
703  // We pin the cache entries against the REST Manager,
704  // so that they can be shared across multiple assets.
705  $cache =& $cm->loadFromCache($rm->id, 'rest_manager', $cache_key, FALSE);
706 
707  if ($cache !== FALSE) {
708  // HIT!
709  $this->_res['responses'][$key] = unserialize($cache);
710  $this->_res['responses'][$key]['source'] = 'cache';
711  } else {
712  // MISS!
713  $this->_res['responses'][$key] = Array();
714  $this->_curlAddUrl($url, $key);
715  }
716  }
717  } else {
718  // No cacheing, so get everything.
719  for ($key = 0; $key < count($this->_res['request']['urls']); $key++) {
720  $this->_res['responses'][$key] = Array();
721  $this->_curlAddUrl($this->_res['request']['urls'][$key], $key);
722  }
723  }
724 
725  // Go!
726  $this->_curlExec();
727 
728  }//end _performRequests()
729 
730 
737  protected function _canCacheResponses()
738  {
739  switch ($this->_config['cache_options']) {
740  case 'NEVER':
741  return FALSE;
742  case 'HTTP':
743  case 'DEFAULT':
744  switch ($this->_res['request']['method']) {
745  case 'GET':
746  return TRUE;
747  case 'POST':
748  return $this->_config['cache_post_requests'];
749  default:
750  return FALSE;
751  }
752  }
753 
754  }//end _canCacheResponse()
755 
756 
763  protected function _curlAddUrl($url, $key)
764  {
765  $ch = curl_init();
766 
767  // oauth requires a unique auth header for each url
768  if (isset($this->_res['request']['oauth_headers'])) {
769  if (isset($this->_res['request']['oauth_headers'][$url])) {
770  $this->_res['request']['headers'][] = $this->_res['request']['oauth_headers'][$url];
771  }
772  }
773 
774  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
775  curl_setopt($ch, CURLOPT_URL, $url);
776  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $this->_config['follow_redirect']);
777  curl_setopt($ch, CURLOPT_TIMEOUT, $this->_config['timeout']);
778  curl_setopt($ch, CURLOPT_HEADERFUNCTION, Array($this, '_readCurlHeaders'));
779  curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_res['request']['headers']);
780 
781  // Use proxy if set
782  if (SQ_PA_ENABLED) {
783  $proxy = get_proxy_info_for_url($url);
784  if (!empty($proxy['host'])) {
785  curl_setopt($ch, CURLOPT_PROXY, $proxy['host']);
786  curl_setopt($ch, CURLOPT_PROXYPORT, $proxy['port']);
787  if (!empty($proxy['user'])) {
788  curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy['user'] . ':' . $proxy['password']);
789  }
790  }
791  }
792 
793  if (isset($this->_config['disable_ssl_verify']) && $this->_config['disable_ssl_verify']) {
794  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
795  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
796  }
797 
798  // Determine the HTTP method
799  switch ($this->_res['request']['method']) {
800  case 'GET':
801  curl_setopt($ch, CURLOPT_HTTPGET, 1);
802  break;
803  case 'HEAD':
804  curl_setopt($ch, CURLOPT_HTTPGET, 1);
805  curl_setopt($ch, CURLOPT_NOBODY, 1);
806  break;
807  case 'POST':
808  curl_setopt($ch, CURLOPT_POST, 1);
809  curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_res['request']['body']);
810  break;
811  case 'PUT':
812  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
813  curl_setopt($ch, CURLOPT_POSTFIELDS, $this->_res['request']['body']);
814  break;
815  case 'DELETE':
816  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
817  break;
818  }
819 
820  // Set authentication if required.
821  switch ($this->_res['request']['auth']['type']) {
822  case "HTTP_Basic":
823  curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
824  curl_setopt($ch, CURLOPT_USERPWD, $this->_res['request']['auth']['http_auth_options']['username'].':'.$this->_res['request']['auth']['http_auth_options']['password']);
825  break;
826  case "Matrix":
827  curl_setopt($ch, CURLOPT_COOKIE, 'SQ_SYSTEM_SESSION=' . $_COOKIE['SQ_SYSTEM_SESSION']);
828  curl_setopt($ch,CURLOPT_COOKIESESSION,true);
829  session_write_close();
830  break;
831  }
832 
833  $this->_urls_to_request[$key] = Array('handle' => $ch, 'url' => $url);
834 
835  }//end _curlAddUrl()
836 
837 
843  protected function _curlExec()
844  {
845  // Loop over *all* URLs to make sure keys are synced. We only do stuff for the entries in _urls_to_request
846  for ($key = 0; $key < count($this->_res['request']['urls']); $key++) {
847 
848  // There might be nothing to do for this URL index (perhaps indexes 0 and 1 are cached but 2 isn't)
849  if (!isset($this->_urls_to_request[$key]['url'])) continue;
850 
851  $response =& $this->_res['responses'][$key];
852 
853  // Populate this response.
854  $response['body'] = curl_exec($this->_urls_to_request[$key]['handle']);
855  $response['headers'] = $this->_headers;
856  $response['info'] = curl_getinfo($this->_urls_to_request[$key]['handle']);
857  $response['source'] = 'remote';
858 
859  // Display/log warning message if request failed
860  if ($response['body'] === FALSE) {
861  trigger_error("cURL request failed: '" . $this->_urls_to_request[$key]['url'] . "': " . curl_error($this->_urls_to_request[$key]['handle']), E_USER_WARNING);
862  }
863 
864  // Clean up.
865  curl_close($this->_urls_to_request[$key]['handle']);
866  $this->_headers = Array();
867 
868  // restart session which was closed previously
869  if ($this->_res['request']['auth']['type'] === 'Matrix')
870  session_start();
871 
872  // Attempt to cache this response.
873  if ($this->_canCacheResponses()) {
874  $this->_setCacheExpiry($response);
875 
876  // Only cache if we have a valid expiry (passing 0 tells the cache manager to use asset type expiry).
877  if ($response['expiry'] > 0) {
878 
879  // We'll need a new request structure for each url, for unique cache keys.
880  $current_request = $this->_res['request'];
881  $current_request['urls'] = NULL;
882  $current_request['current_url'] = $this->_urls_to_request[$key]['url'];
883  $cache_key = serialize($current_request);
884 
885  // Save to cache.
886  $cm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('cache_manager');
887  $rm = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('rest_manager');
888  $cm->saveToCache($rm->id, 'rest_manager', $cache_key, serialize($response), FALSE, $response['expiry']);
889  }
890  }
891  }
892 
893  // Reset now that we've finished, since this can be run twice by paint layouts and the like.
894  $this->_urls_to_request = Array();
895 
896  }//end _curlExec()
897 
898 
909  protected function _setCacheExpiry(&$response)
910  {
911  // Only HTTP and DEFAULT are possible..
912  switch ($this->_config['cache_options']) {
913  case 'HTTP':
914 
915  // Use HTTP headers to control cacheing behaviour.
916 
917  $cache_control = isset($response['headers']['Cache-Control']) ? $response['headers']['Cache-Control'] : NULL;
918  $pragma = isset($response['headers']['Pragma']) ? $response['headers']['Pragma'] : NULL;
919  $expires = isset($response['headers']['Expires']) ? $response['headers']['Expires'] : NULL;
920 
921  // Cache-Control directives take priority over Expires..
922  if ((!is_null($cache_control)) || (!is_null($pragma))) {
923 
924  // For HTTP 1.0 servers.
925  if ($pragma == 'no-cache') {
926  $response['expiry'] = 0;
927  }
928 
929  // Cache-Control directives can be comma separated.
930  $cache_directives = explode(',', $cache_control);
931  foreach ($cache_directives as $directive) {
932 
933  // Directives may contain an extension, with a value.
934  $cache_ext = explode('=', $directive);
935 
936  // no-cache and equivalents override max-age
937 
938  switch (strtolower(trim($cache_ext[0]))) {
939  case 'no-cache':
940  case 'must-revalidate':
941  case 'no-store':
942  case 'private':
943  $response['expiry'] = 0;
944  break;
945  case 's-maxage':
946  // s-maxage overrides max-age
947  $response['expiry'] = isset($cache_ext[1]) ? (int) $cache_ext[1] : NULL;
948  break;
949  case 'max-age':
950  $response['expiry'] = isset($cache_ext[1]) ? (int) $cache_ext[1] : NULL;
951  break;
952  }
953  }
954 
955  } elseif (!is_null($expires)) {
956  // Parse date from the Expires header..
957  $time = (strtotime($expires) - time());
958  if ($time < 0) $time = 0;
959  $response['expiry'] = $time;
960  }
961  // If we still don't have an expiry.. then we can't cache.
962  if (empty($response['expiry'])) {
963  $response['expiry'] = 0;
964  }
965 
966  break;
967 
968  case 'DEFAULT':
969  $response['expiry'] = $this->_config['default_cache_expiry'];
970  break;
971  }
972  $response['expiry'] = (int) $response['expiry'];
973  }//end setCacheExpiry()
974 
975 
982  protected function _readCurlHeaders($ch, $header)
983  {
984  // Indicates we're following a redirect.. so we can discard the last set of headers.
985  if ($this->_last_header) {
986  $this->_headers = Array();
987  $this->_last_header = FALSE;
988  } else {
989  // If we find a blank row, this could be the last header, but we might
990  // be following a redirect.
991  if (trim($header) == '') {
992  $this->_last_header = TRUE;
993  } else {
994  $pair = explode(': ', $header);
995  if (count($pair) == 2) {
996  $this->_headers[trim($pair[0])] = trim($pair[1]);
997  }
998  }
999  }
1000 
1001  return strlen($header);
1002 
1003  }//end _readCurlHeaders()
1004 
1005 }//end class
1006 
1007 ?>