]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/OStatus/actions/pushhub.php
Merge remote branch 'gitorious/0.9.x' into 1.0.x
[quix0rs-gnu-social.git] / plugins / OStatus / actions / pushhub.php
index 901c18f70285f2ac302a35d4216eae7e97501b6f..bfd51ec02f65b1d5ce17dc7d537f3c3528aaf9ad 100644 (file)
  * @maintainer Brion Vibber <brion@status.net>
  */
 
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
 /**
 
 
@@ -36,7 +40,6 @@ Things to consider...
 
 */
 
-
 class PushHubAction extends Action
 {
     function arg($arg, $def=null)
@@ -44,7 +47,7 @@ class PushHubAction extends Action
         // PHP converts '.'s in incoming var names to '_'s.
         // It also merges multiple values, which'll break hub.verify and hub.topic for publishing
         // @fixme handle multiple args
-        $arg = str_replace('.', '_', $arg);
+        $arg = str_replace('hub.', 'hub_', $arg);
         return parent::arg($arg, $def);
     }
 
@@ -59,95 +62,129 @@ class PushHubAction extends Action
         $mode = $this->trimmed('hub.mode');
         switch ($mode) {
         case "subscribe":
-            $this->subscribe();
-            break;
         case "unsubscribe":
-            $this->unsubscribe();
+            $this->subunsub($mode);
             break;
         case "publish":
-            throw new ServerException("Publishing outside feeds not supported.", 400);
+            // TRANS: Client exception.
+            throw new ClientException(_m('Publishing outside feeds not supported.'), 400);
         default:
-            throw new ServerException("Unrecognized mode '$mode'.", 400);
+            // TRANS: Client exception. %s is a mode.
+            throw new ClientException(sprintf(_m('Unrecognized mode "%s".'),$mode), 400);
         }
     }
 
     /**
-     * Process a PuSH feed subscription request.
+     * Process a request for a new or modified PuSH feed subscription.
+     * If asynchronous verification is requested, updates won't be saved immediately.
      *
      * HTTP return codes:
      *   202 Accepted - request saved and awaiting verification
      *   204 No Content - already subscribed
-     *   403 Forbidden - rejecting this (not specifically spec'd)
+     *   400 Bad Request - rejecting this (not specifically spec'd)
      */
