]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/OStatus/actions/pushhub.php
Got photos displaying in the feed *the right way*
[quix0rs-gnu-social.git] / plugins / OStatus / actions / pushhub.php
index 19599d815f51b2ab1344f6a965976e7f28574ec5..842d65e7d29d9eac29518249560aa98c3e37a7e7 100644 (file)
@@ -59,102 +59,121 @@ 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);
+            throw new ClientException("Publishing outside feeds not supported.", 400);
         default:
-            throw new ServerException("Unrecognized mode '$mode'.", 400);
+            throw new ClientException("Unrecognized mode '$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');
-        $token = $this->arg('hub.verify_token', null);
 
-        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)) {
+            throw new ClientException("Unsupported hub.topic $topic; this hub only serves local user and group Atom feeds.");
         }
 
-        common_log(LOG_DEBUG, __METHOD__ . ': setting up');
-        $sub = new HubSub();
-        $sub->topic = $feed;
-        $sub->callback = $callback;
-        $sub->secret = $this->arg('hub.secret', null);
-        if (strlen($sub->secret) > 200) {
-            throw new ClientException("hub.secret must be no longer than 200 chars", 400);
+        $verify = $this->arg('hub.verify'); // @fixme may be multiple
+        if ($verify != 'sync' && $verify != 'async') {
+            throw new ClientException("Invalid hub.verify $verify; must be sync or async.");
         }
-        $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?
+        $lease = $this->arg('hub.lease_seconds', null);
+        if ($mode == 'subscribe' && $lease != '' && !preg_match('/^\d+$/', $lease)) {
+            throw new ClientException("Invalid hub.lease $lease; must be empty or positive integer.");
+        }
+
+        $token = $this->arg('hub.verify_token', null);
 
-        common_log(LOG_DEBUG, __METHOD__ . ': inserting');
-        $ok = $sub->insert();
-        
-        if (!$ok) {
-            throw new ServerException("Failed to save subscription record", 500);
+        $secret = $this->arg('hub.secret', null);
+        if ($secret != '' && strlen($secret) >= 200) {
+            throw new ClientException("Invalid hub.secret $secret; must be under 200 bytes.");
         }
 
-        // @fixme check errors ;)
+        $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));
+            }
+        }
 
-        $data = array('sub' => $sub, 'mode' => 'subscribe', 'token' => $token);
-        $qm = QueueManager::get();
-        $qm->enqueue($data, 'hubverify');
-        
-        header('HTTP/1.1 202 Accepted');
-        common_log(LOG_DEBUG, __METHOD__ . ': done');
+        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.
-     *
-     * HTTP return codes:
-     *   202 Accepted - request saved and awaiting verification
-     *   204 No Content - already subscribed
-     *   400 Bad Request - invalid params or rejected feed
+     * Check whether the given URL represents one of our canonical
+     * user or group Atom feeds.
      *
-     * @fixme background this
+     * @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) {
-            $token = $this->arg('hub.verify_token', null);
-            if ($sub->verify('unsubscribe', $token)) {
-                $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) {
+                    throw new ClientException("Invalid hub.topic $feed; user doesn't exist.");
+                } 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) {
+                    throw new ClientException("Invalid hub.topic $feed; group doesn't exist.");
+                } 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)
     {
@@ -164,7 +183,7 @@ class PushHubAction extends Action
         if (Validate::uri($url, $params)) {
             return $url;
         } else {
-            throw new ServerException("Invalid URL passed for $arg: '$url'", 400);
+            throw new ClientException("Invalid URL passed for $arg: '$url'");
         }
     }