]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/OStatus/classes/FeedSub.php
Merge branch '1.0.x' into schema-x
[quix0rs-gnu-social.git] / plugins / OStatus / classes / FeedSub.php
index dc2c0b710bc128b2c0b406678693af8d3d0bca0d..140f323846498c91817d6dc59a20d083dbebd04d 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
 /**
  * @package OStatusPlugin
  * @maintainer Brion Vibber <brion@status.net>
@@ -39,7 +43,6 @@ PuSH subscription flow:
         hub sends us updates via POST
 
 */
-
 class FeedDBException extends FeedSubException
 {
     public $obj;
@@ -61,7 +64,7 @@ class FeedSub extends Memcached_DataObject
     public $__table = 'feedsub';
 
     public $id;
-    public $feeduri;
+    public $uri;
 
     // PuSH subscription data
     public $huburi;
@@ -88,7 +91,6 @@ class FeedSub extends Memcached_DataObject
      *
      * @return array array of column definitions
      */
-
     function table()
     {
         return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
@@ -99,7 +101,7 @@ class FeedSub extends Memcached_DataObject
                      'sub_state' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
                      'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
                      'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
-                     'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
+                     'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
                      'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
                      'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
     }
@@ -110,7 +112,7 @@ class FeedSub extends Memcached_DataObject
                                    /*size*/ null,
                                    /*nullable*/ false,
                                    /*key*/ 'PRI',
-                                   /*default*/ '0',
+                                   /*default*/ null,
                                    /*extra*/ null,
                                    /*auto_increment*/ true),
                      new ColumnDef('uri', 'varchar',
@@ -143,7 +145,6 @@ class FeedSub extends Memcached_DataObject
      *
      * @return array key definitions
      */
-
     function keys()
     {
         return array_keys($this->keyTypes());
@@ -157,7 +158,6 @@ class FeedSub extends Memcached_DataObject
      *
      * @return array key definitions
      */
-
     function keyTypes()
     {
         return array('id' => 'K', 'uri' => 'U');
@@ -207,8 +207,8 @@ class FeedSub extends Memcached_DataObject
         $discover = new FeedDiscovery();
         $discover->discoverFromFeedURL($feeduri);
 
-        $huburi = $discover->getAtomLink('hub');
-        if (!$huburi) {
+        $huburi = $discover->getHubLink();
+        if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
             throw new FeedSubNoHubException();
         }
 
@@ -238,14 +238,18 @@ class FeedSub extends Memcached_DataObject
     public function subscribe($mode='subscribe')
     {
         if ($this->sub_state && $this->sub_state != 'inactive') {
-            throw new ServerException("Attempting to start PuSH subscription to feed in state $this->sub_state");
+            common_log(LOG_WARNING, "Attempting to (re)start PuSH subscription to $this->uri in unexpected state $this->sub_state");
         }
         if (empty($this->huburi)) {
-            if (common_config('feedsub', 'nohub')) {
+            if (common_config('feedsub', 'fallback_hub')) {
+                // No native hub on this feed?
+                // Use our fallback hub, which handles polling on our behalf.
+            } else if (common_config('feedsub', 'nohub')) {
                 // Fake it! We're just testing remote feeds w/o hubs.
+                // We'll never actually get updates in this mode.
                 return true;
             } else {
-                throw new ServerException("Attempting to start PuSH subscription for feed with no hub");
+                throw new ServerException(_m('Attempting to start PuSH subscription for feed with no hub.'));
             }
         }
 
@@ -255,26 +259,60 @@ class FeedSub extends Memcached_DataObject
     /**
      * Send a PuSH unsubscription request to the hub for this feed.
      * The hub will later send us a confirmation POST to /main/push/callback.
+     * Warning: this will cancel the subscription even if someone else in
+     * the system is using it. Most callers will want garbageCollect() instead,
+     * which confirms there's no uses left.
      *
      * @return bool true on success, false on failure
      * @throws ServerException if feed state is not valid
      */
     public function unsubscribe() {
         if ($this->sub_state != 'active') {
-            throw new ServerException("Attempting to end PuSH subscription to feed in state $this->sub_state");
+            common_log(LOG_WARNING, "Attempting to (re)end PuSH subscription to $this->uri in unexpected state $this->sub_state");
         }
         if (empty($this->huburi)) {
-            if (common_config('feedsub', 'nohub')) {
+            if (common_config('feedsub', 'fallback_hub')) {
+                // No native hub on this feed?
+                // Use our fallback hub, which handles polling on our behalf.
+            } else if (common_config('feedsub', 'nohub')) {
                 // Fake it! We're just testing remote feeds w/o hubs.
+                // We'll never actually get updates in this mode.
                 return true;
             } else {
-                throw new ServerException("Attempting to end PuSH subscription for feed with no hub");
+                throw new ServerException(_m('Attempting to end PuSH subscription for feed with no hub.'));
             }
         }
 
         return $this->doSubscribe('unsubscribe');
     }
 
+    /**
+     * Check if there are any active local uses of this feed, and if not then
+     * make sure it's inactive, unsubscribing if necessary.
+     *
+     * @return boolean true if the subscription is now inactive, false if still active.
+     */
+    public function garbageCollect()
+    {
+        if ($this->sub_state == '' || $this->sub_state == 'inactive') {
+            // No active PuSH subscription, we can just leave it be.
+            return true;
+        } else {
+            // PuSH subscription is either active or in an indeterminate state.
+            // Check if we're out of subscribers, and if so send an unsubscribe.
+            $count = 0;
+            Event::handle('FeedSubSubscriberCount', array($this, &$count));
+
+            if ($count) {
+                common_log(LOG_INFO, __METHOD__ . ': ok, ' . $count . ' user(s) left for ' . $this->uri);
+                return false;
+            } else {
+                common_log(LOG_INFO, __METHOD__ . ': unsubscribing, no users left for ' . $this->uri);
+                return $this->unsubscribe();
+            }
+        }
+    }
+
     protected function doSubscribe($mode)
     {
         $orig = clone($this);
@@ -291,13 +329,26 @@ class FeedSub extends Memcached_DataObject
             $headers = array('Content-Type: application/x-www-form-urlencoded');
             $post = array('hub.mode' => $mode,
                           'hub.callback' => $callback,
-                          'hub.verify' => 'async',
+                          'hub.verify' => 'sync',
                           'hub.verify_token' => $this->verify_token,
                           'hub.secret' => $this->secret,
-                          //'hub.lease_seconds' => 0,
                           'hub.topic' => $this->uri);
             $client = new HTTPClient();
-            $response = $client->post($this->huburi, $headers, $post);
+            if ($this->huburi) {
+                $hub = $this->huburi;
+            } else {
+                if (common_config('feedsub', 'fallback_hub')) {
+                    $hub = common_config('feedsub', 'fallback_hub');
+                    if (common_config('feedsub', 'hub_user')) {
+                        $u = common_config('feedsub', 'hub_user');
+                        $p = common_config('feedsub', 'hub_pass');
+                        $client->setAuth($u, $p);
+                    }
+                } else {
+                    throw new FeedSubException('WTF?');
+                }
+            }
+            $response = $client->post($hub, $headers, $post);
             $status = $response->getStatus();
             if ($status == 202) {
                 common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback');
@@ -317,8 +368,8 @@ class FeedSub extends Memcached_DataObject
             common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->uri");
 
             $orig = clone($this);
-            $this->verify_token = null;
-            $this->sub_state = null;
+            $this->verify_token = '';
+            $this->sub_state = 'inactive';
             $this->update($orig);
             unset($orig);
 
@@ -343,7 +394,7 @@ class FeedSub extends Memcached_DataObject
         } else {
             $this->sub_end = null;
         }
-        $this->lastupdate = common_sql_now();
+        $this->modified = common_sql_now();
 
         return $this->update($original);
     }
@@ -362,7 +413,7 @@ class FeedSub extends Memcached_DataObject
         $this->sub_state = '';
         $this->sub_start = '';
         $this->sub_end = '';
-        $this->lastupdate = common_sql_now();
+        $this->modified = common_sql_now();
 
         return $this->update($original);
     }
@@ -372,6 +423,12 @@ class FeedSub extends Memcached_DataObject
      * feed (as a DOMDocument) will be passed to the StartFeedSubHandleFeed
      * and EndFeedSubHandleFeed events for processing.
      *
+     * Not guaranteed to be running in an immediate POST context; may be run
+     * from a queue handler.
+     *
+     * Side effects: the feedsub record's lastupdate field will be updated
+     * to the current time (not published time) if we got a legit update.
+     *
      * @param string $post source of Atom or RSS feed
      * @param string $hmac X-Hub-Signature header, if present
      */
@@ -402,6 +459,10 @@ class FeedSub extends Memcached_DataObject
             return;
         }
 
+        $orig = clone($this);
+        $this->last_update = common_sql_now();
+        $this->update($orig);
+
         Event::handle('StartFeedSubReceive', array($this, $feed));
         Event::handle('EndFeedSubReceive', array($this, $feed));
     }
@@ -439,5 +500,4 @@ class FeedSub extends Memcached_DataObject
         }
         return false;
     }
-
 }