-    function subscribe()
+    function subunsub($mode)
     {
-        $feed = $this->argUrl('hub.topic');
         $callback = $this->argUrl('hub.callback');
 
-        common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback");
-        if ($this->getSub($feed, $callback)) {
-            // Already subscribed; return 204 per spec.
-            header('HTTP/1.1 204 No Content');
-            common_log(LOG_DEBUG, __METHOD__ . ': already subscribed');
-            return;
+        $topic = $this->argUrl('hub.topic');
+        if (!$this->recognizedFeed($topic)) {
+            // TRANS: Client exception. %s is a topic.
+            throw new ClientException(sprintf(_m('Unsupported hub.topic %s this hub only serves local user and group Atom feeds.'),$topic));
+        }
+
+        $verify = $this->arg('hub.verify'); // @fixme may be multiple
+        if ($verify != 'sync' && $verify != 'async') {
+            // TRANS: Client exception.
+            throw new ClientException(sprintf(_m('Invalid hub.verify "%s". It must be sync or async.'),$verify));
         }
 
-        common_log(LOG_DEBUG, __METHOD__ . ': setting up');
-        $sub = new HubSub();
-        $sub->topic = $feed;
-        $sub->callback = $callback;
-        $sub->secret = $this->arg('hub.secret', null);
-        $sub->setLease(intval($this->arg('hub.lease_seconds')));
-
-        // @fixme check for feeds we don't manage
-        // @fixme check the verification mode, might want a return immediately?
-
-        common_log(LOG_DEBUG, __METHOD__ . ': inserting');
-        $ok = $sub->insert();
-        
-        if (!$ok) {
-            throw new ServerException("Failed to save subscription record", 500);
+        $lease = $this->arg('hub.lease_seconds', null);
+        if ($mode == 'subscribe' && $lease != '' && !preg_match('/^\d+$/', $lease)) {
+            // TRANS: Client exception.
+            throw new ClientException(sprintf(_m('Invalid hub.lease "%s". It must be empty or positive integer.'),$lease));
         }
 
-        // @fixme check errors ;)
+        $token = $this->arg('hub.verify_token', null);
 
-        $data = array('sub' => $sub, 'mode' => 'subscribe');
-        $qm = QueueManager::get();
-        $qm->enqueue($data, 'hubverify');
-        
-        header('HTTP/1.1 202 Accepted');
-        common_log(LOG_DEBUG, __METHOD__ . ': done');
+        $secret = $this->arg('hub.secret', null);
+        if ($secret != '' && strlen($secret) >= 200) {
+            // TRANS: Client exception.
+            throw new ClientException(sprintf(_m('Invalid hub.secret "%s". It must be under 200 bytes.'),$secret));
+        }
+
+        $sub = HubSub::staticGet($topic, $callback);
+        if (!$sub) {
+            // Creating a new one!
+            $sub = new HubSub();
+            $sub->topic = $topic;
+            $sub->callback = $callback;
+        }
+        if ($mode == 'subscribe') {
+            if ($secret) {
+                $sub->secret = $secret;
+            }
+            if ($lease) {
+                $sub->setLease(intval($lease));
+            }
+        }
+
+        if (!common_config('queue', 'enabled')) {
+            // Won't be able to background it.
+            $verify = 'sync';
+        }
+        if ($verify == 'async') {
+            $sub->scheduleVerify($mode, $token);
+            header('HTTP/1.1 202 Accepted');
+        } else {
+            $sub->verify($mode, $token);
+            header('HTTP/1.1 204 No Content');
+        }
     }
 
     /**
-     * Process a PuSH feed unsubscription request.
+     * Check whether the given URL represents one of our canonical
+     * user or group Atom feeds.
      *
-     * HTTP return codes:
-     *   202 Accepted - request saved and awaiting verification
-     *   204 No Content - already subscribed
-     *   400 Bad Request - invalid params or rejected feed
+     * @param string $feed URL
+     * @return boolean true if it matches
      */
-    function unsubscribe()
+    function recognizedFeed($feed)
     {
-        $feed = $this->argUrl('hub.topic');
-        $callback = $this->argUrl('hub.callback');
-        $sub = $this->getSub($feed, $callback);
-        
-        if ($sub) {
-            if ($sub->verify('unsubscribe')) {
-                $sub->delete();
-                common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback");
-            } else {
-                throw new ServerException("Failed PuSH unsubscription: verification failed! $feed for $callback");
+        $matches = array();
+        if (preg_match('!/(\d+)\.atom$!', $feed, $matches)) {
+            $id = $matches[1];
+            $params = array('id' => $id, 'format' => 'atom');
+            $userFeed = common_local_url('ApiTimelineUser', $params);
+            $groupFeed = common_local_url('ApiTimelineGroup', $params);
+
+            if ($feed == $userFeed) {
+                $user = User::staticGet('id', $id);
+                if (!$user) {
+                    // TRANS: Client exception.
+                    throw new ClientException(sprintt(_m('Invalid hub.topic "%s". User doesn\'t exist.'),$feed));
+                } else {
+                    return true;
+                }
             }
-        } else {
-            throw new ServerException("Failed PuSH unsubscription: not subscribed! $feed for $callback");
+            if ($feed == $groupFeed) {
+                $user = User_group::staticGet('id', $id);
+                if (!$user) {
+                    // TRANS: Client exception.
+                    throw new ClientException(sprintf(_m('Invalid hub.topic "%s". Group doesn\'t exist.'),$feed));
+                } else {
+                    return true;
+                }
+            }
+            common_log(LOG_DEBUG, "Not a user or group feed? $feed $userFeed $groupFeed");
         }
+        common_log(LOG_DEBUG, "LOST $feed");
+        return false;
     }
 
     /**
      * Grab and validate a URL from POST parameters.
-     * @throws ServerException for malformed or non-http/https URLs
+     * @throws ClientException for malformed or non-http/https URLs
      */
     protected function argUrl($arg)
     {
@@ -157,7 +194,9 @@ class PushHubAction extends Action
         if (Validate::uri($url, $params)) {
             return $url;
         } else {
-            throw new ServerException("Invalid URL passed for $arg: '$url'", 400);
+            // TRANS: Client exception.
+            // TRANS: %1$s is this argument to the method this exception occurs in, %2$s is a URL.
+            throw new ClientException(sprintf(_m('Invalid URL passed for %1$s: "%2$s"'),$arg,$url));
         }
     }
 
@@ -173,4 +212,3 @@ class PushHubAction extends Action
         return HubSub::staticGet($feed, $callback);
     }
 }
-