You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.0 KiB

  1. <?php
  2. function push_error(&$app, $msg) {
  3. $app->response()->status(400);
  4. echo $msg . "\n";
  5. die();
  6. }
  7. function push_param($params, $name) {
  8. // Look for 'mode' first, fall back to 'hub_mode'
  9. if(k($params, $name))
  10. return k($params, $name);
  11. return k($params, 'hub_'.$name);
  12. }
  13. ///////////////////////////////////////////////////////////////
  14. // These are just test routes
  15. $app->get('/callback-success', function() use($app) {
  16. $params = $app->request()->params();
  17. $app->response()->status(200);
  18. echo $params['hub_challenge'];
  19. });
  20. $app->post('/callback-success', function() use($app) {
  21. $params = $app->request()->params();
  22. $app->response()->status(200);
  23. });
  24. $app->get('/callback-fail', function() use($app) {
  25. $params = $app->request()->params();
  26. $app->response()->status(404);
  27. });
  28. ///////////////////////////////////////////////////////////////
  29. function verify_push_topic_url($topic, &$app) {
  30. // If we've already seen the topic, assume it's valid and don't check it again
  31. if(!db\feed_from_url($topic)) {
  32. $topic_head = request\get_head($topic);
  33. if($topic_head && !request\response_is($topic_head['status'], 2)) {
  34. push_error($app, "The topic URL returned a " . $topic_head['status'] . " status code");
  35. } elseif(!$topic_head) {
  36. push_error($app, 'We tried to verify the topic URL exists but it didn\'t respond to a HEAD request.');
  37. }
  38. }
  39. }
  40. $app->post('/', function() use($app) {
  41. $params = $app->request()->params();
  42. switch($mode=push_param($params, 'mode')) {
  43. case 'subscribe':
  44. case 'unsubscribe':
  45. // Sanity check the request params
  46. $topic = push_param($params, 'url');
  47. // Support subscribing using either "url" or "topic" parameters
  48. if(!$topic) {
  49. $topic = push_param($params, 'topic');
  50. }
  51. $callback = push_param($params, 'callback');
  52. if(!$topic) {
  53. push_error($app, 'No topic URL was specified. Send the topic URL in a parameter named "topic"');
  54. }
  55. if(!$callback) {
  56. push_error($app, 'No callback URL was specified. Send the callback URL in a parameter named "callback"');
  57. }
  58. if(!is_valid_push_url($topic)) {
  59. push_error($app, 'Topic URL was invalid ('.$topic.')');
  60. }
  61. if(!is_valid_push_url($callback)) {
  62. push_error($app, 'Callback URL was invalid');
  63. }
  64. $namespaced = k($params, 'hub_mode') ? 1 : 0; // set namespaced=1 if they used hub_mode in the request
  65. if($mode == 'subscribe') {
  66. verify_push_topic_url($topic, $app);
  67. // Find or create the feed given the topic URL
  68. $feed = db\find_or_create('feeds', ['feed_url'=>$topic], [
  69. 'hash' => db\random_hash(),
  70. ], true);
  71. // Find or create the subscription for this callback URL and feed
  72. $subscription = db\find_or_create('subscriptions', ['feed_id'=>$feed->id, 'callback_url'=>$callback], [
  73. 'hash' => db\random_hash()
  74. ], true);
  75. // Always set a new requested date and challenge
  76. $subscription->date_requested = db\now();
  77. $subscription->challenge = db\random_hash();
  78. $subscription->namespaced = $namespaced;
  79. if($secret=push_param($params, 'secret')) {
  80. $subscription->secret = $secret;
  81. }
  82. db\set_updated($subscription);
  83. $subscription->save();
  84. // Queue the worker to validate the subscription
  85. DeferredTask::queue('PushTask', 'verify_subscription', [$subscription->id, 'subscribe']);
  86. } else {
  87. $feed = db\feed_from_url($topic);
  88. if(!$feed) {
  89. push_error($app, 'The topic was not found, so there is no subscription active');
  90. }
  91. $subscription = db\find('subscriptions', ['feed_id'=>$feed->id, 'callback_url'=>$callback]);
  92. if(!$subscription) {
  93. push_error($app, 'There was no subscription found for this callback URL and topic');
  94. }
  95. // Queue the worker to validate the subscription
  96. DeferredTask::queue('PushTask', 'verify_subscription', [$subscription->id, 'unsubscribe']);
  97. }
  98. $app->response()->status(202);
  99. echo "The subscription request is being validated. Check the status here:\n";
  100. echo Config::$base_url . '/subscription/' . $subscription->hash . "\n";
  101. break;
  102. case 'publish':
  103. // Sanity check the request params
  104. $urls = push_param($params, 'url');
  105. // Allow publishers to use either "url" or "topic" to indicate the URL that changed
  106. if(!$urls) {
  107. $urls = push_param($params, 'topic');
  108. }
  109. if(!$urls) {
  110. push_error($app, 'No URL was specified. When publishing, send the topic URL in a parameter named "url"');
  111. }
  112. if(!is_array($urls)) {
  113. $urls = array($urls);
  114. }
  115. foreach($urls as $url) {
  116. if(!is_valid_push_url($url)) {
  117. push_error($app, 'URL was invalid');
  118. }
  119. verify_push_topic_url($url, $app);
  120. // Find or create the feed given the topic URL
  121. $feed = db\find_or_create('feeds', ['feed_url'=>$url], [
  122. 'hash' => db\random_hash(),
  123. ], true);
  124. $num_subscribers = ORM::for_table('subscriptions')->where('feed_id', $feed->id)->where('active', 1)->count();
  125. $feed->push_last_ping_received = db\now();
  126. db\set_updated($feed);
  127. $feed->save();
  128. $app->response()->status(202);
  129. if($num_subscribers > 0) {
  130. // Queue the worker to ping all the subscribers about the new content
  131. DeferredTask::queue('PushTask', 'publish', $feed->id);
  132. $app->response()->body(
  133. "$url\n".
  134. "There are currently $num_subscribers active subscriptions for this feed.\n".
  135. "The hub is checking the feed for new content and notifying the subscribers.\nCheck the status here:\n".
  136. Config::$base_url . '/feed/' . $feed->hash . "\n\n"
  137. );
  138. } else {
  139. $app->response()->body(
  140. "$url\n".
  141. "There are no active subscriptions for this feed.\n"
  142. );
  143. }
  144. }
  145. break;
  146. }
  147. });