Webinterpret_Connector - Version 1.3.0.0

Version Notes

- Translation service improvement - display international pages under the domestic store domain

Download this release

Release Info

Developer Webinterpret
Extension Webinterpret_Connector
Version 1.3.0.0
Comparing to
See all releases


Code changes from version 1.2.7.7 to 1.3.0.0

Files changed (45) hide show
  1. app/code/community/Webinterpret/Connector/Helper/Data.php +53 -0
  2. app/code/community/Webinterpret/Connector/Lib/Buzz/Browser.php +195 -0
  3. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractClient.php +73 -0
  4. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractCurl.php +232 -0
  5. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractStream.php +45 -0
  6. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/BatchClientInterface.php +27 -0
  7. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/ClientInterface.php +20 -0
  8. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/Curl.php +60 -0
  9. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/FileGetContents.php +51 -0
  10. app/code/community/Webinterpret/Connector/Lib/Buzz/Client/MultiCurl.php +114 -0
  11. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/ClientException.php +10 -0
  12. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/ExceptionInterface.php +10 -0
  13. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/InvalidArgumentException.php +10 -0
  14. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/LogicException.php +10 -0
  15. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/RequestException.php +32 -0
  16. app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/RuntimeException.php +7 -0
  17. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/BasicAuthListener.php +27 -0
  18. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/CallbackListener.php +49 -0
  19. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/CookieListener.php +50 -0
  20. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/DigestAuthListener.php +837 -0
  21. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/History/Entry.php +42 -0
  22. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/History/Journal.php +76 -0
  23. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/HistoryListener.php +33 -0
  24. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/ListenerChain.php +40 -0
  25. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/ListenerInterface.php +12 -0
  26. app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/LoggerListener.php +36 -0
  27. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/AbstractMessage.php +154 -0
  28. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Factory/Factory.php +26 -0
  29. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Factory/FactoryInterface.php +12 -0
  30. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormRequest.php +187 -0
  31. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormRequestInterface.php +27 -0
  32. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormUpload.php +118 -0
  33. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormUploadInterface.php +13 -0
  34. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/MessageInterface.php +74 -0
  35. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Request.php +174 -0
  36. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/RequestInterface.php +75 -0
  37. app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Response.php +193 -0
  38. app/code/community/Webinterpret/Connector/Lib/Buzz/Util/Cookie.php +216 -0
  39. app/code/community/Webinterpret/Connector/Lib/Buzz/Util/CookieJar.php +79 -0
  40. app/code/community/Webinterpret/Connector/Lib/Buzz/Util/Url.php +190 -0
  41. app/code/community/Webinterpret/Connector/Model/StoreExtender.php +198 -0
  42. app/code/community/Webinterpret/Connector/controllers/IndexController.php +12 -0
  43. app/code/community/Webinterpret/Connector/etc/config.xml +33 -2
  44. app/design/frontend/base/default/template/webinterpret/connector/product_view.phtml +1 -0
  45. package.xml +5 -8
app/code/community/Webinterpret/Connector/Helper/Data.php CHANGED
@@ -133,6 +133,11 @@ class Webinterpret_Connector_Helper_Data extends Mage_Core_Helper_Abstract
133
  return (bool)Mage::getStoreConfig('webinterpret_connector/global_notifications_enabled');
134
  }
135
 
 
 
 
 
 
136
  public function isFooterEnabled()
137
  {
138
  $module = $this->isEnabled();
@@ -452,4 +457,52 @@ class Webinterpret_Connector_Helper_Data extends Mage_Core_Helper_Abstract
452
  {
453
  return (string) Mage::getConfig()->getNode()->modules->Webinterpret_Connector->version;
454
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  }
133
  return (bool)Mage::getStoreConfig('webinterpret_connector/global_notifications_enabled');
134
  }
135
 
136
+ public function isStoreExtenderEnabled()
137
+ {
138
+ return (bool)Mage::getStoreConfig('webinterpret_connector/store_extender_enabled');
139
+ }
140
+
141
  public function isFooterEnabled()
142
  {
143
  $module = $this->isEnabled();
457
  {
458
  return (string) Mage::getConfig()->getNode()->modules->Webinterpret_Connector->version;
459
  }
460
+
461
+ /**
462
+ * @return bool
463
+ * @throws \Exception
464
+ */
465
+ public function verifyRequest() {
466
+ if (!function_exists( 'openssl_verify')) {
467
+ throw new \Exception('OpenSSL is required.');
468
+ }
469
+
470
+ $requestId = $_SERVER['HTTP_WEBINTERPRET_REQUEST_ID'];
471
+ $signature = $_SERVER['HTTP_WEBINTERPRET_SIGNATURE'];
472
+ $signature = base64_decode( $signature );
473
+
474
+ $version = $_SERVER['HTTP_WEBINTERPRET_SIGNATURE_VERSION'];
475
+ if (empty( $version )) {
476
+ $version = 1;
477
+ }
478
+
479
+ // Data
480
+ $data = $requestId;
481
+ if ($version == 2) {
482
+ // Query data
483
+ $queryArray = $_GET;
484
+ ksort($queryArray);
485
+ $queryData = serialize($queryArray);
486
+
487
+ // Post data
488
+ $postData = $_POST;
489
+ ksort($postData);
490
+ $postData = serialize($postData);
491
+
492
+ $data = $requestId . $queryData . $postData;
493
+ }
494
+
495
+ // Verify signature
496
+ $public_key = Mage::getStoreConfig('webinterpret_connector/public_key');
497
+ $ok = openssl_verify($data, $signature, $public_key, OPENSSL_ALGO_SHA1);
498
+
499
+ if ($ok == 1) {
500
+ return true;
501
+ }
502
+ if ($ok == 0) {
503
+ throw new \Exception('Incorrect signature.');
504
+ }
505
+
506
+ throw new \Exception('Error checking signature');
507
+ }
508
  }
