]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Update PuSH callback URL if remote side switched to HTTPS
authorMikael Nordfeldth <mmn@hethane.se>
Mon, 11 Jan 2016 18:55:02 +0000 (19:55 +0100)
committerMikael Nordfeldth <mmn@hethane.se>
Mon, 11 Jan 2016 18:55:02 +0000 (19:55 +0100)
See the comment in the source on why we're not following Location headers...

plugins/OStatus/classes/HubSub.php

index c9d65c56a7a45b09a6beff359f1d2753207eeb23..68151b19a1ad9f49447453f034450a9b6be596a5 100644 (file)
@@ -40,7 +40,7 @@ class HubSub extends Managed_DataObject
     public $created;
     public $modified;
 
-    protected static function hashkey($topic, $callback)
+    static function hashkey($topic, $callback)
     {
         return sha1($topic . '|' . $callback);
     }
@@ -120,6 +120,11 @@ class HubSub extends Managed_DataObject
         $qm->enqueue($data, 'hubconf');
     }
 
+    public function getTopic()
+    {
+        return $this->topic;
+    }
+
     /**
      * Send a verification ping to subscriber, and if confirmed apply the changes.
      * This may create, update, or delete the database record.
@@ -134,7 +139,7 @@ class HubSub extends Managed_DataObject
 
         $challenge = common_random_hexstr(32);
         $params = array('hub.mode' => $mode,
-                        'hub.topic' => $this->topic,
+                        'hub.topic' => $this->getTopic(),
                         'hub.challenge' => $challenge);
         if ($mode == 'subscribe') {
             $params['hub.lease_seconds'] = $this->lease;
@@ -157,13 +162,13 @@ class HubSub extends Managed_DataObject
         $status = $response->getStatus();
 
         if ($status >= 200 && $status < 300) {
-            common_log(LOG_INFO, "Verified {$mode} of {$this->callback}:{$this->topic}");
+            common_log(LOG_INFO, "Verified {$mode} of {$this->callback}:{$this->getTopic()}");
         } else {
             // TRANS: Client exception. %s is a HTTP status code.
             throw new ClientException(sprintf(_m('Hub subscriber verification returned HTTP %s.'),$status));
         }
 
-        $old = HubSub::getByHashkey($this->topic, $this->callback);
+        $old = HubSub::getByHashkey($this->getTopic(), $this->callback);
         if ($mode == 'subscribe') {
             if ($old instanceof HubSub) {
                 $this->update($old);
@@ -185,7 +190,7 @@ class HubSub extends Managed_DataObject
      */
     function insert()
     {
-        $this->hashkey = self::hashkey($this->topic, $this->callback);
+        $this->hashkey = self::hashkey($this->getTopic(), $this->callback);
         $this->created = common_sql_now();
         $this->modified = common_sql_now();
         return parent::insert();
@@ -208,11 +213,11 @@ class HubSub extends Managed_DataObject
         // destroy the result data for the parent query.
         // @fixme use clone() again when it's safe to copy an
         // individual item from a multi-item query again.
-        $sub = HubSub::getByHashkey($this->topic, $this->callback);
+        $sub = HubSub::getByHashkey($this->getTopic(), $this->callback);
         $data = array('sub' => $sub,
                       'atom' => $atom,
                       'retries' => $retries);
-        common_log(LOG_INFO, "Queuing PuSH: $this->topic to $this->callback");
+        common_log(LOG_INFO, "Queuing PuSH: {$this->getTopic()} to {$this->callback}");
         $qm = QueueManager::get();
         $qm->enqueue($data, 'hubout');
     }
@@ -229,10 +234,9 @@ class HubSub extends Managed_DataObject
     function bulkDistribute($atom, $pushCallbacks)
     {
         $data = array('atom' => $atom,
-                      'topic' => $this->topic,
+                      'topic' => $this->getTopic(),
                       'pushCallbacks' => $pushCallbacks);
-        common_log(LOG_INFO, "Queuing PuSH batch: $this->topic to " .
-                             count($pushCallbacks) . " sites");
+        common_log(LOG_INFO, "Queuing PuSH batch: {$this->getTopic()} to ".count($pushCallbacks)." sites");
         $qm = QueueManager::get();
         $qm->enqueue($data, 'hubprep');
     }
@@ -256,18 +260,58 @@ class HubSub extends Managed_DataObject
         } else {
             $hmac = '(none)';
         }
-        common_log(LOG_INFO, "About to push feed to $this->callback for $this->topic, HMAC $hmac");
+        common_log(LOG_INFO, "About to push feed to $this->callback for {$this->getTopic()}, HMAC $hmac");
 
         $request = new HTTPClient();
         $request->setBody($atom);
-        $response = $request->post($this->callback, $headers);
+        try {
+            $response = $request->post($this->callback, $headers);
 
-        if ($response->isOk()) {
-            return true;
-        } else {
-            // TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
-            throw new Exception(sprintf(_m('Callback returned status: %1$s. Body: %2$s'),
-                                $response->getStatus(),trim($response->getBody())));
+            if ($response->isOk()) {
+                return true;
+            }
+        } catch (Exception $e) {
+            $response = null;
+
+            common_debug('PuSH callback to '._ve($this->callback).' for '._ve($this->getTopic()).' failed with exception: '._ve($e->getMessage()));
         }
+
+        // XXX: DO NOT trust a Location header here, _especially_ from 'http' protocols,
+        // but not 'https' either at least if we don't do proper CA verification. Trust that
+        // the most common change here is simply switching 'http' to 'https' and we will
+        // solve 99% of all of these issues for now. There should be a proper mechanism
+        // if we want to change the callback URLs, preferrably just manual resubscriptions
+        // from the remote side, combined with implemented PuSH subscription timeouts.
+
+        // We failed the PuSH, but it might be that the remote site has changed their configuration to HTTPS
+        if ('http' === parse_url($this->callback, PHP_URL_SCHEME)) {
+            // Test if the feed callback for this node has migrated to HTTPS
+            $httpscallback = preg_replace('/^http/', 'https', $this->callback, 1);
+            if ($httpscallback === $this->callback) {
+                throw new ServerException('Trying to preg_replace http to https on '._ve($this->callback).' failed and resulted in an identical string: '._ve($httpscallback).'.');
+            }
+            common_debug('PuSH callback to '._ve($this->callback).' for '._ve($this->getTopic()).' testing with HTTPS callback: '._ve($httpscallback));
+            $response = $request->post($httpscallback, $headers);
+            if ($response->isOk()) {
+                $orig = clone($this);
+                $this->callback = $httpscallback;
+                $this->hashkey = self::hashkey($this->getTopic(), $this->callback);
+                common_debug('HubSub DEBUG, from '._ve($orig).' to '._ve($this));
+                $this->updateWithKeys($orig, 'hashkey');
+                return true;
+            }
+        }
+
+        // FIXME: Add 'failed' incremental count for this callback.
+
+        if (is_null($response)) {
+            // This means we got a lower-than-HTTP level error, like domain not found or maybe connection refused
+            // This should be using a more distinguishable exception class, but for now this will do.
+            throw new Exception(sprintf(_m('HTTP request failed without response to URL: %s'), var_export($target, true)));
+        }
+
+        // TRANS: Exception. %1$s is a response status code, %2$s is the body of the response.
+        throw new Exception(sprintf(_m('Callback returned status: %1$s. Body: %2$s'),
+                            $response->getStatus(),trim($response->getBody())));
     }
 }