|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Feed_Pubsubhubbub |
|
17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
18 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
19 * @version $Id: Subscriber.php 23170 2010-10-19 18:29:24Z mabe $ |
|
20 */ |
|
21 |
|
22 /** |
|
23 * @see Zend_Feed_Pubsubhubbub |
|
24 */ |
|
25 require_once 'Zend/Feed/Pubsubhubbub.php'; |
|
26 |
|
27 /** |
|
28 * @see Zend_Date |
|
29 */ |
|
30 require_once 'Zend/Date.php'; |
|
31 |
|
32 /** |
|
33 * @category Zend |
|
34 * @package Zend_Feed_Pubsubhubbub |
|
35 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
36 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
37 */ |
|
38 class Zend_Feed_Pubsubhubbub_Subscriber |
|
39 { |
|
40 /** |
|
41 * An array of URLs for all Hub Servers to subscribe/unsubscribe. |
|
42 * |
|
43 * @var array |
|
44 */ |
|
45 protected $_hubUrls = array(); |
|
46 |
|
47 /** |
|
48 * An array of optional parameters to be included in any |
|
49 * (un)subscribe requests. |
|
50 * |
|
51 * @var array |
|
52 */ |
|
53 protected $_parameters = array(); |
|
54 |
|
55 /** |
|
56 * The URL of the topic (Rss or Atom feed) which is the subject of |
|
57 * our current intent to subscribe to/unsubscribe from updates from |
|
58 * the currently configured Hub Servers. |
|
59 * |
|
60 * @var string |
|
61 */ |
|
62 protected $_topicUrl = ''; |
|
63 |
|
64 /** |
|
65 * The URL Hub Servers must use when communicating with this Subscriber |
|
66 * |
|
67 * @var string |
|
68 */ |
|
69 protected $_callbackUrl = ''; |
|
70 |
|
71 /** |
|
72 * The number of seconds for which the subscriber would like to have the |
|
73 * subscription active. Defaults to null, i.e. not sent, to setup a |
|
74 * permanent subscription if possible. |
|
75 * |
|
76 * @var int |
|
77 */ |
|
78 protected $_leaseSeconds = null; |
|
79 |
|
80 /** |
|
81 * The preferred verification mode (sync or async). By default, this |
|
82 * Subscriber prefers synchronous verification, but is considered |
|
83 * desireable to support asynchronous verification if possible. |
|
84 * |
|
85 * Zend_Feed_Pubsubhubbub_Subscriber will always send both modes, whose |
|
86 * order of occurance in the parameter list determines this preference. |
|
87 * |
|
88 * @var string |
|
89 */ |
|
90 protected $_preferredVerificationMode |
|
91 = Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC; |
|
92 |
|
93 /** |
|
94 * An array of any errors including keys for 'response', 'hubUrl'. |
|
95 * The response is the actual Zend_Http_Response object. |
|
96 * |
|
97 * @var array |
|
98 */ |
|
99 protected $_errors = array(); |
|
100 |
|
101 /** |
|
102 * An array of Hub Server URLs for Hubs operating at this time in |
|
103 * asynchronous verification mode. |
|
104 * |
|
105 * @var array |
|
106 */ |
|
107 protected $_asyncHubs = array(); |
|
108 |
|
109 /** |
|
110 * An instance of Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface used to background |
|
111 * save any verification tokens associated with a subscription or other. |
|
112 * |
|
113 * @var Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface |
|
114 */ |
|
115 protected $_storage = null; |
|
116 |
|
117 /** |
|
118 * An array of authentication credentials for HTTP Basic Authentication |
|
119 * if required by specific Hubs. The array is indexed by Hub Endpoint URI |
|
120 * and the value is a simple array of the username and password to apply. |
|
121 * |
|
122 * @var array |
|
123 */ |
|
124 protected $_authentications = array(); |
|
125 |
|
126 /** |
|
127 * Tells the Subscriber to append any subscription identifier to the path |
|
128 * of the base Callback URL. E.g. an identifier "subkey1" would be added |
|
129 * to the callback URL "http://www.example.com/callback" to create a subscription |
|
130 * specific Callback URL of "http://www.example.com/callback/subkey1". |
|
131 * |
|
132 * This is required for all Hubs using the Pubsubhubbub 0.1 Specification. |
|
133 * It should be manually intercepted and passed to the Callback class using |
|
134 * Zend_Feed_Pubsubhubbub_Subscriber_Callback::setSubscriptionKey(). Will |
|
135 * require a route in the form "callback/:subkey" to allow the parameter be |
|
136 * retrieved from an action using the Zend_Controller_Action::_getParam() |
|
137 * method. |
|
138 * |
|
139 * @var string |
|
140 */ |
|
141 protected $_usePathParameter = false; |
|
142 |
|
143 /** |
|
144 * Constructor; accepts an array or Zend_Config instance to preset |
|
145 * options for the Subscriber without calling all supported setter |
|
146 * methods in turn. |
|
147 * |
|
148 * @param array|Zend_Config $options Options array or Zend_Config instance |
|
149 * @return void |
|
150 */ |
|
151 public function __construct($config = null) |
|
152 { |
|
153 if ($config !== null) { |
|
154 $this->setConfig($config); |
|
155 } |
|
156 } |
|
157 |
|
158 /** |
|
159 * Process any injected configuration options |
|
160 * |
|
161 * @param array|Zend_Config $options Options array or Zend_Config instance |
|
162 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
163 */ |
|
164 public function setConfig($config) |
|
165 { |
|
166 if ($config instanceof Zend_Config) { |
|
167 $config = $config->toArray(); |
|
168 } elseif (!is_array($config)) { |
|
169 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
170 throw new Zend_Feed_Pubsubhubbub_Exception('Array or Zend_Config object' |
|
171 . ' expected, got ' . gettype($config)); |
|
172 } |
|
173 if (array_key_exists('hubUrls', $config)) { |
|
174 $this->addHubUrls($config['hubUrls']); |
|
175 } |
|
176 if (array_key_exists('callbackUrl', $config)) { |
|
177 $this->setCallbackUrl($config['callbackUrl']); |
|
178 } |
|
179 if (array_key_exists('topicUrl', $config)) { |
|
180 $this->setTopicUrl($config['topicUrl']); |
|
181 } |
|
182 if (array_key_exists('storage', $config)) { |
|
183 $this->setStorage($config['storage']); |
|
184 } |
|
185 if (array_key_exists('leaseSeconds', $config)) { |
|
186 $this->setLeaseSeconds($config['leaseSeconds']); |
|
187 } |
|
188 if (array_key_exists('parameters', $config)) { |
|
189 $this->setParameters($config['parameters']); |
|
190 } |
|
191 if (array_key_exists('authentications', $config)) { |
|
192 $this->addAuthentications($config['authentications']); |
|
193 } |
|
194 if (array_key_exists('usePathParameter', $config)) { |
|
195 $this->usePathParameter($config['usePathParameter']); |
|
196 } |
|
197 if (array_key_exists('preferredVerificationMode', $config)) { |
|
198 $this->setPreferredVerificationMode( |
|
199 $config['preferredVerificationMode'] |
|
200 ); |
|
201 } |
|
202 return $this; |
|
203 } |
|
204 |
|
205 /** |
|
206 * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe |
|
207 * event will relate |
|
208 * |
|
209 * @param string $url |
|
210 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
211 */ |
|
212 public function setTopicUrl($url) |
|
213 { |
|
214 if (empty($url) || !is_string($url) || !Zend_Uri::check($url)) { |
|
215 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
216 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "url"' |
|
217 .' of "' . $url . '" must be a non-empty string and a valid' |
|
218 .' URL'); |
|
219 } |
|
220 $this->_topicUrl = $url; |
|
221 return $this; |
|
222 } |
|
223 |
|
224 /** |
|
225 * Set the topic URL (RSS or Atom feed) to which the intended (un)subscribe |
|
226 * event will relate |
|
227 * |
|
228 * @return string |
|
229 */ |
|
230 public function getTopicUrl() |
|
231 { |
|
232 if (empty($this->_topicUrl)) { |
|
233 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
234 throw new Zend_Feed_Pubsubhubbub_Exception('A valid Topic (RSS or Atom' |
|
235 . ' feed) URL MUST be set before attempting any operation'); |
|
236 } |
|
237 return $this->_topicUrl; |
|
238 } |
|
239 |
|
240 /** |
|
241 * Set the number of seconds for which any subscription will remain valid |
|
242 * |
|
243 * @param int $seconds |
|
244 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
245 */ |
|
246 public function setLeaseSeconds($seconds) |
|
247 { |
|
248 $seconds = intval($seconds); |
|
249 if ($seconds <= 0) { |
|
250 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
251 throw new Zend_Feed_Pubsubhubbub_Exception('Expected lease seconds' |
|
252 . ' must be an integer greater than zero'); |
|
253 } |
|
254 $this->_leaseSeconds = $seconds; |
|
255 return $this; |
|
256 } |
|
257 |
|
258 /** |
|
259 * Get the number of lease seconds on subscriptions |
|
260 * |
|
261 * @return int |
|
262 */ |
|
263 public function getLeaseSeconds() |
|
264 { |
|
265 return $this->_leaseSeconds; |
|
266 } |
|
267 |
|
268 /** |
|
269 * Set the callback URL to be used by Hub Servers when communicating with |
|
270 * this Subscriber |
|
271 * |
|
272 * @param string $url |
|
273 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
274 */ |
|
275 public function setCallbackUrl($url) |
|
276 { |
|
277 if (empty($url) || !is_string($url) || !Zend_Uri::check($url)) { |
|
278 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
279 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "url"' |
|
280 . ' of "' . $url . '" must be a non-empty string and a valid' |
|
281 . ' URL'); |
|
282 } |
|
283 $this->_callbackUrl = $url; |
|
284 return $this; |
|
285 } |
|
286 |
|
287 /** |
|
288 * Get the callback URL to be used by Hub Servers when communicating with |
|
289 * this Subscriber |
|
290 * |
|
291 * @return string |
|
292 */ |
|
293 public function getCallbackUrl() |
|
294 { |
|
295 if (empty($this->_callbackUrl)) { |
|
296 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
297 throw new Zend_Feed_Pubsubhubbub_Exception('A valid Callback URL MUST be' |
|
298 . ' set before attempting any operation'); |
|
299 } |
|
300 return $this->_callbackUrl; |
|
301 } |
|
302 |
|
303 /** |
|
304 * Set preferred verification mode (sync or async). By default, this |
|
305 * Subscriber prefers synchronous verification, but does support |
|
306 * asynchronous if that's the Hub Server's utilised mode. |
|
307 * |
|
308 * Zend_Feed_Pubsubhubbub_Subscriber will always send both modes, whose |
|
309 * order of occurance in the parameter list determines this preference. |
|
310 * |
|
311 * @param string $mode Should be 'sync' or 'async' |
|
312 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
313 */ |
|
314 public function setPreferredVerificationMode($mode) |
|
315 { |
|
316 if ($mode !== Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC |
|
317 && $mode !== Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC) { |
|
318 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
319 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid preferred' |
|
320 . ' mode specified: "' . $mode . '" but should be one of' |
|
321 . ' Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC or' |
|
322 . ' Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC'); |
|
323 } |
|
324 $this->_preferredVerificationMode = $mode; |
|
325 return $this; |
|
326 } |
|
327 |
|
328 /** |
|
329 * Get preferred verification mode (sync or async). |
|
330 * |
|
331 * @return string |
|
332 */ |
|
333 public function getPreferredVerificationMode() |
|
334 { |
|
335 return $this->_preferredVerificationMode; |
|
336 } |
|
337 |
|
338 /** |
|
339 * Add a Hub Server URL supported by Publisher |
|
340 * |
|
341 * @param string $url |
|
342 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
343 */ |
|
344 public function addHubUrl($url) |
|
345 { |
|
346 if (empty($url) || !is_string($url) || !Zend_Uri::check($url)) { |
|
347 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
348 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "url"' |
|
349 . ' of "' . $url . '" must be a non-empty string and a valid' |
|
350 . ' URL'); |
|
351 } |
|
352 $this->_hubUrls[] = $url; |
|
353 return $this; |
|
354 } |
|
355 |
|
356 /** |
|
357 * Add an array of Hub Server URLs supported by Publisher |
|
358 * |
|
359 * @param array $urls |
|
360 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
361 */ |
|
362 public function addHubUrls(array $urls) |
|
363 { |
|
364 foreach ($urls as $url) { |
|
365 $this->addHubUrl($url); |
|
366 } |
|
367 return $this; |
|
368 } |
|
369 |
|
370 /** |
|
371 * Remove a Hub Server URL |
|
372 * |
|
373 * @param string $url |
|
374 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
375 */ |
|
376 public function removeHubUrl($url) |
|
377 { |
|
378 if (!in_array($url, $this->getHubUrls())) { |
|
379 return $this; |
|
380 } |
|
381 $key = array_search($url, $this->_hubUrls); |
|
382 unset($this->_hubUrls[$key]); |
|
383 return $this; |
|
384 } |
|
385 |
|
386 /** |
|
387 * Return an array of unique Hub Server URLs currently available |
|
388 * |
|
389 * @return array |
|
390 */ |
|
391 public function getHubUrls() |
|
392 { |
|
393 $this->_hubUrls = array_unique($this->_hubUrls); |
|
394 return $this->_hubUrls; |
|
395 } |
|
396 |
|
397 /** |
|
398 * Add authentication credentials for a given URL |
|
399 * |
|
400 * @param string $url |
|
401 * @param array $authentication |
|
402 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
403 */ |
|
404 public function addAuthentication($url, array $authentication) |
|
405 { |
|
406 if (empty($url) || !is_string($url) || !Zend_Uri::check($url)) { |
|
407 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
408 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "url"' |
|
409 . ' of "' . $url . '" must be a non-empty string and a valid' |
|
410 . ' URL'); |
|
411 } |
|
412 $this->_authentications[$url] = $authentication; |
|
413 return $this; |
|
414 } |
|
415 |
|
416 /** |
|
417 * Add authentication credentials for hub URLs |
|
418 * |
|
419 * @param array $authentications |
|
420 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
421 */ |
|
422 public function addAuthentications(array $authentications) |
|
423 { |
|
424 foreach ($authentications as $url => $authentication) { |
|
425 $this->addAuthentication($url, $authentication); |
|
426 } |
|
427 return $this; |
|
428 } |
|
429 |
|
430 /** |
|
431 * Get all hub URL authentication credentials |
|
432 * |
|
433 * @return array |
|
434 */ |
|
435 public function getAuthentications() |
|
436 { |
|
437 return $this->_authentications; |
|
438 } |
|
439 |
|
440 /** |
|
441 * Set flag indicating whether or not to use a path parameter |
|
442 * |
|
443 * @param bool $bool |
|
444 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
445 */ |
|
446 public function usePathParameter($bool = true) |
|
447 { |
|
448 $this->_usePathParameter = $bool; |
|
449 return $this; |
|
450 } |
|
451 |
|
452 /** |
|
453 * Add an optional parameter to the (un)subscribe requests |
|
454 * |
|
455 * @param string $name |
|
456 * @param string|null $value |
|
457 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
458 */ |
|
459 public function setParameter($name, $value = null) |
|
460 { |
|
461 if (is_array($name)) { |
|
462 $this->setParameters($name); |
|
463 return $this; |
|
464 } |
|
465 if (empty($name) || !is_string($name)) { |
|
466 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
467 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "name"' |
|
468 . ' of "' . $name . '" must be a non-empty string'); |
|
469 } |
|
470 if ($value === null) { |
|
471 $this->removeParameter($name); |
|
472 return $this; |
|
473 } |
|
474 if (empty($value) || (!is_string($value) && $value !== null)) { |
|
475 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
476 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "value"' |
|
477 . ' of "' . $value . '" must be a non-empty string'); |
|
478 } |
|
479 $this->_parameters[$name] = $value; |
|
480 return $this; |
|
481 } |
|
482 |
|
483 /** |
|
484 * Add an optional parameter to the (un)subscribe requests |
|
485 * |
|
486 * @param string $name |
|
487 * @param string|null $value |
|
488 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
489 */ |
|
490 public function setParameters(array $parameters) |
|
491 { |
|
492 foreach ($parameters as $name => $value) { |
|
493 $this->setParameter($name, $value); |
|
494 } |
|
495 return $this; |
|
496 } |
|
497 |
|
498 /** |
|
499 * Remove an optional parameter for the (un)subscribe requests |
|
500 * |
|
501 * @param string $name |
|
502 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
503 */ |
|
504 public function removeParameter($name) |
|
505 { |
|
506 if (empty($name) || !is_string($name)) { |
|
507 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
508 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid parameter "name"' |
|
509 . ' of "' . $name . '" must be a non-empty string'); |
|
510 } |
|
511 if (array_key_exists($name, $this->_parameters)) { |
|
512 unset($this->_parameters[$name]); |
|
513 } |
|
514 return $this; |
|
515 } |
|
516 |
|
517 /** |
|
518 * Return an array of optional parameters for (un)subscribe requests |
|
519 * |
|
520 * @return array |
|
521 */ |
|
522 public function getParameters() |
|
523 { |
|
524 return $this->_parameters; |
|
525 } |
|
526 |
|
527 /** |
|
528 * Sets an instance of Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface used to background |
|
529 * save any verification tokens associated with a subscription or other. |
|
530 * |
|
531 * @param Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface $storage |
|
532 * @return Zend_Feed_Pubsubhubbub_Subscriber |
|
533 */ |
|
534 public function setStorage(Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface $storage) |
|
535 { |
|
536 $this->_storage = $storage; |
|
537 return $this; |
|
538 } |
|
539 |
|
540 /** |
|
541 * Gets an instance of Zend_Feed_Pubsubhubbub_Storage_StorageInterface used |
|
542 * to background save any verification tokens associated with a subscription |
|
543 * or other. |
|
544 * |
|
545 * @return Zend_Feed_Pubsubhubbub_Model_SubscriptionInterface |
|
546 */ |
|
547 public function getStorage() |
|
548 { |
|
549 if ($this->_storage === null) { |
|
550 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
551 throw new Zend_Feed_Pubsubhubbub_Exception('No storage vehicle ' |
|
552 . 'has been set.'); |
|
553 } |
|
554 return $this->_storage; |
|
555 } |
|
556 |
|
557 /** |
|
558 * Subscribe to one or more Hub Servers using the stored Hub URLs |
|
559 * for the given Topic URL (RSS or Atom feed) |
|
560 * |
|
561 * @return void |
|
562 */ |
|
563 public function subscribeAll() |
|
564 { |
|
565 return $this->_doRequest('subscribe'); |
|
566 } |
|
567 |
|
568 /** |
|
569 * Unsubscribe from one or more Hub Servers using the stored Hub URLs |
|
570 * for the given Topic URL (RSS or Atom feed) |
|
571 * |
|
572 * @return void |
|
573 */ |
|
574 public function unsubscribeAll() |
|
575 { |
|
576 return $this->_doRequest('unsubscribe'); |
|
577 } |
|
578 |
|
579 /** |
|
580 * Returns a boolean indicator of whether the notifications to Hub |
|
581 * Servers were ALL successful. If even one failed, FALSE is returned. |
|
582 * |
|
583 * @return bool |
|
584 */ |
|
585 public function isSuccess() |
|
586 { |
|
587 if (count($this->_errors) > 0) { |
|
588 return false; |
|
589 } |
|
590 return true; |
|
591 } |
|
592 |
|
593 /** |
|
594 * Return an array of errors met from any failures, including keys: |
|
595 * 'response' => the Zend_Http_Response object from the failure |
|
596 * 'hubUrl' => the URL of the Hub Server whose notification failed |
|
597 * |
|
598 * @return array |
|
599 */ |
|
600 public function getErrors() |
|
601 { |
|
602 return $this->_errors; |
|
603 } |
|
604 |
|
605 /** |
|
606 * Return an array of Hub Server URLs who returned a response indicating |
|
607 * operation in Asynchronous Verification Mode, i.e. they will not confirm |
|
608 * any (un)subscription immediately but at a later time (Hubs may be |
|
609 * doing this as a batch process when load balancing) |
|
610 * |
|
611 * @return array |
|
612 */ |
|
613 public function getAsyncHubs() |
|
614 { |
|
615 return $this->_asyncHubs; |
|
616 } |
|
617 |
|
618 /** |
|
619 * Executes an (un)subscribe request |
|
620 * |
|
621 * @param string $mode |
|
622 * @return void |
|
623 */ |
|
624 protected function _doRequest($mode) |
|
625 { |
|
626 $client = $this->_getHttpClient(); |
|
627 $hubs = $this->getHubUrls(); |
|
628 if (empty($hubs)) { |
|
629 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
630 throw new Zend_Feed_Pubsubhubbub_Exception('No Hub Server URLs' |
|
631 . ' have been set so no subscriptions can be attempted'); |
|
632 } |
|
633 $this->_errors = array(); |
|
634 $this->_asyncHubs = array(); |
|
635 foreach ($hubs as $url) { |
|
636 if (array_key_exists($url, $this->_authentications)) { |
|
637 $auth = $this->_authentications[$url]; |
|
638 $client->setAuth($auth[0], $auth[1]); |
|
639 } |
|
640 $client->setUri($url); |
|
641 $client->setRawData( |
|
642 $this->_getRequestParameters($url, $mode), |
|
643 'application/x-www-form-urlencoded' |
|
644 ); |
|
645 $response = $client->request(); |
|
646 if ($response->getStatus() !== 204 |
|
647 && $response->getStatus() !== 202 |
|
648 ) { |
|
649 $this->_errors[] = array( |
|
650 'response' => $response, |
|
651 'hubUrl' => $url, |
|
652 ); |
|
653 /** |
|
654 * At first I thought it was needed, but the backend storage will |
|
655 * allow tracking async without any user interference. It's left |
|
656 * here in case the user is interested in knowing what Hubs |
|
657 * are using async verification modes so they may update Models and |
|
658 * move these to asynchronous processes. |
|
659 */ |
|
660 } elseif ($response->getStatus() == 202) { |
|
661 $this->_asyncHubs[] = array( |
|
662 'response' => $response, |
|
663 'hubUrl' => $url, |
|
664 ); |
|
665 } |
|
666 } |
|
667 } |
|
668 |
|
669 /** |
|
670 * Get a basic prepared HTTP client for use |
|
671 * |
|
672 * @param string $mode Must be "subscribe" or "unsubscribe" |
|
673 * @return Zend_Http_Client |
|
674 */ |
|
675 protected function _getHttpClient() |
|
676 { |
|
677 $client = Zend_Feed_Pubsubhubbub::getHttpClient(); |
|
678 $client->setMethod(Zend_Http_Client::POST); |
|
679 $client->setConfig(array('useragent' => 'Zend_Feed_Pubsubhubbub_Subscriber/' |
|
680 . Zend_Version::VERSION)); |
|
681 return $client; |
|
682 } |
|
683 |
|
684 /** |
|
685 * Return a list of standard protocol/optional parameters for addition to |
|
686 * client's POST body that are specific to the current Hub Server URL |
|
687 * |
|
688 * @param string $hubUrl |
|
689 * @param mode $hubUrl |
|
690 * @return string |
|
691 */ |
|
692 protected function _getRequestParameters($hubUrl, $mode) |
|
693 { |
|
694 if (!in_array($mode, array('subscribe', 'unsubscribe'))) { |
|
695 require_once 'Zend/Feed/Pubsubhubbub/Exception.php'; |
|
696 throw new Zend_Feed_Pubsubhubbub_Exception('Invalid mode specified: "' |
|
697 . $mode . '" which should have been "subscribe" or "unsubscribe"'); |
|
698 } |
|
699 |
|
700 $params = array( |
|
701 'hub.mode' => $mode, |
|
702 'hub.topic' => $this->getTopicUrl(), |
|
703 ); |
|
704 |
|
705 if ($this->getPreferredVerificationMode() |
|
706 == Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC |
|
707 ) { |
|
708 $vmodes = array( |
|
709 Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC, |
|
710 Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC, |
|
711 ); |
|
712 } else { |
|
713 $vmodes = array( |
|
714 Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_ASYNC, |
|
715 Zend_Feed_Pubsubhubbub::VERIFICATION_MODE_SYNC, |
|
716 ); |
|
717 } |
|
718 $params['hub.verify'] = array(); |
|
719 foreach($vmodes as $vmode) { |
|
720 $params['hub.verify'][] = $vmode; |
|
721 } |
|
722 |
|
723 /** |
|
724 * Establish a persistent verify_token and attach key to callback |
|
725 * URL's path/querystring |
|
726 */ |
|
727 $key = $this->_generateSubscriptionKey($params, $hubUrl); |
|
728 $token = $this->_generateVerifyToken(); |
|
729 $params['hub.verify_token'] = $token; |
|
730 |
|
731 // Note: query string only usable with PuSH 0.2 Hubs |
|
732 if (!$this->_usePathParameter) { |
|
733 $params['hub.callback'] = $this->getCallbackUrl() |
|
734 . '?xhub.subscription=' . Zend_Feed_Pubsubhubbub::urlencode($key); |
|
735 } else { |
|
736 $params['hub.callback'] = rtrim($this->getCallbackUrl(), '/') |
|
737 . '/' . Zend_Feed_Pubsubhubbub::urlencode($key); |
|
738 } |
|
739 if ($mode == 'subscribe' && $this->getLeaseSeconds() !== null) { |
|
740 $params['hub.lease_seconds'] = $this->getLeaseSeconds(); |
|
741 } |
|
742 |
|
743 // hub.secret not currently supported |
|
744 $optParams = $this->getParameters(); |
|
745 foreach ($optParams as $name => $value) { |
|
746 $params[$name] = $value; |
|
747 } |
|
748 |
|
749 // store subscription to storage |
|
750 $now = new Zend_Date; |
|
751 $expires = null; |
|
752 if (isset($params['hub.lease_seconds'])) { |
|
753 $expires = $now->add($params['hub.lease_seconds'], Zend_Date::SECOND) |
|
754 ->get('yyyy-MM-dd HH:mm:ss'); |
|
755 } |
|
756 $data = array( |
|
757 'id' => $key, |
|
758 'topic_url' => $params['hub.topic'], |
|
759 'hub_url' => $hubUrl, |
|
760 'created_time' => $now->get('yyyy-MM-dd HH:mm:ss'), |
|
761 'lease_seconds' => $expires, |
|
762 'verify_token' => hash('sha256', $params['hub.verify_token']), |
|
763 'secret' => null, |
|
764 'expiration_time' => $expires, |
|
765 'subscription_state' => Zend_Feed_Pubsubhubbub::SUBSCRIPTION_NOTVERIFIED, |
|
766 ); |
|
767 $this->getStorage()->setSubscription($data); |
|
768 |
|
769 return $this->_toByteValueOrderedString( |
|
770 $this->_urlEncode($params) |
|
771 ); |
|
772 } |
|
773 |
|
774 /** |
|
775 * Simple helper to generate a verification token used in (un)subscribe |
|
776 * requests to a Hub Server. Follows no particular method, which means |
|
777 * it might be improved/changed in future. |
|
778 * |
|
779 * @param string $hubUrl The Hub Server URL for which this token will apply |
|
780 * @return string |
|
781 */ |
|
782 protected function _generateVerifyToken() |
|
783 { |
|
784 if (!empty($this->_testStaticToken)) { |
|
785 return $this->_testStaticToken; |
|
786 } |
|
787 return uniqid(rand(), true) . time(); |
|
788 } |
|
789 |
|
790 /** |
|
791 * Simple helper to generate a verification token used in (un)subscribe |
|
792 * requests to a Hub Server. |
|
793 * |
|
794 * @param string $hubUrl The Hub Server URL for which this token will apply |
|
795 * @return string |
|
796 */ |
|
797 protected function _generateSubscriptionKey(array $params, $hubUrl) |
|
798 { |
|
799 $keyBase = $params['hub.topic'] . $hubUrl; |
|
800 $key = md5($keyBase); |
|
801 return $key; |
|
802 } |
|
803 |
|
804 /** |
|
805 * URL Encode an array of parameters |
|
806 * |
|
807 * @param array $params |
|
808 * @return array |
|
809 */ |
|
810 protected function _urlEncode(array $params) |
|
811 { |
|
812 $encoded = array(); |
|
813 foreach ($params as $key => $value) { |
|
814 if (is_array($value)) { |
|
815 $ekey = Zend_Feed_Pubsubhubbub::urlencode($key); |
|
816 $encoded[$ekey] = array(); |
|
817 foreach ($value as $duplicateKey) { |
|
818 $encoded[$ekey][] |
|
819 = Zend_Feed_Pubsubhubbub::urlencode($duplicateKey); |
|
820 } |
|
821 } else { |
|
822 $encoded[Zend_Feed_Pubsubhubbub::urlencode($key)] |
|
823 = Zend_Feed_Pubsubhubbub::urlencode($value); |
|
824 } |
|
825 } |
|
826 return $encoded; |
|
827 } |
|
828 |
|
829 /** |
|
830 * Order outgoing parameters |
|
831 * |
|
832 * @param array $params |
|
833 * @return array |
|
834 */ |
|
835 protected function _toByteValueOrderedString(array $params) |
|
836 { |
|
837 $return = array(); |
|
838 uksort($params, 'strnatcmp'); |
|
839 foreach ($params as $key => $value) { |
|
840 if (is_array($value)) { |
|
841 foreach ($value as $keyduplicate) { |
|
842 $return[] = $key . '=' . $keyduplicate; |
|
843 } |
|
844 } else { |
|
845 $return[] = $key . '=' . $value; |
|
846 } |
|
847 } |
|
848 return implode('&', $return); |
|
849 } |
|
850 |
|
851 /** |
|
852 * This is STRICTLY for testing purposes only... |
|
853 */ |
|
854 protected $_testStaticToken = null; |
|
855 |
|
856 final public function setTestStaticToken($token) |
|
857 { |
|
858 $this->_testStaticToken = (string) $token; |
|
859 } |
|
860 } |