app/code/community/Webinterpret/Connector/Lib/Buzz/Browser.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz;
4
+
5
+ use WebinterpretConnector\Buzz\Client\ClientInterface;
6
+ use WebinterpretConnector\Buzz\Client\FileGetContents;
7
+ use WebinterpretConnector\Buzz\Listener\ListenerChain;
8
+ use WebinterpretConnector\Buzz\Listener\ListenerInterface;
9
+ use WebinterpretConnector\Buzz\Message\Factory\Factory;
10
+ use WebinterpretConnector\Buzz\Message\Factory\FactoryInterface;
11
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
12
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
13
+ use WebinterpretConnector\Buzz\Util\Url;
14
+
15
+ class Browser
16
+ {
17
+ private $client;
18
+ private $factory;
19
+ private $listener;
20
+ private $lastRequest;
21
+ private $lastResponse;
22
+
23
+ public function __construct(ClientInterface $client = null, FactoryInterface $factory = null)
24
+ {
25
+ $this->client = $client ?: new FileGetContents();
26
+ $this->factory = $factory ?: new Factory();
27
+ }
28
+
29
+ public function get($url, $headers = array())
30
+ {
31
+ return $this->call($url, RequestInterface::METHOD_GET, $headers);
32
+ }
33
+
34
+ public function post($url, $headers = array(), $content = '')
35
+ {
36
+ return $this->call($url, RequestInterface::METHOD_POST, $headers, $content);
37
+ }
38
+
39
+ public function head($url, $headers = array())
40
+ {
41
+ return $this->call($url, RequestInterface::METHOD_HEAD, $headers);
42
+ }
43
+
44
+ public function patch($url, $headers = array(), $content = '')
45
+ {
46
+ return $this->call($url, RequestInterface::METHOD_PATCH, $headers, $content);
47
+ }
48
+
49
+ public function put($url, $headers = array(), $content = '')
50
+ {
51
+ return $this->call($url, RequestInterface::METHOD_PUT, $headers, $content);
52
+ }
53
+
54
+ public function delete($url, $headers = array(), $content = '')
55
+ {
56
+ return $this->call($url, RequestInterface::METHOD_DELETE, $headers, $content);
57
+ }
58
+
59
+ /**
60
+ * Sends a request.
61
+ *
62
+ * @param string $url The URL to call
63
+ * @param string $method The request method to use
64
+ * @param array $headers An array of request headers
65
+ * @param string $content The request content
66
+ *
67
+ * @return MessageInterface The response object
68
+ */
69
+ public function call($url, $method, $headers = array(), $content = '')
70
+ {
71
+ $request = $this->factory->createRequest($method);
72
+
73
+ if (!$url instanceof Url) {
74
+ $url = new Url($url);
75
+ }
76
+
77
+ $url->applyToRequest($request);
78
+
79
+ $request->addHeaders($headers);
80
+ $request->setContent($content);
81
+
82
+ return $this->send($request);
83
+ }
84
+
85
+ /**
86
+ * Sends a form request.
87
+ *
88
+ * @param string $url The URL to submit to
89
+ * @param array $fields An array of fields
90
+ * @param string $method The request method to use
91
+ * @param array $headers An array of request headers
92
+ *
93
+ * @return MessageInterface The response object
94
+ */
95
+ public function submit($url, array $fields, $method = RequestInterface::METHOD_POST, $headers = array())
96
+ {
97
+ $request = $this->factory->createFormRequest();
98
+
99
+ if (!$url instanceof Url) {
100
+ $url = new Url($url);
101
+ }
102
+
103
+ $url->applyToRequest($request);
104
+
105
+ $request->addHeaders($headers);
106
+ $request->setMethod($method);
107
+ $request->setFields($fields);
108
+
109
+ return $this->send($request);
110
+ }
111
+
112
+ /**
113
+ * Sends a request.
114
+ *
115
+ * @param RequestInterface $request A request object
116
+ * @param MessageInterface $response A response object
117
+ *
118
+ * @return MessageInterface The response
119
+ */
120
+ public function send(RequestInterface $request, MessageInterface $response = null)
121
+ {
122
+ if (null === $response) {
123
+ $response = $this->factory->createResponse();
124
+ }
125
+
126
+ if ($this->listener) {
127
+ $this->listener->preSend($request);
128
+ }
129
+
130
+ $this->client->send($request, $response);
131
+
132
+ $this->lastRequest = $request;
133
+ $this->lastResponse = $response;
134
+
135
+ if ($this->listener) {
136
+ $this->listener->postSend($request, $response);
137
+ }
138
+
139
+ return $response;
140
+ }
141
+
142
+ public function getLastRequest()
143
+ {
144
+ return $this->lastRequest;
145
+ }
146
+
147
+ public function getLastResponse()
148
+ {
149
+ return $this->lastResponse;
150
+ }
151
+
152
+ public function setClient(ClientInterface $client)
153
+ {
154
+ $this->client = $client;
155
+ }
156
+
157
+ public function getClient()
158
+ {
159
+ return $this->client;
160
+ }
161
+
162
+ public function setMessageFactory(FactoryInterface $factory)
163
+ {
164
+ $this->factory = $factory;
165
+ }
166
+
167
+ public function getMessageFactory()
168
+ {
169
+ return $this->factory;
170
+ }
171
+
172
+ public function setListener(ListenerInterface $listener)
173
+ {
174
+ $this->listener = $listener;
175
+ }
176
+
177
+ public function getListener()
178
+ {
179
+ return $this->listener;
180
+ }
181
+
182
+ public function addListener(ListenerInterface $listener)
183
+ {
184
+ if (!$this->listener) {
185
+ $this->listener = $listener;
186
+ } elseif ($this->listener instanceof ListenerChain) {
187
+ $this->listener->addListener($listener);
188
+ } else {
189
+ $this->listener = new ListenerChain(array(
190
+ $this->listener,
191
+ $listener,
192
+ ));
193
+ }
194
+ }
195
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractClient.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ abstract class AbstractClient implements ClientInterface
6
+ {
7
+ protected $ignoreErrors = true;
8
+ protected $maxRedirects = 5;
9
+ protected $timeout = 5;
10
+ protected $verifyPeer = true;
11
+ protected $verifyHost = 2;
12
+ protected $proxy;
13
+
14
+ public function setIgnoreErrors($ignoreErrors)
15
+ {
16
+ $this->ignoreErrors = $ignoreErrors;
17
+ }
18
+
19
+ public function getIgnoreErrors()
20
+ {
21
+ return $this->ignoreErrors;
22
+ }
23
+
24
+ public function setMaxRedirects($maxRedirects)
25
+ {
26
+ $this->maxRedirects = $maxRedirects;
27
+ }
28
+
29
+ public function getMaxRedirects()
30
+ {
31
+ return $this->maxRedirects;
32
+ }
33
+
34
+ public function setTimeout($timeout)
35
+ {
36
+ $this->timeout = $timeout;
37
+ }
38
+
39
+ public function getTimeout()
40
+ {
41
+ return $this->timeout;
42
+ }
43
+
44
+ public function setVerifyPeer($verifyPeer)
45
+ {
46
+ $this->verifyPeer = $verifyPeer;
47
+ }
48
+
49
+ public function getVerifyPeer()
50
+ {
51
+ return $this->verifyPeer;
52
+ }
53
+
54
+ public function getVerifyHost()
55
+ {
56
+ return $this->verifyHost;
57
+ }
58
+
59
+ public function setVerifyHost($verifyHost)
60
+ {
61
+ $this->verifyHost = $verifyHost;
62
+ }
63
+
64
+ public function setProxy($proxy)
65
+ {
66
+ $this->proxy = $proxy;
67
+ }
68
+
69
+ public function getProxy()
70
+ {
71
+ return $this->proxy;
72
+ }
73
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractCurl.php ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Message\Form\FormRequestInterface;
6
+ use WebinterpretConnector\Buzz\Message\Form\FormUploadInterface;
7
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
8
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
9
+ use WebinterpretConnector\Buzz\Exception\ClientException;
10
+
11
+ /**
12
+ * Base client class with helpers for working with cURL.
13
+ */
14
+ abstract class AbstractCurl extends AbstractClient
15
+ {
16
+ protected $options = array();
17
+
18
+ public function __construct()
19
+ {
20
+ if (defined('CURLOPT_PROTOCOLS')) {
21
+ $this->options = array(
22
+ CURLOPT_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
23
+ CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
24
+ );
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Creates a new cURL resource.
30
+ *
31
+ * @see curl_init()
32
+ *
33
+ * @return resource A new cURL resource
34
+ *
35
+ * @throws ClientException If unable to create a cURL resource
36
+ */
37
+ protected static function createCurlHandle()
38
+ {
39
+ if (false === $curl = curl_init()) {
40
+ throw new ClientException('Unable to create a new cURL handle');
41
+ }
42
+
43
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
44
+ curl_setopt($curl, CURLOPT_HEADER, true);
45
+
46
+ return $curl;
47
+ }
48
+
49
+ /**
50
+ * Populates a response object.
51
+ *
52
+ * @param resource $curl A cURL resource
53
+ * @param string $raw The raw response string
54
+ * @param MessageInterface $response The response object
55
+ */
56
+ protected static function populateResponse($curl, $raw, MessageInterface $response)
57
+ {
58
+ // fixes bug https://sourceforge.net/p/curl/bugs/1204/
59
+ $version = curl_version();
60
+ if (version_compare($version['version'], '7.30.0', '<')) {
61
+ $pos = strlen($raw) - curl_getinfo($curl, CURLINFO_SIZE_DOWNLOAD);
62
+ } else {
63
+ $pos = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
64
+ }
65
+
66
+ $response->setHeaders(static::getLastHeaders(rtrim(substr($raw, 0, $pos))));
67
+ $response->setContent(strlen($raw) > $pos ? substr($raw, $pos) : '');
68
+ }
69
+
70
+ /**
71
+ * Sets options on a cURL resource based on a request.
72
+ */
73
+ private static function setOptionsFromRequest($curl, RequestInterface $request)
74
+ {
75
+ $options = array(
76
+ CURLOPT_HTTP_VERSION => $request->getProtocolVersion() == 1.0 ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1,
77
+ CURLOPT_CUSTOMREQUEST => $request->getMethod(),
78
+ CURLOPT_URL => $request->getHost().$request->getResource(),
79
+ CURLOPT_HTTPHEADER => $request->getHeaders(),
80
+ );
81
+
82
+ switch ($request->getMethod()) {
83
+ case RequestInterface::METHOD_HEAD:
84
+ $options[CURLOPT_NOBODY] = true;
85
+ break;
86
+
87
+ case RequestInterface::METHOD_GET:
88
+ $options[CURLOPT_HTTPGET] = true;
89
+ break;
90
+
91
+ case RequestInterface::METHOD_POST:
92
+ case RequestInterface::METHOD_PUT:
93
+ case RequestInterface::METHOD_DELETE:
94
+ case RequestInterface::METHOD_PATCH:
95
+ case RequestInterface::METHOD_OPTIONS:
96
+ $options[CURLOPT_POSTFIELDS] = $fields = static::getPostFields($request);
97
+
98
+ // remove the content-type header
99
+ if (is_array($fields)) {
100
+ $options[CURLOPT_HTTPHEADER] = array_filter($options[CURLOPT_HTTPHEADER], function($header) {
101
+ return 0 !== stripos($header, 'Content-Type: ');
102
+ });
103
+ }
104
+
105
+ break;
106
+ }
107
+
108
+ curl_setopt_array($curl, $options);
109
+ }
110
+
111
+ /**
112
+ * Returns a value for the CURLOPT_POSTFIELDS option.
113
+ *
114
+ * @return string|array A post fields value
115
+ */
116
+ private static function getPostFields(RequestInterface $request)
117
+ {
118
+ if (!$request instanceof FormRequestInterface) {
119
+ return $request->getContent();
120
+ }
121
+
122
+ $fields = $request->getFields();
123
+ $multipart = false;
124
+
125
+ foreach ($fields as $name => $value) {
126
+ if (!$value instanceof FormUploadInterface) {
127
+ continue;
128
+ }
129
+
130
+ if (!$file = $value->getFile()) {
131
+ return $request->getContent();
132
+ }
133
+
134
+ $multipart = true;
135
+
136
+ if (version_compare(PHP_VERSION, '5.5', '>=')) {
137
+ $curlFile = new \CURLFile($file);
138
+ if ($contentType = $value->getContentType()) {
139
+ $curlFile->setMimeType($contentType);
140
+ }
141
+
142
+ if (basename($file) != $value->getFilename()) {
143
+ $curlFile->setPostFilename($value->getFilename());
144
+ }
145
+
146
+ $fields[$name] = $curlFile;
147
+ } else {
148
+ // replace value with upload string
149
+ $fields[$name] = '@'.$file;
150
+
151
+ if ($contentType = $value->getContentType()) {
152
+ $fields[$name] .= ';type='.$contentType;
153
+ }
154
+ if (basename($file) != $value->getFilename()) {
155
+ $fields[$name] .= ';filename='.$value->getFilename();
156
+ }
157
+ }
158
+ }
159
+
160
+ return $multipart ? $fields : http_build_query($fields, '', '&');
161
+ }
162
+
163
+ /**
164
+ * A helper for getting the last set of headers.
165
+ *
166
+ * @param string $raw A string of many header chunks
167
+ *
168
+ * @return array An array of header lines
169
+ */
170
+ private static function getLastHeaders($raw)
171
+ {
172
+ $headers = array();
173
+ foreach (preg_split('/(\\r?\\n)/', $raw) as $header) {
174
+ if ($header) {
175
+ $headers[] = $header;
176
+ } else {
177
+ $headers = array();
178
+ }
179
+ }
180
+
181
+ return $headers;
182
+ }
183
+
184
+ /**
185
+ * Stashes a cURL option to be set on send, when the resource is created.
186
+ *
187
+ * If the supplied value it set to null the option will be removed.
188
+ *
189
+ * @param integer $option The option
190
+ * @param mixed $value The value
191
+ *
192
+ * @see curl_setopt()
193
+ */
194
+ public function setOption($option, $value)
195
+ {
196
+ if (null === $value) {
197
+ unset($this->options[$option]);
198
+ } else {
199
+ $this->options[$option] = $value;
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Prepares a cURL resource to send a request.
205
+ */
206
+ protected function prepare($curl, RequestInterface $request, array $options = array())
207
+ {
208
+ static::setOptionsFromRequest($curl, $request);
209
+
210
+ // apply settings from client
211
+ if ($this->getTimeout() < 1) {
212
+ curl_setopt($curl, CURLOPT_TIMEOUT_MS, $this->getTimeout() * 1000);
213
+ } else {
214
+ curl_setopt($curl, CURLOPT_TIMEOUT, $this->getTimeout());
215
+ }
216
+
217
+ if ($this->proxy) {
218
+ curl_setopt($curl, CURLOPT_PROXY, $this->proxy);
219
+ }
220
+
221
+ $canFollow = !ini_get('safe_mode') && !ini_get('open_basedir');
222
+
223
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, $canFollow && $this->getMaxRedirects() > 0);
224
+ curl_setopt($curl, CURLOPT_MAXREDIRS, $canFollow ? $this->getMaxRedirects() : 0);
225
+ curl_setopt($curl, CURLOPT_FAILONERROR, !$this->getIgnoreErrors());
226
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->getVerifyPeer());
227
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->getVerifyHost());
228
+
229
+ // apply additional options
230
+ curl_setopt_array($curl, $options + $this->options);
231
+ }
232
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/AbstractStream.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+
7
+ abstract class AbstractStream extends AbstractClient
8
+ {
9
+ /**
10
+ * Converts a request into an array for stream_context_create().
11
+ *
12
+ * @param RequestInterface $request A request object
13
+ *
14
+ * @return array An array for stream_context_create()
15
+ */
16
+ public function getStreamContextArray(RequestInterface $request)
17
+ {
18
+ $options = array(
19
+ 'http' => array(
20
+ // values from the request
21
+ 'method' => $request->getMethod(),
22
+ 'header' => implode("\r\n", $request->getHeaders()),
23
+ 'content' => $request->getContent(),
24
+ 'protocol_version' => $request->getProtocolVersion(),
25
+
26
+ // values from the current client
27
+ 'ignore_errors' => $this->getIgnoreErrors(),
28
+ 'follow_location' => $this->getMaxRedirects() > 0,
29
+ 'max_redirects' => $this->getMaxRedirects() + 1,
30
+ 'timeout' => $this->getTimeout(),
31
+ ),
32
+ 'ssl' => array(
33
+ 'verify_peer' => $this->getVerifyPeer(),
34
+ 'verify_host' => $this->getVerifyHost(),
35
+ ),
36
+ );
37
+
38
+ if ($this->proxy) {
39
+ $options['http']['proxy'] = $this->proxy;
40
+ $options['http']['request_fulluri'] = true;
41
+ }
42
+
43
+ return $options;
44
+ }
45
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/BatchClientInterface.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Exception\ClientException;
6
+
7
+ /**
8
+ * A client capable of running batches of requests.
9
+ *
10
+ * The Countable implementation should return the number of queued requests.
11
+ */
12
+ interface BatchClientInterface extends ClientInterface, \Countable
13
+ {
14
+ /**
15
+ * Processes all queued requests.
16
+ *
17
+ * @throws ClientException If something goes wrong
18
+ */
19
+ public function flush();
20
+
21
+ /**
22
+ * Processes zero or more queued requests.
23
+ *
24
+ * @throws ClientException If something goes wrong
25
+ */
26
+ public function proceed();
27
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/ClientInterface.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Exception\ClientException;
6
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
7
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
8
+
9
+ interface ClientInterface
10
+ {
11
+ /**
12
+ * Populates the supplied response with the response for the supplied request.
13
+ *
14
+ * @param RequestInterface $request A request object
15
+ * @param MessageInterface $response A response object
16
+ *
17
+ * @throws ClientException If something goes wrong
18
+ */
19
+ public function send(RequestInterface $request, MessageInterface $response);
20
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/Curl.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Exception\RequestException;
6
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
7
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
8
+ use WebinterpretConnector\Buzz\Exception\LogicException;
9
+
10
+ class Curl extends AbstractCurl
11
+ {
12
+ private $lastCurl;
13
+
14
+ public function send(RequestInterface $request, MessageInterface $response, array $options = array())
15
+ {
16
+ if (is_resource($this->lastCurl)) {
17
+ curl_close($this->lastCurl);
18
+ }
19
+
20
+ $this->lastCurl = static::createCurlHandle();
21
+ $this->prepare($this->lastCurl, $request, $options);
22
+
23
+ $data = curl_exec($this->lastCurl);
24
+
25
+ if (false === $data) {
26
+ $errorMsg = curl_error($this->lastCurl);
27
+ $errorNo = curl_errno($this->lastCurl);
28
+
29
+ $e = new RequestException($errorMsg, $errorNo);
30
+ $e->setRequest($request);
31
+
32
+ throw $e;
33
+ }
34
+
35
+ static::populateResponse($this->lastCurl, $data, $response);
36
+ }
37
+
38
+ /**
39
+ * Introspects the last cURL request.
40
+ *
41
+ * @see curl_getinfo()
42
+ *
43
+ * @throws LogicException If there is no cURL resource
44
+ */
45
+ public function getInfo($opt = 0)
46
+ {
47
+ if (!is_resource($this->lastCurl)) {
48
+ throw new LogicException('There is no cURL resource');
49
+ }
50
+
51
+ return 0 === $opt ? curl_getinfo($this->lastCurl) : curl_getinfo($this->lastCurl, $opt);
52
+ }
53
+
54
+ public function __destruct()
55
+ {
56
+ if (is_resource($this->lastCurl)) {
57
+ curl_close($this->lastCurl);
58
+ }
59
+ }
60
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/FileGetContents.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Exception\RequestException;
6
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
7
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
8
+
9
+ use WebinterpretConnector\Buzz\Exception\ClientException;
10
+
11
+ class FileGetContents extends AbstractStream
12
+ {
13
+ /**
14
+ * @see ClientInterface
15
+ *
16
+ * @throws ClientException If file_get_contents() fires an error
17
+ */
18
+ public function send(RequestInterface $request, MessageInterface $response)
19
+ {
20
+ $context = stream_context_create($this->getStreamContextArray($request));
21
+ $url = $request->getHost().$request->getResource();
22
+
23
+ $level = error_reporting(0);
24
+ $content = file_get_contents($url, 0, $context);
25
+ error_reporting($level);
26
+ if (false === $content) {
27
+ $error = error_get_last();
28
+ $e = new RequestException($error['message']);
29
+ $e->setRequest($request);
30
+
31
+ throw $e;
32
+ }
33
+
34
+ $response->setHeaders($this->filterHeaders((array) $http_response_header));
35
+ $response->setContent($content);
36
+ }
37
+
38
+ private function filterHeaders(array $headers)
39
+ {
40
+ $filtered = array();
41
+ foreach ($headers as $header) {
42
+ if (0 === stripos($header, 'http/')) {
43
+ $filtered = array();
44
+ }
45
+
46
+ $filtered[] = $header;
47
+ }
48
+
49
+ return $filtered;
50
+ }
51
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Client/MultiCurl.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Client;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+ use WebinterpretConnector\Buzz\Exception\ClientException;
8
+
9
+ class MultiCurl extends AbstractCurl implements BatchClientInterface
10
+ {
11
+ private $queue = array();
12
+ private $curlm;
13
+
14
+ /**
15
+ * Populates the supplied response with the response for the supplied request.
16
+ *
17
+ * The array of options will be passed to curl_setopt_array().
18
+ *
19
+ * If a "callback" option is supplied, its value will be called when the
20
+ * request completes. The callable should have the following signature:
21
+ *
22
+ * $callback = function($client, $request, $response, $options, $error) {
23
+ * if (!$error) {
24
+ * // success
25
+ * } else {
26
+ * // error ($error is one of the CURLE_* constants)
27
+ * }
28
+ * };
29
+ *
30
+ * @param RequestInterface $request A request object
31
+ * @param MessageInterface $response A response object
32
+ * @param array $options An array of options
33
+ */
34
+ public function send(RequestInterface $request, MessageInterface $response, array $options = array())
35
+ {
36
+ $this->queue[] = array($request, $response, $options);
37
+ }
38
+
39
+ public function count()
40
+ {
41
+ return count($this->queue);
42
+ }
43
+
44
+ public function flush()
45
+ {
46
+ while ($this->queue) {
47
+ $this->proceed();
48
+ }
49
+ }
50
+
51
+ public function proceed()
52
+ {
53
+ if (!$this->queue) {
54
+ return;
55
+ }
56
+
57
+ if (!$this->curlm && false === $this->curlm = curl_multi_init()) {
58
+ throw new ClientException('Unable to create a new cURL multi handle');
59
+ }
60
+
61
+ foreach (array_keys($this->queue) as $i) {
62
+ if (3 == count($this->queue[$i])) {
63
+ // prepare curl handle
64
+ list($request, , $options) = $this->queue[$i];
65
+ $curl = static::createCurlHandle();
66
+
67
+ // remove custom option
68
+ unset($options['callback']);
69
+
70
+ $this->prepare($curl, $request, $options);
71
+ $this->queue[$i][] = $curl;
72
+ curl_multi_add_handle($this->curlm, $curl);
73
+ }
74
+ }
75
+
76
+ // process outstanding perform
77
+ $active = null;
78
+ do {
79
+ $mrc = curl_multi_exec($this->curlm, $active);
80
+ } while ($active && CURLM_CALL_MULTI_PERFORM == $mrc);
81
+
82
+ // handle any completed requests
83
+ while ($done = curl_multi_info_read($this->curlm)) {
84
+ foreach (array_keys($this->queue) as $i) {
85
+ list($request, $response, $options, $curl) = $this->queue[$i];
86
+
87
+ if ($curl !== $done['handle']) {
88
+ continue;
89
+ }
90
+
91
+ // populate the response object
92
+ if (CURLE_OK === $done['result']) {
93
+ static::populateResponse($curl, curl_multi_getcontent($curl), $response);
94
+ }
95
+
96
+ // remove from queue
97
+ curl_multi_remove_handle($this->curlm, $curl);
98
+ curl_close($curl);
99
+ unset($this->queue[$i]);
100
+
101
+ // callback
102
+ if (isset($options['callback'])) {
103
+ call_user_func($options['callback'], $this, $request, $response, $options, $done['result']);
104
+ }
105
+ }
106
+ }
107
+
108
+ // cleanup
109
+ if (!$this->queue) {
110
+ curl_multi_close($this->curlm);
111
+ $this->curlm = null;
112
+ }
113
+ }
114
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/ClientException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ /**
6
+ * Thrown whenever a client process fails.
7
+ */
8
+ class ClientException extends RuntimeException
9
+ {
10
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/ExceptionInterface.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ /**
6
+ * Marker interface to denote exceptions thrown from the Buzz context.
7
+ */
8
+ interface ExceptionInterface
9
+ {
10
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/InvalidArgumentException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ /**
6
+ * Thrown when an invalid argument is provided.
7
+ */
8
+ class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
9
+ {
10
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/LogicException.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ /**
6
+ * Thrown whenever a required call-flow is not respected.
7
+ */
8
+ class LogicException extends \LogicException implements ExceptionInterface
9
+ {
10
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/RequestException.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+
7
+ class RequestException extends ClientException
8
+ {
9
+ /**
10
+ * Request object
11
+ *
12
+ * @var RequestInterface
13
+ */
14
+ private $request;
15
+
16
+ /**
17
+ * @return RequestInterface
18
+ */
19
+ public function getRequest()
20
+ {
21
+ return $this->request;
22
+ }
23
+
24
+ /**
25
+ * @param RequestInterface $request
26
+ */
27
+ public function setRequest(RequestInterface $request)
28
+ {
29
+ $this->request = $request;
30
+ }
31
+
32
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Exception/RuntimeException.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Exception;
4
+
5
+ class RuntimeException extends \RuntimeException implements ExceptionInterface
6
+ {
7
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/BasicAuthListener.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class BasicAuthListener implements ListenerInterface
9
+ {
10
+ private $username;
11
+ private $password;
12
+
13
+ public function __construct($username, $password)
14
+ {
15
+ $this->username = $username;
16
+ $this->password = $password;
17
+ }
18
+
19
+ public function preSend(RequestInterface $request)
20
+ {
21
+ $request->addHeader('Authorization: Basic '.base64_encode($this->username.':'.$this->password));
22
+ }
23
+
24
+ public function postSend(RequestInterface $request, MessageInterface $response)
25
+ {
26
+ }
27
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/CallbackListener.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+ use WebinterpretConnector\Buzz\Exception\InvalidArgumentException;
8
+
9
+ class CallbackListener implements ListenerInterface
10
+ {
11
+ private $callable;
12
+
13
+ /**
14
+ * Constructor.
15
+ *
16
+ * The callback should expect either one or two arguments, depending on
17
+ * whether it is receiving a pre or post send notification.
18
+ *
19
+ * $listener = new CallbackListener(function($request, $response = null) {
20
+ * if ($response) {
21
+ * // postSend
22
+ * } else {
23
+ * // preSend
24
+ * }
25
+ * });
26
+ *
27
+ * @param mixed $callable A PHP callable
28
+ *
29
+ * @throws InvalidArgumentException If the argument is not callable
30
+ */
31
+ public function __construct($callable)
32
+ {
33
+ if (!is_callable($callable)) {
34
+ throw new InvalidArgumentException('The argument is not callable.');
35
+ }
36
+
37
+ $this->callable = $callable;
38
+ }
39
+
40
+ public function preSend(RequestInterface $request)
41
+ {
42
+ call_user_func($this->callable, $request);
43
+ }
44
+
45
+ public function postSend(RequestInterface $request, MessageInterface $response)
46
+ {
47
+ call_user_func($this->callable, $request, $response);
48
+ }
49
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/CookieListener.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
7
+
8
+ use WebinterpretConnector\Buzz\Util\Cookie;
9
+ use WebinterpretConnector\Buzz\Util\CookieJar;
10
+
11
+ class CookieListener implements ListenerInterface
12
+ {
13
+ private $cookieJar;
14
+
15
+ public function __construct()
16
+ {
17
+ $this->cookieJar = new CookieJar();
18
+ }
19
+
20
+ public function setCookies($cookies)
21
+ {
22
+ $this->cookieJar->setCookies($cookies);
23
+ }
24
+
25
+ public function getCookies()
26
+ {
27
+ return $this->cookieJar->getCookies();
28
+ }
29
+
30
+ /**
31
+ * Adds a cookie to the current cookie jar.
32
+ *
33
+ * @param Cookie $cookie A cookie object
34
+ */
35
+ public function addCookie(Cookie $cookie)
36
+ {
37
+ $this->cookieJar->addCookie($cookie);
38
+ }
39
+
40
+ public function preSend(RequestInterface $request)
41
+ {
42
+ $this->cookieJar->clearExpiredCookies();
43
+ $this->cookieJar->addCookieHeaders($request);
44
+ }
45
+
46
+ public function postSend(RequestInterface $request, MessageInterface $response)
47
+ {
48
+ $this->cookieJar->processSetCookieHeaders($request, $response);
49
+ }
50
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/DigestAuthListener.php ADDED
@@ -0,0 +1,837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class DigestAuthListener implements ListenerInterface
9
+ {
10
+ private $username;
11
+ private $password;
12
+ private $realm;
13
+
14
+ private $algorithm;
15
+ private $authenticationMethod;
16
+ private $clientNonce;
17
+ private $domain;
18
+ private $entityBody;
19
+ private $method;
20
+ private $nonce;
21
+ private $nonceCount;
22
+ private $opaque;
23
+ private $uri;
24
+
25
+ /**
26
+ * QOP options: Only one of the following can be set at any time. setOptions will throw an exception otherwise.
27
+ * OPTION_QOP_AUTH_INT - Always use auth-int (if available)
28
+ * OPTION_QOP_AUTH - Always use auth (even if auth-int available)
29
+ */
30
+ const OPTION_QOP_AUTH_INT = 1;
31
+ const OPTION_QOP_AUTH = 2;
32
+ /**
33
+ * Ignore server request to downgrade authentication from Digest to Basic.
34
+ * Breaks RFC compatibility, but ensures passwords are never sent using base64 which is trivial for an attacker to decode.
35
+ */
36
+ const OPTION_IGNORE_DOWNGRADE_REQUEST = 4;
37
+ /**
38
+ * Discard Client Nonce on each request.
39
+ */
40
+ const OPTION_DISCARD_CLIENT_NONCE = 8;
41
+
42
+ private $options;
43
+
44
+ /**
45
+ * Set OPTION_QOP_BEST_AVAILABLE and OPTION_DISCARD_CLIENT_NONCE by default.
46
+ */
47
+ public function __construct($username = null, $password = null, $realm = null)
48
+ {
49
+ $this->setUsername($username);
50
+ $this->setPassword($password);
51
+ $this->setRealm($realm);
52
+ $this->setOptions(DigestAuthListener::OPTION_QOP_AUTH_INT & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE);
53
+ }
54
+
55
+ /**
56
+ * Passes the returned server headers to parseServerHeaders() to check if any authentication variables need to be set.
57
+ * Inteprets the returned status code and attempts authentication if status is 401 (Authentication Required) by resending
58
+ * the last request with an Authentication header.
59
+ *
60
+ * @param RequestInterface $request A request object
61
+ * @param MessageInterface $response A response object
62
+ */
63
+ public function postSend(RequestInterface $request, MessageInterface $response)
64
+ {
65
+ $statusCode = $response->getStatusCode();
66
+ $this->parseServerHeaders($response->getHeaders(), $statusCode);
67
+ }
68
+
69
+ /**
70
+ * Populates uri, method and entityBody used to generate the Authentication header using the specified request object.
71
+ * Appends the Authentication header if it is present and has been able to be calculated.
72
+ *
73
+ * @param RequestInterface $request A request object
74
+ */
75
+ public function preSend(RequestInterface $request)
76
+ {
77
+ $this->setUri($request->getResource());
78
+ $this->setMethod($request->getMethod());
79
+ $this->setEntityBody($request->getContent());
80
+
81
+ $header = $this->getHeader();
82
+ if($header) {
83
+ $request->addHeader($header);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Sets the password to be used to authenticate the client.
89
+ *
90
+ * @param string $password The password
91
+ *
92
+ * @return void
93
+ */
94
+ public function setPassword($password)
95
+ {
96
+ $this->password = $password;
97
+ }
98
+
99
+ /**
100
+ * Sets the realm to be used to authenticate the client.
101
+ *
102
+ * @param string $realm The realm
103
+ *
104
+ * @return void
105
+ */
106
+ public function setRealm($realm)
107
+ {
108
+ $this->realm = $realm;
109
+ }
110
+
111
+ /**
112
+ * Sets the username to be used to authenticate the client.
113
+ *
114
+ * @param string $username The username
115
+ *
116
+ * @return void
117
+ */
118
+ public function setUsername($username)
119
+ {
120
+ $this->username = $username;
121
+ }
122
+
123
+ /**
124
+ * Sets the options to be used by this class.
125
+ *
126
+ * @param int $options A bitmask of the constants defined in this class.
127
+ *
128
+ * @return void
129
+ */
130
+ public function setOptions($options)
131
+ {
132
+ if(($options & DigestAuthListener::OPTION_QOP_AUTH_INT) === true) {
133
+ if(($options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
134
+ throw new \InvalidArgumentException('DigestAuthListener: Only one value of OPTION_QOP_AUTH_INT or OPTION_QOP_AUTH may be set.');
135
+ }
136
+ $this->options = $this->options | DigestAuthListener::OPTION_QOP_AUTH_INT;
137
+ } else {
138
+ if(($options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
139
+ $this->options = $this->options | DigestAuthListener::OPTION_QOP_AUTH;
140
+ }
141
+ }
142
+
143
+ if(($options & DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST) === true) {
144
+ $this->options = $this->options | DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST;
145
+ }
146
+
147
+ if(($options & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE) === true) {
148
+ $this->options = $this->options | DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Discards the Client Nonce forcing the generation of a new Client Nonce on the next request.
154
+ *
155
+ * @return void
156
+ */
157
+ private function discardClientNonce()
158
+ {
159
+ $this->clientNonce = null;
160
+ }
161
+
162
+ /**
163
+ * Returns the hashing algorithm to be used to generate the digest value. Currently only returns MD5.
164
+ *
165
+ * @return string The hashing algorithm to be used.
166
+ */
167
+ private function getAlgorithm()
168
+ {
169
+ if($this->algorithm == null) {
170
+ $this->algorithm = 'MD5';
171
+ }
172
+ return $this->algorithm;
173
+ }
174
+
175
+ /**
176
+ * Returns the authentication method requested by the server.
177
+ * If OPTION_IGNORE_DOWNGRADE_REQUEST is set this will always return "Digest"
178
+ *
179
+ * @return string Returns either "Digest" or "Basic".
180
+ */
181
+ private function getAuthenticationMethod()
182
+ {
183
+ if(($this->options & DigestAuthListener::OPTION_IGNORE_DOWNGRADE_REQUEST) === true) {
184
+ return "Digest";
185
+ }
186
+ return $this->authenticationMethod;
187
+ }
188
+
189
+ /**
190
+ * Returns either the current value of clientNonce or generates a new value if clientNonce is null.
191
+ * Also increments nonceCount.
192
+ *
193
+ * @return string Returns either the current value of clientNonce the newly generated clientNonce;
194
+ */
195
+ private function getClientNonce()
196
+ {
197
+ if($this->clientNonce == null) {
198
+ $this->clientNonce = uniqid();
199
+
200
+ if($this->nonceCount == null) {
201
+ // If nonceCount is not set then set it to 00000001.
202
+ $this->nonceCount = '00000001';
203
+ } else {
204
+ // If it is set then increment it.
205
+ $this->nonceCount++;
206
+ // Ensure nonceCount is zero-padded at the start of the string to a length of 8
207
+ while(strlen($this->nonceCount) < 8) {
208
+ $this->nonceCount = '0' . $this->nonceCount;
209
+ }
210
+ }
211
+ }
212
+ return $this->clientNonce;
213
+ }
214
+
215
+ /**
216
+ * Returns a space separated list of uris that the server nonce can be used to generate an authentication response against.
217
+ *
218
+ * @return string Space separated list of uris.
219
+ */
220
+ private function getDomain()
221
+ {
222
+ return $this->domain;
223
+ }
224
+
225
+ /**
226
+ * Returns the entity body of the current request.
227
+ * The entity body is the request before it has been encoded with the content-encoding and minus the request headers.
228
+ *
229
+ * @return string The full entity-body.
230
+ */
231
+ private function getEntityBody()
232
+ {
233
+ return (string)$this->entityBody;
234
+ }
235
+
236
+ /**
237
+ * Calculates the value of HA1 according to RFC 2617 and RFC 2069.
238
+ *
239
+ * @return string The value of HA1
240
+ */
241
+ private function getHA1()
242
+ {
243
+ $username = $this->getUsername();
244
+ $password = $this->getPassword();
245
+ $realm = $this->getRealm();
246
+
247
+ if(($username) AND ($password) AND ($realm)) {
248
+ $algorithm = $this->getAlgorithm();
249
+
250
+ if(!isset($algorithm) OR ($algorithm == "MD5")) {
251
+
252
+ $A1 = "{$username}:{$realm}:{$password}";
253
+ }
254
+ if($this->algorithm == "MD5-sess") {
255
+
256
+ $nonce = $this->getNonce();
257
+ $cnonce = $this->getClientNonce();
258
+ if(($nonce) AND ($cnonce)) {
259
+ $A1 = $this->hash("{$username}:{$realm}:{$password}") . ":{$nonce}:{$cnonce}";
260
+ }
261
+ }
262
+ if(isset($A1)) {
263
+ $HA1 = $this->hash($A1);
264
+ return $HA1;
265
+ }
266
+ }
267
+ return null;
268
+ }
269
+
270
+ /**
271
+ * Calculates the value of HA2 according to RFC 2617 and RFC 2069.
272
+ *
273
+ * @return string The value of HA2
274
+ */
275
+ private function getHA2()
276
+ {
277
+ $method = $this->getMethod();
278
+ $uri = $this->getUri();
279
+
280
+ if(($method) AND ($uri)) {
281
+ $qop = $this->getQOP();
282
+
283
+ if(!isset($qop) OR ($qop == 'auth')) {
284
+ $A2 = "{$method}:{$uri}";
285
+ }
286
+ if($qop == 'auth-int') {
287
+ $entityBody = $this->getEntityBody();
288
+ $A2 = "{$method}:{$uri}:" . $this->hash($entityBody);
289
+ }
290
+
291
+ if(isset($A2)) {
292
+ $HA2 = $this->hash($A2);
293
+ return $HA2;
294
+ }
295
+ }
296
+ return null;
297
+ }
298
+
299
+ /**
300
+ * Returns the full Authentication header for use in authenticating the client with either Digest or Basic authentication.
301
+ *
302
+ * @return string The Authentication header to be sent to the server.
303
+ */
304
+ private function getHeader()
305
+ {
306
+ if($this->getAuthenticationMethod() == 'Digest') {
307
+ $username = $this->getUsername();
308
+ $realm = $this->getRealm();
309
+ $nonce = $this->getNonce();
310
+ $response = $this->getResponse();
311
+ if(($username) AND ($realm) AND ($nonce) AND ($response)) {
312
+ $uri = $this->getUri();
313
+ $opaque = $this->getOpaque();
314
+ $domain = $this->getDomain();
315
+ $qop = $this->getQOP();
316
+
317
+ $header = "Authorization: Digest";
318
+ $header .= " username=\"" . $username . "\",";
319
+ $header .= " realm=\"" . $realm . "\",";
320
+ $header .= " nonce=\"" . $nonce . "\",";
321
+ $header .= " response=\"" . $response . "\",";
322
+
323
+ if($uri) {
324
+ $header .= " uri=\"" . $uri . "\",";
325
+ }
326
+ if($opaque) {
327
+ $header .= " opaque=\"" . $opaque . "\",";
328
+ }
329
+
330
+ if($qop) {
331
+ $header .= " qop=" . $qop . ",";
332
+
333
+ $cnonce = $this->getClientNonce();
334
+ $nc = $this->getNonceCount();
335
+
336
+ if($cnonce) {
337
+ $header .= " nc=" . $nc . ",";
338
+ }
339
+ if($cnonce) {
340
+ $header .= " cnonce=\"" . $cnonce . "\",";
341
+ }
342
+ }
343
+
344
+ // Remove the last comma from the header
345
+ $header = substr($header, 0, strlen($header) - 1);
346
+ // Discard the Client Nonce if OPTION_DISCARD_CLIENT_NONCE is set.
347
+ if(($this->options & DigestAuthListener::OPTION_DISCARD_CLIENT_NONCE) === true) {
348
+ $this->discardClientNonce();
349
+ }
350
+ return $header;
351
+ }
352
+ }
353
+ if($this->getAuthenticationMethod() == 'Basic') {
354
+ $username = $this->getUsername();
355
+ $password = $this->getPassword();
356
+ if(($username) AND ($password)) {
357
+ $header = 'Authorization: Basic ' . base64_encode("{$username}:{$password}");
358
+ return $header;
359
+ }
360
+ }
361
+ return null;
362
+ }
363
+
364
+ /**
365
+ * Returns the HTTP method used in the current request.
366
+ *
367
+ * @return string One of GET,POST,PUT,DELETE or HEAD.
368
+ */
369
+ private function getMethod()
370
+ {
371
+ return $this->method;
372
+ }
373
+
374
+ /**
375
+ * Returns the value of nonce we have received in the server headers.
376
+ *
377
+ * @return string The value of the server nonce.
378
+ */
379
+ private function getNonce()
380
+ {
381
+ return $this->nonce;
382
+ }
383
+
384
+ /**
385
+ * Returns the current nonce counter for the client nonce.
386
+ *
387
+ * @return string An eight digit zero-padded string which reflects the number of times the clientNonce has been generated.
388
+ */
389
+ private function getNonceCount()
390
+ {
391
+ return $this->nonceCount;
392
+ }
393
+
394
+ /**
395
+ * Returns the opaque value that was sent to us from the server.
396
+ *
397
+ * @return string The value of opaque.
398
+ */
399
+ private function getOpaque()
400
+ {
401
+ return $this->opaque;
402
+ }
403
+
404
+ /**
405
+ * Returns the plaintext password for the client.
406
+ *
407
+ * @return string The value of password.
408
+ */
409
+ private function getPassword()
410
+ {
411
+ return $this->password;
412
+ }
413
+
414
+ /**
415
+ * Returns either the realm specified by the client, or the realm specified by the server.
416
+ * If the server set the value of realm then anything set by our client is overwritten.
417
+ *
418
+ * @return string The value of realm.
419
+ */
420
+ private function getRealm()
421
+ {
422
+ return $this->realm;
423
+ }
424
+
425
+ /**
426
+ * Calculates the value of response according to RFC 2617 and RFC 2069.
427
+ *
428
+ * @return string The value of response
429
+ */
430
+ private function getResponse()
431
+ {
432
+ $HA1 = $this->getHA1();
433
+ $nonce = $this->getNonce();
434
+ $HA2 = $this->getHA2();
435
+
436
+ if(($HA1) AND ($nonce) AND ($HA2)) {
437
+ $qop = $this->getQOP();
438
+
439
+ if(!isset($qop)) {
440
+ $response = $this->hash("{$HA1}:{$nonce}:{$HA2}");
441
+ return $response;
442
+ } else {
443
+ $cnonce = $this->getClientNonce();
444
+ $nc = $this->getNonceCount();
445
+ if(($cnonce) AND ($nc)) {
446
+ $response = $this->hash("{$HA1}:{$nonce}:{$nc}:{$cnonce}:{$qop}:{$HA2}");
447
+ return $response;
448
+ }
449
+ }
450
+ }
451
+ return null;
452
+ }
453
+
454
+ /**
455
+ * Returns the Quality of Protection to be used when authenticating with the server.
456
+ *
457
+ * @return string This will either be auth-int or auth.
458
+ */
459
+ private function getQOP()
460
+ {
461
+ // Has the server specified any options for Quality of Protection
462
+ if(isset($this->qop) AND count($this->qop)) {
463
+ if(($this->options & DigestAuthListener::OPTION_QOP_AUTH_INT) === true) {
464
+ if(in_array('auth-int', $this->qop)) {
465
+ return 'auth-int';
466
+ }
467
+ if(in_array('auth', $this->qop)) {
468
+ return 'auth';
469
+ }
470
+ }
471
+ if(($this->options & DigestAuthListener::OPTION_QOP_AUTH) === true) {
472
+ if(in_array('auth', $this->qop)) {
473
+ return 'auth';
474
+ }
475
+ if(in_array('auth-int', $this->qop)) {
476
+ return 'auth-int';
477
+ }
478
+ }
479
+ }
480
+ // Server has not specified any value for Quality of Protection so return null
481
+ return null;
482
+ }
483
+
484
+ /**
485
+ * Returns the username set by the client to authenticate with the server.
486
+ *
487
+ * @return string The value of username
488
+ */
489
+ private function getUsername()
490
+ {
491
+ return $this->username;
492
+ }
493
+
494
+ /**
495
+ * Returns the uri that we are requesting access to.
496
+ *
497
+ * @return string The value of uri
498
+ */
499
+ private function getUri()
500
+ {
501
+ return $this->uri;
502
+ }
503
+
504
+ /**
505
+ * Calculates the hash for a given value using the algorithm specified by the server.
506
+ *
507
+ * @param string $value The value to be hashed
508
+ *
509
+ * @return string The hashed value.
510
+ */
511
+ private function hash($value)
512
+ {
513
+ $algorithm = $this->getAlgorithm();
514
+ if(($algorithm == 'MD5') OR ($algorithm == 'MD5-sess')) {
515
+ return hash('md5', $value);
516
+ }
517
+ return null;
518
+ }
519
+
520
+ /**
521
+ * Parses the Authentication-Info header received from the server and calls the relevant setter method on each variable received.
522
+ *
523
+ * @param string $authenticationInfo The full Authentication-Info header.
524
+ *
525
+ * @return void
526
+ */
527
+ private function parseAuthenticationInfoHeader($authenticationInfo)
528
+ {
529
+ // Remove "Authentication-Info: " from start of header
530
+ $wwwAuthenticate = substr($wwwAuthenticate, 21, strlen($wwwAuthenticate) - 21);
531
+
532
+ $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
533
+ foreach($nameValuePairs as $name => $value) {
534
+ switch($name) {
535
+ case 'message-qop':
536
+
537
+ break;
538
+ case 'nextnonce':
539
+ // This function needs to only set the Nonce once the rspauth has been verified.
540
+ $this->setNonce($value);
541
+ break;
542
+ case 'rspauth':
543
+ // Check server rspauth value
544
+ break;
545
+ }
546
+ }
547
+ }
548
+
549
+ /**
550
+ * Parses a string of name=value pairs separated by commas and returns and array with the name as the index.
551
+ *
552
+ * @param string $nameValuePairs The string containing the name=value pairs.
553
+ *
554
+ * @return array An array with the name used as the index and the values stored within.
555
+ */
556
+ private function parseNameValuePairs($nameValuePairs)
557
+ {
558
+ $parsedNameValuePairs = array();
559
+ $nameValuePairs = explode(',', $nameValuePairs);
560
+ foreach($nameValuePairs as $nameValuePair) {
561
+ // Trim the Whitespace from the start and end of the name value pair string
562
+ $nameValuePair = trim($nameValuePair);
563
+ // Split $nameValuePair (name=value) into $name and $value
564
+ list($name, $value) = explode('=', $nameValuePair, 2);
565
+ // Remove quotes if the string is quoted
566
+ $value = $this->unquoteString($value);
567
+ // Add pair to array[name] => value
568
+ $parsedNameValuePairs[$name] = $value;
569
+ }
570
+ return $parsedNameValuePairs;
571
+ }
572
+
573
+ /**
574
+ * Parses the server headers received and checks for WWW-Authenticate and Authentication-Info headers.
575
+ * Calls parseWwwAuthenticateHeader() and parseAuthenticationInfoHeader() respectively if either of these headers are present.
576
+ *
577
+ * @param array $headers An array of the headers received by the client.
578
+ *
579
+ * @return void
580
+ */
581
+ private function parseServerHeaders(array $headers)
582
+ {
583
+ foreach($headers as $header) {
584
+ // Check to see if the WWW-Authenticate header is present and if so set $authHeader
585
+ if(strtolower(substr($header, 0, 18)) == 'www-authenticate: ') {
586
+ $wwwAuthenticate = $header;
587
+ $this->parseWwwAuthenticateHeader($wwwAuthenticate);
588
+ }
589
+ // Check to see if the Authentication-Info header is present and if so set $authInfo
590
+ if(strtolower(substr($header, 0, 21)) == 'authentication-info: ') {
591
+ $authenticationInfo = $header;
592
+ $this->parseAuthenticationInfoHeader($wwwAuthenticate);
593
+ }
594
+ }
595
+ }
596
+
597
+ /**
598
+ * Parses the WWW-Authenticate header received from the server and calls the relevant setter method on each variable received.
599
+ *
600
+ * @param string $wwwAuthenticate The full WWW-Authenticate header.
601
+ *
602
+ * @return void
603
+ */
604
+ private function parseWwwAuthenticateHeader($wwwAuthenticate)
605
+ {
606
+ // Remove "WWW-Authenticate: " from start of header
607
+ $wwwAuthenticate = substr($wwwAuthenticate, 18, strlen($wwwAuthenticate) - 18);
608
+ if(substr($wwwAuthenticate, 0, 7) == 'Digest ') {
609
+ $this->setAuthenticationMethod('Digest');
610
+ // Remove "Digest " from start of header
611
+ $wwwAuthenticate = substr($wwwAuthenticate, 7, strlen($wwwAuthenticate) - 7);
612
+
613
+ $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
614
+
615
+ foreach($nameValuePairs as $name => $value) {
616
+ switch($name) {
617
+ case 'algorithm':
618
+ $this->setAlgorithm($value);
619
+ break;
620
+ case 'domain':
621
+ $this->setDomain($value);
622
+ break;
623
+ case 'nonce':
624
+ $this->setNonce($value);
625
+ break;
626
+ case 'realm':
627
+ $this->setRealm($value);
628
+ break;
629
+ case 'opaque':
630
+ $this->setOpaque($value);
631
+ break;
632
+ case 'qop':
633
+ $this->setQOP(explode(',', $value));
634
+ break;
635
+ }
636
+ }
637
+ }
638
+ if (substr($wwwAuthenticate, 0, 6) == 'Basic ') {
639
+ $this->setAuthenticationMethod('Basic');
640
+ // Remove "Basic " from start of header
641
+ $wwwAuthenticate = substr($wwwAuthenticate, 6, strlen($wwwAuthenticate) - 6);
642
+
643
+ $nameValuePairs = $this->parseNameValuePairs($wwwAuthenticate);
644
+
645
+ foreach($nameValuePairs as $name => $value) {
646
+ switch($name) {
647
+ case 'realm':
648
+ $this->setRealm($value);
649
+ break;
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ /**
656
+ * Sets the hashing algorithm to be used. Currently only uses MD5 specified by either MD5 or MD5-sess.
657
+ * RFCs are currently in draft stage for the proposal of SHA-256 and SHA-512-256.
658
+ * Support will be added once the RFC leaves the draft stage.
659
+ *
660
+ * @param string $algorithm The algorithm the server has requested to use.
661
+ *
662
+ * @throws \InvalidArgumentException If $algorithm is set to anything other than MD5 or MD5-sess.
663
+ *
664
+ * @return void
665
+ */
666
+ private function setAlgorithm($algorithm)
667
+ {
668
+ if(($algorithm == 'MD5') OR ($algorithm == 'MD5-sess')) {
669
+ $this->algorithm = $algorithm;
670
+ } else {
671
+ throw new \InvalidArgumentException('DigestAuthListener: Only MD5 and MD5-sess algorithms are currently supported.');
672
+ }
673
+ }
674
+
675
+ /**
676
+ * Sets authentication method to be used. Options are "Digest" and "Basic".
677
+ * If the server and the client are unable to authenticate using Digest then the RFCs state that the server should attempt
678
+ * to authenticate the client using Basic authentication. This ensures that we adhere to that behaviour.
679
+ * This does however create the possibilty of a downgrade attack so it may be an idea to add a way of disabling this functionality
680
+ * as Basic authentication is trivial to decrypt and exposes the username/password to a man-in-the-middle attack.
681
+ *
682
+ * @param string $authenticationMethod The authentication method requested by the server.
683
+ *
684
+ * @throws \InvalidArgumentException If $authenticationMethod is set to anything other than Digest or Basic
685
+ *
686
+ * @return void
687
+ */
688
+ private function setAuthenticationMethod($authenticationMethod)
689
+ {
690
+ if(($authenticationMethod == 'Digest') OR ($authenticationMethod == 'Basic')) {
691
+ $this->authenticationMethod = $authenticationMethod;
692
+ } else {
693
+ throw new \InvalidArgumentException('DigestAuthListener: Only Digest and Basic authentication methods are currently supported.');
694
+ }
695
+ }
696
+
697
+ /**
698
+ * Sets the domain to be authenticated against. THIS IS NOT TO BE CONFUSED WITH THE HOSTNAME/DOMAIN.
699
+ * This is specified by the RFC to be a list of uris separated by spaces that the client will be allowed to access.
700
+ * An RFC in draft stage is proposing the removal of this functionality, it does not seem to be in widespread use.
701
+ *
702
+ * @param string $domain The list of uris separated by spaces that the client will be able to access upon successful authentication.
703
+ *
704
+ * @return void
705
+ */
706
+ private function setDomain($value)
707
+ {
708
+ $this->domain = $value;
709
+ }
710
+
711
+ /**
712
+ * Sets the Entity Body of the Request for use with qop=auth-int
713
+ *
714
+ * @param string $entityBody The body of the entity (The unencoded request minus the headers).
715
+ *
716
+ * @return void
717
+ */
718
+ private function setEntityBody($entityBody = null)
719
+ {
720
+ $this->entityBody = $entityBody;
721
+ }
722
+
723
+ /**
724
+ * Sets the HTTP method being used for the request
725
+ *
726
+ * @param string $method The HTTP method
727
+ *
728
+ * @throws \InvalidArgumentException If $method is set to anything other than GET,POST,PUT,DELETE or HEAD.
729
+ *
730
+ * @return void
731
+ */
732
+ private function setMethod($method = null)
733
+ {
734
+ if($method == 'GET') {
735
+ $this->method = 'GET';
736
+ return;
737
+ }
738
+ if($method == 'POST') {
739
+ $this->method = 'POST';
740
+ return;
741
+ }
742
+ if($method == 'PUT') {
743
+ $this->method = 'PUT';
744
+ return;
745
+ }
746
+ if($method == 'DELETE') {
747
+ $this->method = 'DELETE';
748
+ return;
749
+ }
750
+ if($method == 'HEAD') {
751
+ $this->method = 'HEAD';
752
+ return;
753
+ }
754
+ throw new \InvalidArgumentException('DigestAuthListener: Only GET,POST,PUT,DELETE,HEAD HTTP methods are currently supported.');
755
+ }
756
+
757
+ /**
758
+ * Sets the value of nonce
759
+ *
760
+ * @param string $opaque The server nonce value
761
+ *
762
+ * @return void
763
+ */
764
+ private function setNonce($nonce = null)
765
+ {
766
+ $this->nonce = $nonce;
767
+ }
768
+
769
+ /**
770
+ * Sets the value of opaque
771
+ *
772
+ * @param string $opaque The opaque value
773
+ *
774
+ * @return void
775
+ */
776
+ private function setOpaque($opaque)
777
+ {
778
+ $this->opaque = $opaque;
779
+ }
780
+
781
+ /**
782
+ * Sets the acceptable value(s) for the quality of protection used by the server. Supported values are auth and auth-int.
783
+ * TODO: This method should give precedence to using qop=auth-int first as this offers integrity protection.
784
+ *
785
+ * @param array $qop An array with the values of qop that the server has specified it will accept.
786
+ *
787
+ * @throws \InvalidArgumentException If $qop contains any values other than auth-int or auth.
788
+ *
789
+ * @return void
790
+ */
791
+ private function setQOP(array $qop = array())
792
+ {
793
+ $this->qop = array();
794
+ foreach($qop as $protection) {
795
+ $protection = trim($protection);
796
+ if($protection == 'auth-int') {
797
+ $this->qop[] = 'auth-int';
798
+ } elseif($protection == 'auth') {
799
+ $this->qop[] = 'auth';
800
+ } else {
801
+ throw new \InvalidArgumentException('DigestAuthListener: Only auth-int and auth are supported Quality of Protection mechanisms.');
802
+ }
803
+ }
804
+ }
805
+
806
+ /**
807
+ * Sets the value of uri
808
+ *
809
+ * @param string $uri The uri
810
+ *
811
+ * @return void
812
+ */
813
+ private function setUri($uri = null)
814
+ {
815
+ $this->uri = $uri;
816
+ }
817
+
818
+ /**
819
+ * If a string contains quotation marks at either end this function will strip them. Otherwise it will remain unchanged.
820
+ *
821
+ * @param string $str The string to be stripped of quotation marks.
822
+ *
823
+ * @return string Returns the original string without the quotation marks at either end.
824
+ */
825
+ private function unquoteString($str = null)
826
+ {
827
+ if($str) {
828
+ if(substr($str, 0, 1) == '"') {
829
+ $str = substr($str, 1, strlen($str) - 1);
830
+ }
831
+ if(substr($str, strlen($str) - 1, 1) == '"') {
832
+ $str = substr($str, 0, strlen($str) - 1);
833
+ }
834
+ }
835
+ return $str;
836
+ }
837
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/History/Entry.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener\History;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class Entry
9
+ {
10
+ private $request;
11
+ private $response;
12
+ private $duration;
13
+
14
+ /**
15
+ * Constructor.
16
+ *
17
+ * @param RequestInterface $request The request
18
+ * @param MessageInterface $response The response
19
+ * @param integer $duration The duration in seconds
20
+ */
21
+ public function __construct(RequestInterface $request, MessageInterface $response, $duration = null)
22
+ {
23
+ $this->request = $request;
24
+ $this->response = $response;
25
+ $this->duration = $duration;
26
+ }
27
+
28
+ public function getRequest()
29
+ {
30
+ return $this->request;
31
+ }
32
+
33
+ public function getResponse()
34
+ {
35
+ return $this->response;
36
+ }
37
+
38
+ public function getDuration()
39
+ {
40
+ return $this->duration;
41
+ }
42
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/History/Journal.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener\History;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class Journal implements \Countable, \IteratorAggregate
9
+ {
10
+ private $entries = array();
11
+ private $limit = 10;
12
+
13
+ /**
14
+ * Records an entry in the journal.
15
+ *
16
+ * @param RequestInterface $request The request
17
+ * @param MessageInterface $response The response
18
+ * @param integer $duration The duration in seconds
19
+ */
20
+ public function record(RequestInterface $request, MessageInterface $response, $duration = null)
21
+ {
22
+ $this->addEntry(new Entry($request, $response, $duration));
23
+ }
24
+
25
+ public function addEntry(Entry $entry)
26
+ {
27
+ array_push($this->entries, $entry);
28
+ $this->entries = array_slice($this->entries, $this->getLimit() * -1);
29
+ end($this->entries);
30
+ }
31
+
32
+ public function getEntries()
33
+ {
34
+ return $this->entries;
35
+ }
36
+
37
+ public function getLast()
38
+ {
39
+ return end($this->entries);
40
+ }
41
+
42
+ public function getLastRequest()
43
+ {
44
+ return $this->getLast()->getRequest();
45
+ }
46
+
47
+ public function getLastResponse()
48
+ {
49
+ return $this->getLast()->getResponse();
50
+ }
51
+
52
+ public function clear()
53
+ {
54
+ $this->entries = array();
55
+ }
56
+
57
+ public function count()
58
+ {
59
+ return count($this->entries);
60
+ }
61
+
62
+ public function setLimit($limit)
63
+ {
64
+ $this->limit = $limit;
65
+ }
66
+
67
+ public function getLimit()
68
+ {
69
+ return $this->limit;
70
+ }
71
+
72
+ public function getIterator()
73
+ {
74
+ return new \ArrayIterator(array_reverse($this->entries));
75
+ }
76
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/HistoryListener.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Listener\History\Journal;
6
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
7
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
8
+
9
+ class HistoryListener implements ListenerInterface
10
+ {
11
+ private $journal;
12
+ private $startTime;
13
+
14
+ public function __construct(Journal $journal)
15
+ {
16
+ $this->journal = $journal;
17
+ }
18
+
19
+ public function getJournal()
20
+ {
21
+ return $this->journal;
22
+ }
23
+
24
+ public function preSend(RequestInterface $request)
25
+ {
26
+ $this->startTime = microtime(true);
27
+ }
28
+
29
+ public function postSend(RequestInterface $request, MessageInterface $response)
30
+ {
31
+ $this->journal->record($request, $response, microtime(true) - $this->startTime);
32
+ }
33
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/ListenerChain.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class ListenerChain implements ListenerInterface
9
+ {
10
+ private $listeners;
11
+
12
+ public function __construct(array $listeners = array())
13
+ {
14
+ $this->listeners = $listeners;
15
+ }
16
+
17
+ public function addListener(ListenerInterface $listener)
18
+ {
19
+ $this->listeners[] = $listener;
20
+ }
21
+
22
+ public function getListeners()
23
+ {
24
+ return $this->listeners;
25
+ }
26
+
27
+ public function preSend(RequestInterface $request)
28
+ {
29
+ foreach ($this->listeners as $listener) {
30
+ $listener->preSend($request);
31
+ }
32
+ }
33
+
34
+ public function postSend(RequestInterface $request, MessageInterface $response)
35
+ {
36
+ foreach ($this->listeners as $listener) {
37
+ $listener->postSend($request, $response);
38
+ }
39
+ }
40
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/ListenerInterface.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ interface ListenerInterface
9
+ {
10
+ public function preSend(RequestInterface $request);
11
+ public function postSend(RequestInterface $request, MessageInterface $response);
12
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Listener/LoggerListener.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Listener;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+ use WebinterpretConnector\Buzz\Exception\InvalidArgumentException;
8
+
9
+ class LoggerListener implements ListenerInterface
10
+ {
11
+ private $logger;
12
+ private $prefix;
13
+ private $startTime;
14
+
15
+ public function __construct($logger, $prefix = null)
16
+ {
17
+ if (!is_callable($logger)) {
18
+ throw new InvalidArgumentException('The logger must be a callable.');
19
+ }
20
+
21
+ $this->logger = $logger;
22
+ $this->prefix = $prefix;
23
+ }
24
+
25
+ public function preSend(RequestInterface $request)
26
+ {
27
+ $this->startTime = microtime(true);
28
+ }
29
+
30
+ public function postSend(RequestInterface $request, MessageInterface $response)
31
+ {
32
+ $seconds = microtime(true) - $this->startTime;
33
+
34
+ call_user_func($this->logger, sprintf('%sSent "%s %s%s" in %dms', $this->prefix, $request->getMethod(), $request->getHost(), $request->getResource(), round($seconds * 1000)));
35
+ }
36
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/AbstractMessage.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message;
4
+
5
+ abstract class AbstractMessage implements MessageInterface
6
+ {
7
+ private $headers = array();
8
+ private $content;
9
+
10
+ /**
11
+ * Returns the value of a header.
12
+ *
13
+ * @param string $name
14
+ * @param string|boolean $glue Glue for implode, or false to return an array
15
+ *
16
+ * @return string|array|null
17
+ */
18
+ public function getHeader($name, $glue = "\r\n")
19
+ {
20
+ $needle = $name.':';
21
+
22
+ $values = array();
23
+ foreach ($this->getHeaders() as $header) {
24
+ if (0 === stripos($header, $needle)) {
25
+ $values[] = trim(substr($header, strlen($needle)));
26
+ }
27
+ }
28
+
29
+ if (false === $glue) {
30
+ return $values;
31
+ } else {
32
+ return count($values) ? implode($glue, $values) : null;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Returns a header's attributes.
38
+ *
39
+ * @param string $name A header name
40
+ *
41
+ * @return array An associative array of attributes
42
+ */
43
+ public function getHeaderAttributes($name)
44
+ {
45
+ $attributes = array();
46
+ foreach ($this->getHeader($name, false) as $header) {
47
+ if (false !== strpos($header, ';')) {
48
+ // remove header value
49
+ list(, $header) = explode(';', $header, 2);
50
+
51
+ // loop through attribute key=value pairs
52
+ foreach (array_map('trim', explode(';', trim($header))) as $pair) {
53
+ list($key, $value) = explode('=', $pair);
54
+ $attributes[$key] = $value;
55
+ }
56
+ }
57
+ }
58
+
59
+ return $attributes;
60
+ }
61
+
62
+ /**
63
+ * Returns the value of a particular header attribute.
64
+ *
65
+ * @param string $header A header name
66
+ * @param string $attribute An attribute name
67
+ *
68
+ * @return string|null The value of the attribute or null if it isn't set
69
+ */
70
+ public function getHeaderAttribute($header, $attribute)
71
+ {
72
+ $attributes = $this->getHeaderAttributes($header);
73
+
74
+ if (isset($attributes[$attribute])) {
75
+ return $attributes[$attribute];
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Returns the current message as a DOMDocument.
81
+ *
82
+ * @return \DOMDocument
83
+ */
84
+ public function toDomDocument()
85
+ {
86
+ $revert = libxml_use_internal_errors(true);
87
+
88
+ $document = new \DOMDocument('1.0', $this->getHeaderAttribute('Content-Type', 'charset') ?: 'UTF-8');
89
+ if (0 === strpos($this->getHeader('Content-Type'), 'text/xml')) {
90
+ $document->loadXML($this->getContent());
91
+ } else {
92
+ $document->loadHTML($this->getContent());
93
+ }
94
+
95
+ libxml_use_internal_errors($revert);
96
+
97
+ return $document;
98
+ }
99
+
100
+ public function setHeaders(array $headers)
101
+ {
102
+ $this->headers = $this->flattenHeaders($headers);
103
+ }
104
+
105
+ public function addHeader($header)
106
+ {
107
+ $this->headers[] = $header;
108
+ }
109
+
110
+ public function addHeaders(array $headers)
111
+ {
112
+ $this->headers = array_merge($this->headers, $this->flattenHeaders($headers));
113
+ }
114
+
115
+ public function getHeaders()
116
+ {
117
+ return $this->headers;
118
+ }
119
+
120
+ public function setContent($content)
121
+ {
122
+ $this->content = $content;
123
+ }
124
+
125
+ public function getContent()
126
+ {
127
+ return $this->content;
128
+ }
129
+
130
+ public function __toString()
131
+ {
132
+ $string = implode("\r\n", $this->getHeaders())."\r\n";
133
+
134
+ if ($content = $this->getContent()) {
135
+ $string .= "\r\n$content\r\n";
136
+ }
137
+
138
+ return $string;
139
+ }
140
+
141
+ protected function flattenHeaders(array $headers)
142
+ {
143
+ $flattened = array();
144
+ foreach ($headers as $key => $header) {
145
+ if (is_int($key)) {
146
+ $flattened[] = $header;
147
+ } else {
148
+ $flattened[] = $key.': '.$header;
149
+ }
150
+ }
151
+
152
+ return $flattened;
153
+ }
154
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Factory/Factory.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Factory;
4
+
5
+ use WebinterpretConnector\Buzz\Message\Form\FormRequest;
6
+ use WebinterpretConnector\Buzz\Message\Request;
7
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
8
+ use WebinterpretConnector\Buzz\Message\Response;
9
+
10
+ class Factory implements FactoryInterface
11
+ {
12
+ public function createRequest($method = RequestInterface::METHOD_GET, $resource = '/', $host = null)
13
+ {
14
+ return new Request($method, $resource, $host);
15
+ }
16
+
17
+ public function createFormRequest($method = RequestInterface::METHOD_POST, $resource = '/', $host = null)
18
+ {
19
+ return new FormRequest($method, $resource, $host);
20
+ }
21
+
22
+ public function createResponse()
23
+ {
24
+ return new Response();
25
+ }
26
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Factory/FactoryInterface.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Factory;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+
7
+ interface FactoryInterface
8
+ {
9
+ public function createRequest($method = RequestInterface::METHOD_GET, $resource = '/', $host = null);
10
+ public function createFormRequest($method = RequestInterface::METHOD_POST, $resource = '/', $host = null);
11
+ public function createResponse();
12
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormRequest.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Form;
4
+
5
+ use WebinterpretConnector\Buzz\Message\Request;
6
+ use WebinterpretConnector\Buzz\Exception\LogicException;
7
+
8
+ /**
9
+ * FormRequest.
10
+ *
11
+ * $request = new FormRequest();
12
+ * $request->setField('user[name]', 'Kris Wallsmith');
13
+ * $request->setField('user[image]', new FormUpload('/path/to/image.jpg'));
14
+ *
15
+ * @author Marc Weistroff <marc.weistroff@sensio.com>
16
+ * @author Kris Wallsmith <kris.wallsmith@gmail.com>
17
+ */
18
+ class FormRequest extends Request implements FormRequestInterface
19
+ {
20
+ private $fields = array();
21
+ private $boundary;
22
+
23
+ /**
24
+ * Constructor.
25
+ *
26
+ * Defaults to POST rather than GET.
27
+ */
28
+ public function __construct($method = self::METHOD_POST, $resource = '/', $host = null)
29
+ {
30
+ parent::__construct($method, $resource, $host);
31
+ }
32
+
33
+ /**
34
+ * Sets the value of a form field.
35
+ *
36
+ * If the value is an array it will be flattened and one field value will
37
+ * be added for each leaf.
38
+ */
39
+ public function setField($name, $value)
40
+ {
41
+ if (is_array($value)) {
42
+ $this->addFields(array($name => $value));
43
+
44
+ return;
45
+ }
46
+
47
+ if ('[]' == substr($name, -2)) {
48
+ $this->fields[substr($name, 0, -2)][] = $value;
49
+ } else {
50
+ $this->fields[$name] = $value;
51
+ }
52
+ }
53
+
54
+ public function addFields(array $fields)
55
+ {
56
+ foreach ($this->flattenArray($fields) as $name => $value) {
57
+ $this->setField($name, $value);
58
+ }
59
+ }
60
+
61
+ public function setFields(array $fields)
62
+ {
63
+ $this->fields = array();
64
+ $this->addFields($fields);
65
+ }
66
+
67
+ public function getFields()
68
+ {
69
+ return $this->fields;
70
+ }
71
+
72
+ public function getResource()
73
+ {
74
+ $resource = parent::getResource();
75
+
76
+ if (!$this->isSafe() || !$this->fields) {
77
+ return $resource;
78
+ }
79
+
80
+ // append the query string
81
+ $resource .= false === strpos($resource, '?') ? '?' : '&';
82
+ $resource .= http_build_query($this->fields);
83
+
84
+ return $resource;
85
+ }
86
+
87
+ public function setContent($content)
88
+ {
89
+ throw new \BadMethodCallException('It is not permitted to set the content.');
90
+ }
91
+
92
+ public function getHeaders()
93
+ {
94
+ $headers = parent::getHeaders();
95
+
96
+ if ($this->isSafe()) {
97
+ return $headers;
98
+ }
99
+
100
+ if ($this->isMultipart()) {
101
+ $headers[] = 'Content-Type: multipart/form-data; boundary='.$this->getBoundary();
102
+ } else {
103
+ $headers[] = 'Content-Type: application/x-www-form-urlencoded';
104
+ }
105
+
106
+ return $headers;
107
+ }
108
+
109
+ public function getContent()
110
+ {
111
+ if ($this->isSafe()) {
112
+ return;
113
+ }
114
+
115
+ if (!$this->isMultipart()) {
116
+ return http_build_query($this->fields, '', '&');
117
+ }
118
+
119
+ $content = '';
120
+
121
+ foreach ($this->fields as $name => $values) {
122
+ $content .= '--'.$this->getBoundary()."\r\n";
123
+ if ($values instanceof FormUploadInterface) {
124
+ if (!$values->getFilename()) {
125
+ throw new LogicException(sprintf('Form upload at "%s" does not include a filename.', $name));
126
+ }
127
+
128
+ $values->setName($name);
129
+ $content .= (string) $values;
130
+ } else {
131
+ foreach (is_array($values) ? $values : array($values) as $value) {
132
+ $content .= "Content-Disposition: form-data; name=\"$name\"\r\n";
133
+ $content .= "\r\n";
134
+ $content .= $value."\r\n";
135
+ }
136
+ }
137
+ }
138
+
139
+ $content .= '--'.$this->getBoundary().'--';
140
+
141
+ return $content;
142
+ }
143
+
144
+ // private
145
+
146
+ private function flattenArray(array $values, $prefix = '', $format = '%s')
147
+ {
148
+ $flat = array();
149
+
150
+ foreach ($values as $name => $value) {
151
+ $flatName = $prefix.sprintf($format, $name);
152
+
153
+ if (is_array($value)) {
154
+ $flat += $this->flattenArray($value, $flatName, '[%s]');
155
+ } else {
156
+ $flat[$flatName] = $value;
157
+ }
158
+ }
159
+
160
+ return $flat;
161
+ }
162
+
163
+ private function isSafe()
164
+ {
165
+ return in_array($this->getMethod(), array(self::METHOD_GET, self::METHOD_HEAD));
166
+ }
167
+
168
+ private function isMultipart()
169
+ {
170
+ foreach ($this->fields as $name => $value) {
171
+ if (is_object($value) && $value instanceof FormUploadInterface) {
172
+ return true;
173
+ }
174
+ }
175
+
176
+ return false;
177
+ }
178
+
179
+ private function getBoundary()
180
+ {
181
+ if (!$this->boundary) {
182
+ $this->boundary = sha1(rand(11111, 99999).time().uniqid());
183
+ }
184
+
185
+ return $this->boundary;
186
+ }
187
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormRequestInterface.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Form;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+
7
+ /**
8
+ * An HTTP request message sent by a web form.
9
+ *
10
+ * @author Kris Wallsmith <kris.wallsmith@gmail.com>
11
+ */
12
+ interface FormRequestInterface extends RequestInterface
13
+ {
14
+ /**
15
+ * Returns an array of field names and values.
16
+ *
17
+ * @return array A array of names and values
18
+ */
19
+ public function getFields();
20
+
21
+ /**
22
+ * Sets the form fields for the current request.
23
+ *
24
+ * @param array $fields An array of field names and values
25
+ */
26
+ public function setFields(array $fields);
27
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormUpload.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Form;
4
+
5
+ use WebinterpretConnector\Buzz\Message\AbstractMessage;
6
+
7
+ class FormUpload extends AbstractMessage implements FormUploadInterface
8
+ {
9
+ private $name;
10
+ private $filename;
11
+ private $contentType;
12
+ private $file;
13
+
14
+ public function __construct($file = null, $contentType = null)
15
+ {
16
+ if ($file) {
17
+ $this->loadContent($file);
18
+ }
19
+
20
+ $this->contentType = $contentType;
21
+ }
22
+
23
+ public function getName()
24
+ {
25
+ return $this->name;
26
+ }
27
+
28
+ public function setName($name)
29
+ {
30
+ $this->name = $name;
31
+ }
32
+
33
+ public function getFilename()
34
+ {
35
+ if ($this->filename) {
36
+ return $this->filename;
37
+ } elseif ($this->file) {
38
+ return basename($this->file);
39
+ }
40
+ }
41
+
42
+ public function setFilename($filename)
43
+ {
44
+ $this->filename = $filename;
45
+ }
46
+
47
+ public function getContentType()
48
+ {
49
+ return $this->contentType ?: $this->detectContentType() ?: 'application/octet-stream';
50
+ }
51
+
52
+ public function setContentType($contentType)
53
+ {
54
+ $this->contentType = $contentType;
55
+ }
56
+
57
+ /**
58
+ * Prepends Content-Disposition and Content-Type headers.
59
+ */
60
+ public function getHeaders()
61
+ {
62
+ $headers = array('Content-Disposition: form-data');
63
+
64
+ if ($name = $this->getName()) {
65
+ $headers[0] .= sprintf('; name="%s"', $name);
66
+ }
67
+
68
+ if ($filename = $this->getFilename()) {
69
+ $headers[0] .= sprintf('; filename="%s"', $filename);
70
+ }
71
+
72
+ if ($contentType = $this->getContentType()) {
73
+ $headers[] = 'Content-Type: '.$contentType;
74
+ }
75
+
76
+ return array_merge($headers, parent::getHeaders());
77
+ }
78
+
79
+ /**
80
+ * Loads the content from a file.
81
+ */
82
+ public function loadContent($file)
83
+ {
84
+ $this->file = $file;
85
+
86
+ parent::setContent(null);
87
+ }
88
+
89
+ public function setContent($content)
90
+ {
91
+ parent::setContent($content);
92
+
93
+ $this->file = null;
94
+ }
95
+
96
+ public function getFile()
97
+ {
98
+ return $this->file;
99
+ }
100
+
101
+ public function getContent()
102
+ {
103
+ return $this->file ? file_get_contents($this->file) : parent::getContent();
104
+ }
105
+
106
+ // private
107
+
108
+ private function detectContentType()
109
+ {
110
+ if (!class_exists('finfo', false)) {
111
+ return false;
112
+ }
113
+
114
+ $finfo = new \finfo(FILEINFO_MIME_TYPE);
115
+
116
+ return $this->file ? $finfo->file($this->file) : $finfo->buffer(parent::getContent());
117
+ }
118
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Form/FormUploadInterface.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message\Form;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+
7
+ interface FormUploadInterface extends MessageInterface
8
+ {
9
+ public function setName($name);
10
+ public function getFile();
11
+ public function getFilename();
12
+ public function getContentType();
13
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/MessageInterface.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message;
4
+
5
+ /**
6
+ * An HTTP message.
7
+ *
8
+ * @author Kris Wallsmith <kris.wallsmith@gmail.com>
9
+ */
10
+ interface MessageInterface
11
+ {
12
+ /**
13
+ * Returns a header value.
14
+ *
15
+ * @param string $name A header name
16
+ * @param string|boolean $glue Glue for implode, or false to return an array
17
+ *
18
+ * @return string|array|null The header value(s)
19
+ */
20
+ public function getHeader($name, $glue = "\r\n");
21
+
22
+ /**
23
+ * Returns an array of header lines.
24
+ *
25
+ * @return array An array of header lines (integer indexes, e.g. ["Header: value"])
26
+ */
27
+ public function getHeaders();
28
+
29
+ /**
30
+ * Sets all headers on the current message.
31
+ *
32
+ * Headers can be complete ["Header: value"] pairs or an associative array ["Header" => "value"]
33
+ *
34
+ * @param array $headers An array of header lines
35
+ */
36
+ public function setHeaders(array $headers);
37
+
38
+ /**
39
+ * Adds a header to this message.
40
+ *
41
+ * @param string $header A header line
42
+ */
43
+ public function addHeader($header);
44
+
45
+ /**
46
+ * Adds a set of headers to this message.
47
+ *
48
+ * Headers can be complete ["Header: value"] pairs or an associative array ["Header" => "value"]
49
+ *
50
+ * @param array $headers Headers
51
+ */
52
+ public function addHeaders(array $headers);
53
+
54
+ /**
55
+ * Returns the content of the message.
56
+ *
57
+ * @return string The message content
58
+ */
59
+ public function getContent();
60
+
61
+ /**
62
+ * Sets the content of the message.
63
+ *
64
+ * @param string $content The message content
65
+ */
66
+ public function setContent($content);
67
+
68
+ /**
69
+ * Returns the message document.
70
+ *
71
+ * @return string The message
72
+ */
73
+ public function __toString();
74
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Request.php ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message;
4
+
5
+ use WebinterpretConnector\Buzz\Util\Url;
6
+
7
+ class Request extends AbstractMessage implements RequestInterface
8
+ {
9
+ private $method;
10
+ private $resource;
11
+ private $host;
12
+ private $protocolVersion = 1.1;
13
+
14
+ /**
15
+ * Constructor.
16
+ *
17
+ * @param string $method
18
+ * @param string $resource
19
+ * @param string $host
20
+ */
21
+ public function __construct($method = self::METHOD_GET, $resource = '/', $host = null)
22
+ {
23
+ $this->method = strtoupper($method);
24
+ $this->resource = $resource;
25
+ $this->host = $host;
26
+ }
27
+
28
+ public function setHeaders(array $headers)
29
+ {
30
+ parent::setHeaders(array());
31
+
32
+ foreach ($this->flattenHeaders($headers) as $header) {
33
+ $this->addHeader($header);
34
+ }
35
+ }
36
+
37
+ public function addHeader($header)
38
+ {
39
+ if (0 === stripos(substr($header, -8), 'HTTP/1.') && 3 == count($parts = explode(' ', $header))) {
40
+ list($method, $resource, $protocolVersion) = $parts;
41
+
42
+ $this->setMethod($method);
43
+ $this->setResource($resource);
44
+ $this->setProtocolVersion((float) substr($protocolVersion, 5));
45
+ } else {
46
+ parent::addHeader($header);
47
+ }
48
+ }
49
+
50
+ public function setMethod($method)
51
+ {
52
+ $this->method = strtoupper($method);
53
+ }
54
+
55
+ public function getMethod()
56
+ {
57
+ return $this->method;
58
+ }
59
+
60
+ public function setResource($resource)
61
+ {
62
+ $this->resource = $resource;
63
+ }
64
+
65
+ public function getResource()
66
+ {
67
+ return $this->resource;
68
+ }
69
+
70
+ public function setHost($host)
71
+ {
72
+ $this->host = $host;
73
+ }
74
+
75
+ public function getHost()
76
+ {
77
+ return $this->host;
78
+ }
79
+
80
+ public function setProtocolVersion($protocolVersion)
81
+ {
82
+ $this->protocolVersion = $protocolVersion;
83
+ }
84
+
85
+ public function getProtocolVersion()
86
+ {
87
+ return $this->protocolVersion;
88
+ }
89
+
90
+ /**
91
+ * A convenience method for getting the full URL of the current request.
92
+ *
93
+ * @return string
94
+ */
95
+ public function getUrl()
96
+ {
97
+ return $this->getHost().$this->getResource();
98
+ }
99
+
100
+ /**
101
+ * A convenience method for populating the current request from a URL.
102
+ *
103
+ * @param Url|string $url An URL
104
+ */
105
+ public function fromUrl($url)
106
+ {
107
+ if (!$url instanceof Url) {
108
+ $url = new Url($url);
109
+ }
110
+
111
+ $url->applyToRequest($this);
112
+ }
113
+
114
+ /**
115
+ * Returns true if the current request is secure.
116
+ *
117
+ * @return boolean
118
+ */
119
+ public function isSecure()
120
+ {
121
+ return 'https' == parse_url($this->getHost(), PHP_URL_SCHEME);
122
+ }
123
+
124
+ /**
125
+ * Merges cookie headers on the way out.
126
+ */
127
+ public function getHeaders()
128
+ {
129
+ return $this->mergeCookieHeaders(parent::getHeaders());
130
+ }
131
+
132
+ /**
133
+ * Returns a string representation of the current request.
134
+ *
135
+ * @return string
136
+ */
137
+ public function __toString()
138
+ {
139
+ $string = sprintf("%s %s HTTP/%.1f\r\n", $this->getMethod(), $this->getResource(), $this->getProtocolVersion());
140
+
141
+ if ($host = $this->getHost()) {
142
+ $string .= 'Host: '.$host."\r\n";
143
+ }
144
+
145
+ if ($parent = trim(parent::__toString())) {
146
+ $string .= $parent."\r\n";
147
+ }
148
+
149
+ return $string;
150
+ }
151
+
152
+ // private
153
+
154
+ private function mergeCookieHeaders(array $headers)
155
+ {
156
+ $cookieHeader = null;
157
+ $needle = 'Cookie:';
158
+
159
+ foreach ($headers as $i => $header) {
160
+ if (0 !== stripos($header, $needle)) {
161
+ continue;
162
+ }
163
+
164
+ if (null === $cookieHeader) {
165
+ $cookieHeader = $i;
166
+ } else {
167
+ $headers[$cookieHeader] .= '; '.trim(substr($header, strlen($needle)));
168
+ unset($headers[$i]);
169
+ }
170
+ }
171
+
172
+ return array_values($headers);
173
+ }
174
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/RequestInterface.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message;
4
+
5
+ /**
6
+ * An HTTP request message.
7
+ *
8
+ * @author Kris Wallsmith <kris.wallsmith@gmail.com>
9
+ */
10
+ interface RequestInterface extends MessageInterface
11
+ {
12
+ const METHOD_OPTIONS = 'OPTIONS';
13
+ const METHOD_GET = 'GET';
14
+ const METHOD_HEAD = 'HEAD';
15
+ const METHOD_POST = 'POST';
16
+ const METHOD_PUT = 'PUT';
17
+ const METHOD_DELETE = 'DELETE';
18
+ const METHOD_PATCH = 'PATCH';
19
+
20
+ /**
21
+ * Returns the HTTP method of the current request.
22
+ *
23
+ * @return string An HTTP method
24
+ */
25
+ public function getMethod();
26
+
27
+ /**
28
+ * Sets the HTTP method of the current request.
29
+ *
30
+ * @param string $method The request method
31
+ */
32
+ public function setMethod($method);
33
+
34
+ /**
35
+ * Returns the resource portion of the request line.
36
+ *
37
+ * @return string The resource requested
38
+ */
39
+ public function getResource();
40
+
41
+ /**
42
+ * Sets the resource for the current request.
43
+ *
44
+ * @param string $resource The resource being requested
45
+ */
46
+ public function setResource($resource);
47
+
48
+ /**
49
+ * Returns the protocol version of the current request.
50
+ *
51
+ * @return float The protocol version
52
+ */
53
+ public function getProtocolVersion();
54
+
55
+ /**
56
+ * Returns the value of the host header.
57
+ *
58
+ * @return string|null The host
59
+ */
60
+ public function getHost();
61
+
62
+ /**
63
+ * Sets the host for the current request.
64
+ *
65
+ * @param string $host The host
66
+ */
67
+ public function setHost($host);
68
+
69
+ /**
70
+ * Checks if the current request is secure.
71
+ *
72
+ * @return Boolean True if the request is secure
73
+ */
74
+ public function isSecure();
75
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Message/Response.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Message;
4
+
5
+ class Response extends AbstractMessage
6
+ {
7
+ private $protocolVersion;
8
+ private $statusCode;
9
+ private $reasonPhrase;
10
+
11
+ /**
12
+ * Returns the protocol version of the current response.
13
+ *
14
+ * @return float
15
+ */
16
+ public function getProtocolVersion()
17
+ {
18
+ if (null === $this->protocolVersion) {
19
+ $this->parseStatusLine();
20
+ }
21
+
22
+ return $this->protocolVersion ?: null;
23
+ }
24
+
25
+ /**
26
+ * Returns the status code of the current response.
27
+ *
28
+ * @return integer
29
+ */
30
+ public function getStatusCode()
31
+ {
32
+ if (null === $this->statusCode) {
33
+ $this->parseStatusLine();
34
+ }
35
+
36
+ return $this->statusCode ?: null;
37
+ }
38
+
39
+ /**
40
+ * Returns the reason phrase for the current response.
41
+ *
42
+ * @return string
43
+ */
44
+ public function getReasonPhrase()
45
+ {
46
+ if (null === $this->reasonPhrase) {
47
+ $this->parseStatusLine();
48
+ }
49
+
50
+ return $this->reasonPhrase ?: null;
51
+ }
52
+
53
+ public function setHeaders(array $headers)
54
+ {
55
+ parent::setHeaders($headers);
56
+
57
+ $this->resetStatusLine();
58
+ }
59
+
60
+ public function addHeader($header)
61
+ {
62
+ parent::addHeader($header);
63
+
64
+ $this->resetStatusLine();
65
+ }
66
+
67
+ public function addHeaders(array $headers)
68
+ {
69
+ parent::addHeaders($headers);
70
+
71
+ $this->resetStatusLine();
72
+ }
73
+
74
+ /**
75
+ * Is response invalid?
76
+ *
77
+ * @return Boolean
78
+ */
79
+ public function isInvalid()
80
+ {
81
+ return $this->getStatusCode() < 100 || $this->getStatusCode() >= 600;
82
+ }
83
+
84
+ /**
85
+ * Is response informative?
86
+ *
87
+ * @return Boolean
88
+ */
89
+ public function isInformational()
90
+ {
91
+ return $this->getStatusCode() >= 100 && $this->getStatusCode() < 200;
92
+ }
93
+
94
+ /**
95
+ * Is response successful?
96
+ *
97
+ * @return Boolean
98
+ */
99
+ public function isSuccessful()
100
+ {
101
+ return $this->getStatusCode() >= 200 && $this->getStatusCode() < 300;
102
+ }
103
+
104
+ /**
105
+ * Is the response a redirect?
106
+ *
107
+ * @return Boolean
108
+ */
109
+ public function isRedirection()
110
+ {
111
+ return $this->getStatusCode() >= 300 && $this->getStatusCode() < 400;
112
+ }
113
+
114
+ /**
115
+ * Is there a client error?
116
+ *
117
+ * @return Boolean
118
+ */
119
+ public function isClientError()
120
+ {
121
+ return $this->getStatusCode() >= 400 && $this->getStatusCode() < 500;
122
+ }
123
+
124
+ /**
125
+ * Was there a server side error?
126
+ *
127
+ * @return Boolean
128
+ */
129
+ public function isServerError()
130
+ {
131
+ return $this->getStatusCode() >= 500 && $this->getStatusCode() < 600;
132
+ }
133
+
134
+ /**
135
+ * Is the response OK?
136
+ *
137
+ * @return Boolean
138
+ */
139
+ public function isOk()
140
+ {
141
+ return 200 === $this->getStatusCode();
142
+ }
143
+
144
+ /**
145
+ * Is the reponse forbidden?
146
+ *
147
+ * @return Boolean
148
+ */
149
+ public function isForbidden()
150
+ {
151
+ return 403 === $this->getStatusCode();
152
+ }
153
+
154
+ /**
155
+ * Is the response a not found error?
156
+ *
157
+ * @return Boolean
158
+ */
159
+ public function isNotFound()
160
+ {
161
+ return 404 === $this->getStatusCode();
162
+ }
163
+
164
+ /**
165
+ * Is the response empty?
166
+ *
167
+ * @return Boolean
168
+ */
169
+ public function isEmpty()
170
+ {
171
+ return in_array($this->getStatusCode(), array(201, 204, 304));
172
+ }
173
+
174
+ // private
175
+
176
+ private function parseStatusLine()
177
+ {
178
+ $headers = $this->getHeaders();
179
+
180
+ if (isset($headers[0]) && 3 == count($parts = explode(' ', $headers[0], 3))) {
181
+ $this->protocolVersion = (float) substr($parts[0], 5);
182
+ $this->statusCode = (integer) $parts[1];
183
+ $this->reasonPhrase = $parts[2];
184
+ } else {
185
+ $this->protocolVersion = $this->statusCode = $this->reasonPhrase = false;
186
+ }
187
+ }
188
+
189
+ private function resetStatusLine()
190
+ {
191
+ $this->protocolVersion = $this->statusCode = $this->reasonPhrase = null;
192
+ }
193
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Util/Cookie.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Util;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+
7
+ class Cookie
8
+ {
9
+ const ATTR_DOMAIN = 'domain';
10
+ const ATTR_PATH = 'path';
11
+ const ATTR_SECURE = 'secure';
12
+ const ATTR_MAX_AGE = 'max-age';
13
+ const ATTR_EXPIRES = 'expires';
14
+
15
+ protected $name;
16
+ protected $value;
17
+ protected $attributes = array();
18
+ protected $createdAt;
19
+
20
+ /**
21
+ * Constructor.
22
+ */
23
+ public function __construct()
24
+ {
25
+ $this->createdAt = time();
26
+ }
27
+
28
+ /**
29
+ * Returns true if the current cookie matches the supplied request.
30
+ *
31
+ * @return boolean
32
+ */
33
+ public function matchesRequest(RequestInterface $request)
34
+ {
35
+ // domain
36
+ if (!$this->matchesDomain(parse_url($request->getHost(), PHP_URL_HOST))) {
37
+ return false;
38
+ }
39
+
40
+ // path
41
+ if (!$this->matchesPath($request->getResource())) {
42
+ return false;
43
+ }
44
+
45
+ // secure
46
+ if ($this->hasAttribute(static::ATTR_SECURE) && !$request->isSecure()) {
47
+ return false;
48
+ }
49
+
50
+ return true;
51
+ }
52
+
53
+ /**
54
+ * Returns true of the current cookie has expired.
55
+ *
56
+ * Checks the max-age and expires attributes.
57
+ *
58
+ * @return boolean Whether the current cookie has expired
59
+ */
60
+ public function isExpired()
61
+ {
62
+ $maxAge = $this->getAttribute(static::ATTR_MAX_AGE);
63
+ if ($maxAge && time() - $this->getCreatedAt() > $maxAge) {
64
+ return true;
65
+ }
66
+
67
+ $expires = $this->getAttribute(static::ATTR_EXPIRES);
68
+ if ($expires && strtotime($expires) < time()) {
69
+ return true;
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ /**
76
+ * Returns true if the current cookie matches the supplied domain.
77
+ *
78
+ * @param string $domain A domain hostname
79
+ *
80
+ * @return boolean
81
+ */
82
+ public function matchesDomain($domain)
83
+ {
84
+ $cookieDomain = $this->getAttribute(static::ATTR_DOMAIN);
85
+
86
+ if (0 === strpos($cookieDomain, '.')) {
87
+ $pattern = '/\b'.preg_quote(substr($cookieDomain, 1), '/').'$/i';
88
+
89
+ return (boolean) preg_match($pattern, $domain);
90
+ } else {
91
+ return 0 == strcasecmp($cookieDomain, $domain);
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Returns true if the current cookie matches the supplied path.
97
+ *
98
+ * @param string $path A path
99
+ *
100
+ * @return boolean
101
+ */
102
+ public function matchesPath($path)
103
+ {
104
+ $needle = $this->getAttribute(static::ATTR_PATH);
105
+
106
+ return null === $needle || 0 === strpos($path, $needle);
107
+ }
108
+
109
+ /**
110
+ * Populates the current cookie with data from the supplied Set-Cookie header.
111
+ *
112
+ * @param string $header A Set-Cookie header
113
+ * @param string $issuingDomain The domain that issued the header
114
+ */
115
+ public function fromSetCookieHeader($header, $issuingDomain)
116
+ {
117
+ list($this->name, $header) = explode('=', $header, 2);
118
+ if (false === strpos($header, ';')) {
119
+ $this->value = $header;
120
+ $header = null;
121
+ } else {
122
+ list($this->value, $header) = explode(';', $header, 2);
123
+ }
124
+
125
+ $this->clearAttributes();
126
+ foreach (array_map('trim', explode(';', trim($header))) as $pair) {
127
+ if (false === strpos($pair, '=')) {
128
+ $name = $pair;
129
+ $value = null;
130
+ } else {
131
+ list($name, $value) = explode('=', $pair);
132
+ }
133
+
134
+ $this->setAttribute($name, $value);
135
+ }
136
+
137
+ if (!$this->getAttribute(static::ATTR_DOMAIN)) {
138
+ $this->setAttribute(static::ATTR_DOMAIN, $issuingDomain);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Formats a Cookie header for the current cookie.
144
+ *
145
+ * @return string An HTTP request Cookie header
146
+ */
147
+ public function toCookieHeader()
148
+ {
149
+ return 'Cookie: '.$this->getName().'='.$this->getValue();
150
+ }
151
+
152
+ public function setName($name)
153
+ {
154
+ $this->name = $name;
155
+ }
156
+
157
+ public function getName()
158
+ {
159
+ return $this->name;
160
+ }
161
+
162
+ public function setValue($value)
163
+ {
164
+ $this->value = $value;
165
+ }
166
+
167
+ public function getValue()
168
+ {
169
+ return $this->value;
170
+ }
171
+
172
+ public function setAttributes(array $attributes)
173
+ {
174
+ // attributes are case insensitive
175
+ $this->attributes = array_change_key_case($attributes);
176
+ }
177
+
178
+ public function setAttribute($name, $value)
179
+ {
180
+ $this->attributes[strtolower($name)] = $value;
181
+ }
182
+
183
+ public function getAttributes()
184
+ {
185
+ return $this->attributes;
186
+ }
187
+
188
+ public function getAttribute($name)
189
+ {
190
+ $name = strtolower($name);
191
+
192
+ if (isset($this->attributes[$name])) {
193
+ return $this->attributes[$name];
194
+ }
195
+ }
196
+
197
+ public function hasAttribute($name)
198
+ {
199
+ return array_key_exists($name, $this->attributes);
200
+ }
201
+
202
+ public function clearAttributes()
203
+ {
204
+ $this->setAttributes(array());
205
+ }
206
+
207
+ public function setCreatedAt($createdAt)
208
+ {
209
+ $this->createdAt = $createdAt;
210
+ }
211
+
212
+ public function getCreatedAt()
213
+ {
214
+ return $this->createdAt;
215
+ }
216
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Util/CookieJar.php ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Util;
4
+
5
+ use WebinterpretConnector\Buzz\Message\MessageInterface;
6
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
7
+
8
+ class CookieJar
9
+ {
10
+ protected $cookies = array();
11
+
12
+ public function setCookies($cookies)
13
+ {
14
+ $this->cookies = array();
15
+ foreach ($cookies as $cookie) {
16
+ $this->addCookie($cookie);
17
+ }
18
+ }
19
+
20
+ public function getCookies()
21
+ {
22
+ return $this->cookies;
23
+ }
24
+
25
+ /**
26
+ * Adds a cookie to the current cookie jar.
27
+ *
28
+ * @param Cookie $cookie A cookie object
29
+ */
30
+ public function addCookie(Cookie $cookie)
31
+ {
32
+ $this->cookies[] = $cookie;
33
+ }
34
+
35
+ /**
36
+ * Adds Cookie headers to the supplied request.
37
+ *
38
+ * @param RequestInterface $request A request object
39
+ */
40
+ public function addCookieHeaders(RequestInterface $request)
41
+ {
42
+ foreach ($this->cookies as $cookie) {
43
+ if ($cookie->matchesRequest($request)) {
44
+ $request->addHeader($cookie->toCookieHeader());
45
+ }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Processes Set-Cookie headers from a request/response pair.
51
+ *
52
+ * @param RequestInterface $request A request object
53
+ * @param MessageInterface $response A response object
54
+ */
55
+ public function processSetCookieHeaders(RequestInterface $request, MessageInterface $response)
56
+ {
57
+ foreach ($response->getHeader('Set-Cookie', false) as $header) {
58
+ $cookie = new Cookie();
59
+ $cookie->fromSetCookieHeader($header, parse_url($request->getHost(), PHP_URL_HOST));
60
+
61
+ $this->addCookie($cookie);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Removes expired cookies.
67
+ */
68
+ public function clearExpiredCookies()
69
+ {
70
+ foreach ($this->cookies as $i => $cookie) {
71
+ if ($cookie->isExpired()) {
72
+ unset($this->cookies[$i]);
73
+ }
74
+ }
75
+
76
+ // reset array keys
77
+ $this->cookies = array_values($this->cookies);
78
+ }
79
+ }
app/code/community/Webinterpret/Connector/Lib/Buzz/Util/Url.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WebinterpretConnector\Buzz\Util;
4
+
5
+ use WebinterpretConnector\Buzz\Message\RequestInterface;
6
+ use WebinterpretConnector\Buzz\Exception\InvalidArgumentException;
7
+
8
+ class Url
9
+ {
10
+ private static $defaultPorts = array(
11
+ 'http' => 80,
12
+ 'https' => 443,
13
+ );
14
+
15
+ private $url;
16
+ private $components;
17
+
18
+ /**
19
+ * Constructor.
20
+ *
21
+ * @param string $url The URL
22
+ *
23
+ * @throws InvalidArgumentException If the URL is invalid
24
+ */
25
+ public function __construct($url)
26
+ {
27
+ $components = parse_url($url);
28
+
29
+ if (false === $components) {
30
+ throw new InvalidArgumentException(sprintf('The URL "%s" is invalid.', $url));
31
+ }
32
+
33
+ // support scheme-less URLs
34
+ if (!isset($components['host']) && isset($components['path'])) {
35
+ $pos = strpos($components['path'], '/');
36
+ if (false === $pos) {
37
+ $components['host'] = $components['path'];
38
+ unset($components['path']);
39
+ } elseif (0 !== $pos) {
40
+ list($host, $path) = explode('/', $components['path'], 2);
41
+ $components['host'] = $host;
42
+ $components['path'] = '/'.$path;
43
+ }
44
+ }
45
+
46
+ // default port
47
+ if (isset($components['scheme']) && !isset($components['port']) && isset(self::$defaultPorts[$components['scheme']])) {
48
+ $components['port'] = self::$defaultPorts[$components['scheme']];
49
+ }
50
+
51
+ $this->url = $url;
52
+ $this->components = $components;
53
+ }
54
+
55
+ public function getScheme()
56
+ {
57
+ return $this->parseUrl('scheme');
58
+ }
59
+
60
+ public function getHostname()
61
+ {
62
+ return $this->parseUrl('host');
63
+ }
64
+
65
+ public function getPort()
66
+ {
67
+ return $this->parseUrl('port');
68
+ }
69
+
70
+ public function getUser()
71
+ {
72
+ return $this->parseUrl('user');
73
+ }
74
+
75
+ public function getPassword()
76
+ {
77
+ return $this->parseUrl('pass');
78
+ }
79
+
80
+ public function getPath()
81
+ {
82
+ return $this->parseUrl('path');
83
+ }
84
+
85
+ public function getQueryString()
86
+ {
87
+ return $this->parseUrl('query');
88
+ }
89
+
90
+ public function getFragment()
91
+ {
92
+ return $this->parseUrl('fragment');
93
+ }
94
+
95
+ /**
96
+ * Returns a host string that combines scheme, hostname and port.
97
+ *
98
+ * @return string A host value for an HTTP message
99
+ */
100
+ public function getHost()
101
+ {
102
+ if ($hostname = $this->parseUrl('host')) {
103
+ $host = $scheme = $this->parseUrl('scheme', 'http');
104
+ $host .= '://';
105
+ $host .= $hostname;
106
+
107
+ $port = $this->parseUrl('port');
108
+ if ($port && (!isset(self::$defaultPorts[$scheme]) || self::$defaultPorts[$scheme] != $port)) {
109
+ $host .= ':'.$port;
110
+ }
111
+
112
+ return $host;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Returns a resource string that combines path and query string.
118
+ *
119
+ * @return string A resource value for an HTTP message
120
+ */
121
+ public function getResource()
122
+ {
123
+ $resource = $this->parseUrl('path', '/');
124
+
125
+ if ($query = $this->parseUrl('query')) {
126
+ $resource .= '?'.$query;
127
+ }
128
+
129
+ return $resource;
130
+ }
131
+
132
+ /**
133
+ * Returns a formatted URL.
134
+ */
135
+ public function format($pattern)
136
+ {
137
+ static $map = array(
138
+ 's' => 'getScheme',
139
+ 'u' => 'getUser',
140
+ 'a' => 'getPassword',
141
+ 'h' => 'getHostname',
142
+ 'o' => 'getPort',
143
+ 'p' => 'getPath',
144
+ 'q' => 'getQueryString',
145
+ 'f' => 'getFragment',
146
+ 'H' => 'getHost',
147
+ 'R' => 'getResource',
148
+ );
149
+
150
+ $url = '';
151
+
152
+ $parts = str_split($pattern);
153
+ while ($part = current($parts)) {
154
+ if (isset($map[$part])) {
155
+ $method = $map[$part];
156
+ $url .= $this->$method();
157
+ } elseif ('\\' == $part) {
158
+ $url .= next($parts);
159
+ } elseif (!ctype_alpha($part)) {
160
+ $url .= $part;
161
+ } else {
162
+ throw new InvalidArgumentException(sprintf('The format character "%s" is invalid.', $part));
163
+ }
164
+
165
+ next($parts);
166
+ }
167
+
168
+ return $url;
169
+ }
170
+
171
+ /**
172
+ * Applies the current URL to the supplied request.
173
+ */
174
+ public function applyToRequest(RequestInterface $request)
175
+ {
176
+ $request->setResource($this->getResource());
177
+ $request->setHost($this->getHost());
178
+ }
179
+
180
+ private function parseUrl($component = null, $default = null)
181
+ {
182
+ if (null === $component) {
183
+ return $this->components;
184
+ } elseif (isset($this->components[$component])) {
185
+ return $this->components[$component];
186
+ } else {
187
+ return $default;
188
+ }
189
+ }
190
+ }
app/code/community/Webinterpret/Connector/Model/StoreExtender.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use WebinterpretConnector\Buzz\Browser;
4
+ use WebinterpretConnector\Buzz\Client\Curl;
5
+ use WebinterpretConnector\Buzz\Client\FileGetContents;
6
+ use WebinterpretConnector\Buzz\Exception\ClientException;
7
+ use WebinterpretConnector\Buzz\Message\Response;
8
+
9
+ /**
10
+ * StoreExtender
11
+ *
12
+ * @category Webinterpret
13
+ * @package Webinterpret_Connector
14
+ * @author Webinterpret Team <info@webinterpret.com>
15
+ * @license http://opensource.org/licenses/osl-3.0.php
16
+ */
17
+ class Webinterpret_Connector_Model_StoreExtender extends Varien_object
18
+ {
19
+ /**
20
+ * @var string Prefix for cookies that should be forwarded to store extender proxy
21
+ */
22
+ private $cookiePrefix = 'wi_ws_store_ext_';
23
+
24
+ public function __construct()
25
+ {
26
+ spl_autoload_register(array($this, 'loadStoreExtenderDependencies'), true, true);
27
+ }
28
+
29
+ public static function loadStoreExtenderDependencies($class)
30
+ {
31
+ if (preg_match( '/^WebinterpretConnector\\\\/', $class)) {
32
+ $path = Mage::getModuleDir('', 'Webinterpret_Connector');
33
+ $libClassName = str_replace('\\', '/', substr($class, strlen('WebinterpretConnector\\')));
34
+ require_once($path . '/Lib/' . $libClassName . '.php');
35
+ }
36
+ }
37
+
38
+ public function parseRequest()
39
+ {
40
+ $storeExtenderUrl = Mage::getStoreConfig('webinterpret_connector/store_extender_url');
41
+ $storeExtenderUrl .= '?originalUrl=' . urlencode(Mage::helper('core/url')->getCurrentUrl());
42
+
43
+ $browser = new Browser($this->getBrowserClient());
44
+ $browser->getClient()->setMaxRedirects(0);
45
+
46
+ $method = $_SERVER['REQUEST_METHOD'];
47
+
48
+ try {
49
+ $response = $browser->call(
50
+ $storeExtenderUrl,
51
+ $_SERVER['REQUEST_METHOD'],
52
+ $this->getRequestHeaders(),
53
+ $this->getRequestContent($method)
54
+ );
55
+ } catch (ClientException $e) {
56
+ // fallback - redirect to base store URL if there is problem with proxy
57
+ header('Location: ' . Mage::getBaseUrl());
58
+ die();
59
+ }
60
+
61
+ $this->outputResponseCode($response->getStatusCode());
62
+ $this->outputHeaders($this->parseResponseHeadersToArray($response));
63
+ echo $response->getContent();
64
+ die;
65
+ }
66
+
67
+ /**
68
+ * @param $method
69
+ * @return string
70
+ */
71
+ private function getRequestContent($method)
72
+ {
73
+ $content = '';
74
+
75
+ if (in_array($method, array('PUT', 'PATCH', 'DELETE'))) {
76
+ $content = file_get_contents("php://input");
77
+ } elseif ($method === 'POST') {
78
+ $content = http_build_query($_POST);
79
+ }
80
+ return $content;
81
+ }
82
+
83
+ /**
84
+ * @return array
85
+ */
86
+ private function getRequestHeaders()
87
+ {
88
+ $headers = array();
89
+
90
+ $supportedHeaders = array(
91
+ 'HTTP_X_REQUESTED_WITH' => 'X-Requested-With',
92
+ 'HTTP_CONTENT_TYPE' => 'Content-Type',
93
+ 'HTTP_CONNECTION' => 'Connection',
94
+ 'HTTP_CACHE_CONTROL' => 'Cache-Control',
95
+ 'HTTP_USER_AGENT' => 'User-Agent',
96
+ 'HTTP_ACCEPT' => 'Accept',
97
+ 'HTTP_ACCEPT_LANGUAGE' => 'Accept-Language',
98
+ );
99
+
100
+ foreach ($supportedHeaders as $supportedHeader => $supportedHeaderKey) {
101
+ if (isset($_SERVER[$supportedHeader])) {
102
+ $headers[$supportedHeaderKey] = $_SERVER[$supportedHeader];
103
+ }
104
+ }
105
+
106
+ $cookies = $this->parseRequestCookies();
107
+ if (!empty($cookies)) {
108
+ $headers['Cookie'] = $cookies;
109
+ }
110
+
111
+ return $headers;
112
+ }
113
+
114
+ private function parseRequestCookies()
115
+ {
116
+ $cookies = array();
117
+ if (isset($_SERVER['HTTP_COOKIE'])) {
118
+ $cookies = array_map('trim', explode(';', $_SERVER['HTTP_COOKIE']));
119
+ $cookiePrefix = $this->cookiePrefix;
120
+ $cookies = array_filter($cookies, function($cookie) use ($cookiePrefix) {
121
+ return (strpos($cookie, $cookiePrefix) === 0);
122
+ });
123
+ }
124
+
125
+ return implode('; ', $cookies);
126
+ }
127
+
128
+ /**
129
+ * @param \WebinterpretConnector\Buzz\Message\Response $response
130
+ * @return array
131
+ */
132
+ private function parseResponseHeadersToArray(Response $response)
133
+ {
134
+ $supportedHeaders = array(
135
+ 'Date',
136
+ 'Content-Type',
137
+ 'Content-Length',
138
+ 'Connection',
139
+ 'Keep-Alive',
140
+ 'Location',
141
+ 'Set-Cookie',
142
+ );
143
+
144
+ //reformat response headers
145
+ $responseHeaders = array();
146
+
147
+ foreach ($response->getHeaders() as $header) {
148
+ list($headerName, $headerValue) = explode(':', $header, 2);
149
+ if ($headerValue) {
150
+ $responseHeaders[$headerName] = trim($headerValue);
151
+ }
152
+ }
153
+
154
+ foreach ($responseHeaders as $header => $contents) {
155
+ if (in_array($header, $supportedHeaders)) {
156
+ $responseHeaders[] = $header . ': ' . $contents;
157
+ }
158
+ unset($responseHeaders[$header]);
159
+ }
160
+
161
+ return $responseHeaders;
162
+ }
163
+
164
+ /**
165
+ * @param array $responseHeaders
166
+ */
167
+ private function outputHeaders($responseHeaders)
168
+ {
169
+ // remove previously set headers
170
+ header_remove();
171
+
172
+ foreach ($responseHeaders as $responseHeader) {
173
+ header($responseHeader);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @param $code
179
+ */
180
+ private function outputResponseCode($code)
181
+ {
182
+ if (function_exists( 'http_response_code')) {
183
+ http_response_code($code);
184
+ } else {
185
+ // X-PHP-Response-Code is a fake header name, we rely on the webserver to handle the 3rd parameter accordingly
186
+ header('X-PHP-Response-Code: ' . $code, true, $code);
187
+ }
188
+ }
189
+
190
+ private function getBrowserClient()
191
+ {
192
+ if (function_exists('curl_version')) {
193
+ return new Curl();
194
+ } else {
195
+ return new FileGetContents();
196
+ }
197
+ }
198
+ }
app/code/community/Webinterpret/Connector/controllers/IndexController.php CHANGED
@@ -11,6 +11,18 @@ class Webinterpret_Connector_IndexController extends Mage_Core_Controller_Front_
11
  {
12
  public function indexAction()
13
  {
 
 
 
 
 
 
 
 
 
 
 
 
14
  try {
15
  $dir = Mage::helper('webinterpret_connector')->getModuleBridgeDir();
16
  $path = $dir . DS . 'bridge.php';
11
  {
12
  public function indexAction()
13
  {
14
+ // Handle glopal request
15
+ $module = Mage::app()->getRequest()->getModuleName();
16
+ if ($module === 'glopal') {
17
+ if (!Mage::helper('webinterpret_connector')->isStoreExtenderEnabled()) {
18
+ $this->_forward('defaultNoRoute');
19
+ return;
20
+ }
21
+ $storeExtender = Mage::getModel('webinterpret_connector/storeExtender');
22
+ $storeExtender->parseRequest();
23
+ die();
24
+ }
25
+
26
  try {
27
  $dir = Mage::helper('webinterpret_connector')->getModuleBridgeDir();
28
  $path = $dir . DS . 'bridge.php';
app/code/community/Webinterpret/Connector/etc/config.xml CHANGED
@@ -8,7 +8,7 @@
8
  <config>
9
  <modules>
10
  <Webinterpret_Connector>
11
- <version>1.2.7.7</version>
12
  </Webinterpret_Connector>
13
  </modules>
14
  <global>
@@ -58,9 +58,17 @@
58
  </events>
59
  <request>
60
  <direct_front_name>
61
- <webinterpret_connector/>
 
62
  </direct_front_name>
63
  </request>
 
 
 
 
 
 
 
64
  </global>
65
  <frontend>
66
  <routers>
@@ -71,6 +79,13 @@
71
  <frontName>webinterpret_connector</frontName>
72
  </args>
73
  </webinterpret_connector>
 
 
 
 
 
 
 
74
  </routers>
75
  <layout>
76
  <updates>
@@ -102,6 +117,22 @@
102
  <installation_mode><![CDATA[1]]></installation_mode>
103
  <global_notifications_enabled><![CDATA[1]]></global_notifications_enabled>
104
  <automatic_updates_enabled><![CDATA[1]]></automatic_updates_enabled>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  </webinterpret_connector>
106
  </default>
107
  </config>
8
  <config>
9
  <modules>
10
  <Webinterpret_Connector>
11
+ <version>1.3.0.0</version>
12
  </Webinterpret_Connector>
13
  </modules>
14
  <global>
58
  </events>
59
  <request>
60
  <direct_front_name>
61
+ <webinterpret_connector />
62
+ <glopal />
63
  </direct_front_name>
64
  </request>
65
+ <rewrite>
66
+ <glopal_url>
67
+ <from><![CDATA[/glopal\/(.+)/]]></from>
68
+ <to><![CDATA[glopal/index/index/$1]]></to>
69
+ <complete>1</complete>
70
+ </glopal_url>
71
+ </rewrite>
72
  </global>
73
  <frontend>
74
  <routers>
79
  <frontName>webinterpret_connector</frontName>
80
  </args>
81
  </webinterpret_connector>
82
+ <glopal>
83
+ <use>standard</use>
84
+ <args>
85
+ <module>Webinterpret_Connector</module>
86
+ <frontName>glopal</frontName>
87
+ </args>
88
+ </glopal>
89
  </routers>
90
  <layout>
91
  <updates>
117
  <installation_mode><![CDATA[1]]></installation_mode>
118
  <global_notifications_enabled><![CDATA[1]]></global_notifications_enabled>
119
  <automatic_updates_enabled><![CDATA[1]]></automatic_updates_enabled>
120
+ <store_extender_enabled><![CDATA[0]]></store_extender_enabled>
121
+ <store_extender_url><![CDATA[https://wi-ws-store-ext.glopal.com/index.php]]></store_extender_url>
122
+ <public_key>-----BEGIN PUBLIC KEY-----
123
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6YW1LitbxWceRjvGZnwd
124
+ h7/NH0Gbmnt+jEpC99KJuu4wPV+AyByUV3KxOwo6fUWozsts2tSgg3hgxnTEwjwF
125
+ XqFdft5pE794YLRtYnBMaMYkcAyl9nZTYimKT+fz+MfKceMIkFmvOVh8IMPjHLV+
126
+ 7GOjBnrUxUl2/z/ohE9wUyxhP7Zo4HpHNDyDsHCBpANWVoRs9n5sBBb4cnBVIXBl
127
+ AJuWmf5c0NkMim6Sj5mM3fKb0vDhykU9IXIbQMn5cw5+g4Qy6ldcrXNm+M3+4JzW
128
+ XGqwtKrXbKLJTlA+H57szT4wYOBT1HGeDD9vFDOwTq2AjNN3mcOJawMbkiZ3sJG3
129
+ /rHMptZtE6ToMbdC/PW2neLcalpgclxafoSoD9byIYA3qWYN3GAEDGJa4UHIcZpv
130
+ nUxdL5qUgJAz2q/rk7blCSIRwLv85jQtArlfsdoxp3F3iaahjz9sB/VX1xZ6YOJH
131
+ uZGGd1aUimDOAgVfmqeFOvTEBQFYTpGg9W7TyMIqvfAqPUtOxDmzB6fNxPd45W3s
132
+ azayJumvfHBOs53t6fI/a52CfQ2I4jaz3eHI0hpHyZVxvsAj5NbFXBYzQ+mOoTEa
133
+ 38d6cBHbci6rLRJ0PdaP6ZABz9OI3UE8VCz5n5BZEJ0QWmXmnn2YnWq8p3Si53KK
134
+ tQMgSP58oPZ6Nq6qM6KrlmkCAwEAAQ==
135
+ -----END PUBLIC KEY-----</public_key>
136
  </webinterpret_connector>
137
  </default>
138
  </config>
app/design/frontend/base/default/template/webinterpret/connector/product_view.phtml CHANGED
@@ -17,6 +17,7 @@ _webinterpret.plugin_version = '<?php echo $this->getPluginVersion() ?>';
17
  _webinterpret.store_locale = '<?php echo $this->getLocaleCode() ?>';
18
  _webinterpret.store_url = '<?php echo $this->getStoreUrl() ?>';
19
  _webinterpret.product_id = '<?php echo $this->getProductId() ?>';
 
20
  //]]>
21
  </script>
22
  <?php endif; ?>
17
  _webinterpret.store_locale = '<?php echo $this->getLocaleCode() ?>';
18
  _webinterpret.store_url = '<?php echo $this->getStoreUrl() ?>';
19
  _webinterpret.product_id = '<?php echo $this->getProductId() ?>';
20
+ _webinterpret.store_ext_available = '<?php echo Mage::helper('webinterpret_connector')->isStoreExtenderEnabled() ? 'yes' : 'no' ?>';
21
  //]]>
22
  </script>
23
  <?php endif; ?>
package.xml CHANGED
@@ -1,21 +1,18 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Webinterpret_Connector</name>
4
- <version>1.2.7.7</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Integrate Magento with Webinterpret.</summary>
10
  <description>Translate and market your products internationally with Webinterpret. This is the official Magento extension for integrating with Webinterpret.</description>
11
- <notes>- Redirector optimization&#xD;
12
- - Simplified installation process&#xD;
13
- &#xD;
14
- </notes>
15
  <authors><author><name>Webinterpret</name><user>webinterpret</user><email>info@webinterpret.com</email></author></authors>
16
- <date>2016-05-17</date>
17
- <time>13:18:28</time>
18
- <contents><target name="magecommunity"><dir name="Webinterpret"><dir name="Connector"><dir name="Block"><dir name="Adminhtml"><file name="Notifications.php" hash="5e1935e32f1b5d10f0b76fee427a23a6"/><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Status.php" hash="99b95c7991e91a4d4d98efdaf8007b88"/></dir></dir></dir></dir><file name="Footer.php" hash="160f03e528d7e9411e85d524919fd280"/><file name="Head.php" hash="1261ad40df9f0dc708178d78d63158d8"/><dir name="Product"><file name="View.php" hash="3b3b40108b53d326c0dcff0e45fa8407"/></dir></dir><dir name="Helper"><file name="Data.php" hash="7db8a4f7e834b17eb16a21cf6fe66316"/></dir><dir name="Model"><file name="Notification.php" hash="a62c3b7ff11cd2d1082c0d959d8eb295"/><file name="Observer.php" hash="b3cb7f22f5bc376b6f834a211a7c205b"/></dir><dir name="bridge2cart"><file name="bridge.php" hash="cdd197a1af1a05082a8836ea24922dad"/><file name="config.php" hash="0fb8e12c8d9c293354eb4fb96a713608"/><file name="helper.php" hash="afb5e2141259c580285ac85d0c180144"/><file name="preloader.php" hash="8a8ada3537394687defdc28d4b6077e8"/></dir><dir name="controllers"><file name="HelperController.php" hash="73fb01f035db7d75eb1ee0a3d535fd0b"/><file name="IndexController.php" hash="9354193ee33ea22ebfdd0b3766a9524e"/></dir><dir name="etc"><file name="adminhtml.xml" hash="07e287503c40ce7c588efe7863c05002"/><file name="config.xml" hash="06556566e4a0099e2f9036688b488fd5"/><file name="system.xml" hash="cfe59ca34ebfef69b900f4ad275a809a"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Webinterpret_Connector.xml" hash="087c2742f6bcb89ed6a77921e6493feb"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="webinterpret"><file name="connector.xml" hash="1226a298ca08a92008293ba4e8c09eab"/></dir></dir><dir name="template"><dir name="webinterpret"><dir name="connector"><file name="footer.phtml" hash="bd48d0e36dea0936b9f28fbdada3b8c1"/><file name="head.phtml" hash="107312871a7b2730532db8d986de5726"/><file name="product_view.phtml" hash="baf77c24eb31579162db50b7c4f2b456"/></dir></dir></dir></dir></dir></dir><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="webinterpret"><dir name="system"><dir name="config"><file name="activate.phtml" hash="5ef389ad58cb1be9cc666fecd8379fbf"/><dir name="fieldset"><file name="banner.phtml" hash="921b677bd3e52d9c86172bdf26ab4a37"/><file name="status.phtml" hash="412bd1ea314a7f0c8897e6a9225c3551"/></dir></dir></dir></dir></dir><dir name="layout"><dir name="webinterpret"><file name="connector.xml" hash="867fafe49fa978668f6e5d7027c57aa6"/></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><file name="Webinterpret_Connector.csv" hash="c382db753974630198cf029cf90b5d27"/></dir><dir name="de_DE"><file name="Webinterpret_Connector.csv" hash="91312fd3bd2645b88e3b3643b6b614a9"/></dir></target></contents>
19
  <compatible/>
20
  <dependencies><required><php><min>5.3.0</min><max>7.0.0</max></php></required></dependencies>
21
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Webinterpret_Connector</name>
4
+ <version>1.3.0.0</version>
5
  <stability>stable</stability>
6
  <license uri="http://www.opensource.org/licenses/osl-3.0.php">OSL v3.0</license>
7
  <channel>community</channel>
8
  <extends/>
9
  <summary>Integrate Magento with Webinterpret.</summary>
10
  <description>Translate and market your products internationally with Webinterpret. This is the official Magento extension for integrating with Webinterpret.</description>
11
+ <notes>- Translation service improvement - display international pages under the domestic store domain</notes>
 
 
 
12
  <authors><author><name>Webinterpret</name><user>webinterpret</user><email>info@webinterpret.com</email></author></authors>
13
+ <date>2016-08-18</date>
14
+ <time>08:54:47</time>
15
+ <contents><target name="magecommunity"><dir><dir name="Webinterpret"><dir name="Connector"><dir><dir name="Block"><dir name="Adminhtml"><file name="Notifications.php" hash="5e1935e32f1b5d10f0b76fee427a23a6"/><dir name="System"><dir name="Config"><dir name="Fieldset"><file name="Status.php" hash="99b95c7991e91a4d4d98efdaf8007b88"/></dir></dir></dir></dir><file name="Footer.php" hash="160f03e528d7e9411e85d524919fd280"/><file name="Head.php" hash="1261ad40df9f0dc708178d78d63158d8"/><dir name="Product"><file name="View.php" hash="3b3b40108b53d326c0dcff0e45fa8407"/></dir></dir><dir name="Helper"><file name="Data.php" hash="bdce8c1bef0471de67c3893b24abe69d"/></dir><dir name="Lib"><dir name="Buzz"><file name="Browser.php" hash="faa5485f0fa3ca4958890052fe90ac92"/><dir name="Client"><file name="AbstractClient.php" hash="e35ab76a0453cd49d25eb677fdfb1cfb"/><file name="AbstractCurl.php" hash="39b85ceac150c234ab7ac9855f5b4afd"/><file name="AbstractStream.php" hash="651db2b755b301ad8b5f8dba6172b800"/><file name="BatchClientInterface.php" hash="3f8ddac0208efa3c8abe2b1025cb2b9c"/><file name="ClientInterface.php" hash="42583838ba59397e693e4d34db056ade"/><file name="Curl.php" hash="7002d06681911925bc67196a64be5e48"/><file name="FileGetContents.php" hash="e9a64db88683338e0a720f0046e413c2"/><file name="MultiCurl.php" hash="5c956bda37ffa542c93cb0582cd51e52"/></dir><dir name="Exception"><file name="ClientException.php" hash="b03c3fc142011180b155916db3016d19"/><file name="ExceptionInterface.php" hash="6dd392e9121db19fc6ee2faf50163175"/><file name="InvalidArgumentException.php" hash="715735e6313fe5d5cc846277ff2635cb"/><file name="LogicException.php" hash="0fb322aa30d4fcf0691471866b1378fb"/><file name="RequestException.php" hash="5ba0bb7e029fd5405f5cdd8b7e247a86"/><file name="RuntimeException.php" hash="8780764e1f114d7ae3f106a656c12a4f"/></dir><dir name="Listener"><file name="BasicAuthListener.php" hash="d3b4bf91414e52137b1bc66a97914da3"/><file name="CallbackListener.php" hash="2b3e473fb154fb424718dcf318c92553"/><file name="CookieListener.php" hash="0dc12bbbafa865701836cb5f1772581e"/><file name="DigestAuthListener.php" hash="991db4773e9d40253e61b2bc9e44ca68"/><dir name="History"><file name="Entry.php" hash="73650b956104e7db6889a373ed3c2522"/><file name="Journal.php" hash="6898357c87dd0d1490b3c004a72fa7a1"/></dir><file name="HistoryListener.php" hash="39f3fd76c57ab4aa7ffebaf49347a078"/><file name="ListenerChain.php" hash="efaa53a20164fae43ea57797b3da3e1f"/><file name="ListenerInterface.php" hash="778bdbf3aa80c34cfab26e5ce3663e96"/><file name="LoggerListener.php" hash="7709c2dfe5c7273dc7e6df8da2b7276e"/></dir><dir name="Message"><file name="AbstractMessage.php" hash="b8fbd6ecb218f09736a21b2396fc93c7"/><dir name="Factory"><file name="Factory.php" hash="19505683584eb8d9aab014288f21ace9"/><file name="FactoryInterface.php" hash="0f5d14dbc1a7b7e99826e33a6bd9b1c7"/></dir><dir name="Form"><file name="FormRequest.php" hash="335bcc0fb762fa3934883165c5f103f9"/><file name="FormRequestInterface.php" hash="cdce91424c9287a154cd53637c2142c4"/><file name="FormUpload.php" hash="3ffdbacf5d22643cab4afca459444a93"/><file name="FormUploadInterface.php" hash="7e0ca9ae5cc0c3b0bfef3717033d36bc"/></dir><file name="MessageInterface.php" hash="e755508e39647ebc9278d9d1b77c9f5a"/><file name="Request.php" hash="2d805ac034a28d4bd04c1275cef06234"/><file name="RequestInterface.php" hash="d5259b58d09230aa97227e798c5334ee"/><file name="Response.php" hash="a6b32fbae2a6b380d5f7bc68cbffea61"/></dir><dir name="Util"><file name="Cookie.php" hash="b08275dc28e7ce1923dc1f16a0500b77"/><file name="CookieJar.php" hash="c6eb7bb4958cac7a8b334e0843cb7b4a"/><file name="Url.php" hash="1825c6e2ff3e1e230c6dde833dbf655d"/></dir></dir></dir><dir name="Model"><file name="Notification.php" hash="a62c3b7ff11cd2d1082c0d959d8eb295"/><file name="Observer.php" hash="b3cb7f22f5bc376b6f834a211a7c205b"/><file name="StoreExtender.php" hash="c5fd0294f6b36c05d1293ca899deb152"/></dir><dir name="bridge2cart"><file name="bridge.php" hash="cdd197a1af1a05082a8836ea24922dad"/><file name="config.php" hash="0fb8e12c8d9c293354eb4fb96a713608"/><file name="helper.php" hash="afb5e2141259c580285ac85d0c180144"/><file name="preloader.php" hash="8a8ada3537394687defdc28d4b6077e8"/></dir><dir name="controllers"><file name="HelperController.php" hash="73fb01f035db7d75eb1ee0a3d535fd0b"/><file name="IndexController.php" hash="a76fb36d3f6fa4c370b766ed9ba818ed"/></dir><dir name="etc"><file name="adminhtml.xml" hash="07e287503c40ce7c588efe7863c05002"/><file name="config.xml" hash="f9eeac722631b966f1231b8ee8cd9712"/><file name="system.xml" hash="cfe59ca34ebfef69b900f4ad275a809a"/></dir></dir></dir></dir></dir></target><target name="magedesign"><dir><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="webinterpret"><file name="connector.xml" hash="867fafe49fa978668f6e5d7027c57aa6"/></dir></dir><dir name="template"><dir name="webinterpret"><dir name="system"><dir name="config"><file name="activate.phtml" hash="5ef389ad58cb1be9cc666fecd8379fbf"/><dir name="fieldset"><file name="banner.phtml" hash="921b677bd3e52d9c86172bdf26ab4a37"/><file name="status.phtml" hash="412bd1ea314a7f0c8897e6a9225c3551"/></dir></dir></dir></dir></dir></dir></dir></dir><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><dir name="webinterpret"><file name="connector.xml" hash="1226a298ca08a92008293ba4e8c09eab"/></dir></dir><dir name="template"><dir name="webinterpret"><dir name="connector"><file name="footer.phtml" hash="bd48d0e36dea0936b9f28fbdada3b8c1"/><file name="head.phtml" hash="107312871a7b2730532db8d986de5726"/><file name="product_view.phtml" hash="2036bc444284b99f4cc8228fa4751777"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Webinterpret_Connector.xml" hash="087c2742f6bcb89ed6a77921e6493feb"/></dir></target><target name="magelocale"><dir><dir name="de_DE"><file name="Webinterpret_Connector.csv" hash="91312fd3bd2645b88e3b3643b6b614a9"/></dir><dir name="en_US"><file name="Webinterpret_Connector.csv" hash="c382db753974630198cf029cf90b5d27"/></dir></dir></target></contents>
16
  <compatible/>
17
  <dependencies><required><php><min>5.3.0</min><max>7.0.0</max></php></required></dependencies>
18
  </package>