forked from GNUsocial/gnu-social
		
	
		
			
	
	
		
			213 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
		
		
			
		
	
	
			213 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| 
								 | 
							
								<?php
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Subscription flow:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    $feedinfo->subscribe()
							 | 
						||
| 
								 | 
							
								        generate random verification token
							 | 
						||
| 
								 | 
							
								            save to verify_token
							 | 
						||
| 
								 | 
							
								        sends a sub request to the hub...
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    feedsub/callback
							 | 
						||
| 
								 | 
							
								        hub sends confirmation back to us via GET
							 | 
						||
| 
								 | 
							
								        We verify the request, then echo back the challenge.
							 | 
						||
| 
								 | 
							
								        On our end, we save the time we subscribed and the lease expiration
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								    feedsub/callback
							 | 
						||
| 
								 | 
							
								        hub sends us updates via POST
							 | 
						||
| 
								 | 
							
								        ?
							 | 
						||
| 
								 | 
							
								    
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class FeedDBException extends FeedSubException
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    public $obj;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function __construct($obj)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        parent::__construct('Database insert failure');
							 | 
						||
| 
								 | 
							
								        $this->obj = $obj;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Feedinfo extends Plugin_DataObject
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    public $__table = 'feedinfo';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public $id;
							 | 
						||
| 
								 | 
							
								    public $profile_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public $feeduri;
							 | 
						||
| 
								 | 
							
								    public $homeuri;
							 | 
						||
| 
								 | 
							
								    public $huburi;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // PuSH subscription data
							 | 
						||
| 
								 | 
							
								    public $verify_token;
							 | 
						||
| 
								 | 
							
								    public $sub_start;
							 | 
						||
| 
								 | 
							
								    public $sub_end;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public $created;
							 | 
						||
| 
								 | 
							
								    public $lastupdate;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public /*static*/ function staticGet($k, $v=null)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return parent::staticGet(__CLASS__, $k, $v);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function tableDef()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        class_exists('Schema'); // autoload hack
							 | 
						||
| 
								 | 
							
								        // warning: the autoincrement doesn't seem to set.
							 | 
						||
| 
								 | 
							
								        // alter table feedinfo change column id id int(11) not null  auto_increment;
							 | 
						||
| 
								 | 
							
								        return new TableDef($this->__table,
							 | 
						||
| 
								 | 
							
								                            array(new ColumnDef('id', 'integer',
							 | 
						||
| 
								 | 
							
								                                                null, false, 'PRI', '0', null, true),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('profile_id', 'integer',
							 | 
						||
| 
								 | 
							
								                                                null, false),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('feeduri', 'varchar',
							 | 
						||
| 
								 | 
							
								                                                255, false, 'UNI'),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('homeuri', 'varchar',
							 | 
						||
| 
								 | 
							
								                                                255, false),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('huburi', 'varchar',
							 | 
						||
| 
								 | 
							
								                                                255, false),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('verify_token', 'varchar',
							 | 
						||
| 
								 | 
							
								                                                32, true),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('sub_start', 'datetime',
							 | 
						||
| 
								 | 
							
								                                                null, true),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('sub_end', 'datetime',
							 | 
						||
| 
								 | 
							
								                                                null, true),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('created', 'datetime',
							 | 
						||
| 
								 | 
							
								                                                null, false),
							 | 
						||
| 
								 | 
							
								                                  new ColumnDef('lastupdate', 'datetime',
							 | 
						||
| 
								 | 
							
								                                                null, false)));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function getProfile()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return Profile::staticGet('id', $this->profile_id);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * @param FeedMunger $munger
							 | 
						||
| 
								 | 
							
								     * @return Feedinfo
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public static function ensureProfile($munger)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        $feedinfo = $munger->feedinfo();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $current = self::staticGet('feeduri', $feedinfo->feeduri);
							 | 
						||
| 
								 | 
							
								        if ($current) {
							 | 
						||
| 
								 | 
							
								            // @fixme we should probably update info as necessary
							 | 
						||
| 
								 | 
							
								            return $current;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        $feedinfo->query('BEGIN');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								            $profile = $munger->profile();
							 | 
						||
| 
								 | 
							
								            $result = $profile->insert();
							 | 
						||
| 
								 | 
							
								            if (empty($result)) {
							 | 
						||
| 
								 | 
							
								                throw new FeedDBException($profile);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $feedinfo->profile_id = $profile->id;
							 | 
						||
| 
								 | 
							
								            $result = $feedinfo->insert();
							 | 
						||
| 
								 | 
							
								            if (empty($result)) {
							 | 
						||
| 
								 | 
							
								                throw new FeedDBException($feedinfo);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            $feedinfo->query('COMMIT');
							 | 
						||
| 
								 | 
							
								        } catch (FeedDBException $e) {
							 | 
						||
| 
								 | 
							
								            common_log_db_error($e->obj, 'INSERT', __FILE__);
							 | 
						||
| 
								 | 
							
								            $feedinfo->query('ROLLBACK');
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return $feedinfo;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Send a subscription request to the hub for this feed.
							 | 
						||
| 
								 | 
							
								     * The hub will later send us a confirmation POST to /feedsub/callback.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @return bool true on success, false on failure
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function subscribe()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // @fixme use the verification token
							 | 
						||
| 
								 | 
							
								        #$token = md5(mt_rand() . ':' . $this->feeduri);
							 | 
						||
| 
								 | 
							
								        #$this->verify_token = $token;
							 | 
						||
| 
								 | 
							
								        #$this->update(); // @fixme
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								            $callback = common_local_url('feedsubcallback', array('feed' => $this->id));
							 | 
						||
| 
								 | 
							
								            $headers = array('Content-Type: application/x-www-form-urlencoded');
							 | 
						||
| 
								 | 
							
								            $post = array('hub.mode' => 'subscribe',
							 | 
						||
| 
								 | 
							
								                          'hub.callback' => $callback,
							 | 
						||
| 
								 | 
							
								                          'hub.verify' => 'async',
							 | 
						||
| 
								 | 
							
								                          //'hub.verify_token' => $token,
							 | 
						||
| 
								 | 
							
								                          //'hub.lease_seconds' => 0,
							 | 
						||
| 
								 | 
							
								                          'hub.topic' => $this->feeduri);
							 | 
						||
| 
								 | 
							
								            $client = new HTTPClient();
							 | 
						||
| 
								 | 
							
								            $response = $client->post($this->huburi, $headers, $post);
							 | 
						||
| 
								 | 
							
								            if ($response->getStatus() >= 200 && $response->getStatus() < 300) {
							 | 
						||
| 
								 | 
							
								                common_log(LOG_INFO, __METHOD__ . ': sub req ok');
							 | 
						||
| 
								 | 
							
								                return true;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                common_log(LOG_INFO, __METHOD__ . ': sub req failed');
							 | 
						||
| 
								 | 
							
								                return false;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } catch (Exception $e) {
							 | 
						||
| 
								 | 
							
								            // wtf!
							 | 
						||
| 
								 | 
							
								            common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->feeduri");
							 | 
						||
| 
								 | 
							
								            return false;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    /**
							 | 
						||
| 
								 | 
							
								     * Read and post notices for updates from the feed.
							 | 
						||
| 
								 | 
							
								     * Currently assumes that all items in the feed are new,
							 | 
						||
| 
								 | 
							
								     * coming from a PuSH hub.
							 | 
						||
| 
								 | 
							
								     *
							 | 
						||
| 
								 | 
							
								     * @param string $xml source of Atom or RSS feed
							 | 
						||
| 
								 | 
							
								     */
							 | 
						||
| 
								 | 
							
								    public function postUpdates($xml)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        common_log(LOG_INFO, __METHOD__ . ": packet for \"$this->feeduri\"! $xml");
							 | 
						||
| 
								 | 
							
								        require_once "XML/Feed/Parser.php";
							 | 
						||
| 
								 | 
							
								        $feed = new XML_Feed_Parser($xml, false, false, true);
							 | 
						||
| 
								 | 
							
								        $munger = new FeedMunger($feed);
							 | 
						||
| 
								 | 
							
								        
							 | 
						||
| 
								 | 
							
								        $hits = 0;
							 | 
						||
| 
								 | 
							
								        foreach ($feed as $index => $entry) {
							 | 
						||
| 
								 | 
							
								            // @fixme this might sort in wrong order if we get multiple updates
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            $notice = $munger->notice($index);
							 | 
						||
| 
								 | 
							
								            $notice->profile_id = $this->profile_id;
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            // Double-check for oldies
							 | 
						||
| 
								 | 
							
								            // @fixme this could explode horribly for multiple feeds on a blog. sigh
							 | 
						||
| 
								 | 
							
								            $dupe = new Notice();
							 | 
						||
| 
								 | 
							
								            $dupe->uri = $notice->uri;
							 | 
						||
| 
								 | 
							
								            $dupe->find();
							 | 
						||
| 
								 | 
							
								            if ($dupe->fetch()) {
							 | 
						||
| 
								 | 
							
								                common_log(LOG_WARNING, __METHOD__ . ": tried to save dupe notice for entry {$notice->uri} of feed {$this->feeduri}");
							 | 
						||
| 
								 | 
							
								                continue;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            
							 | 
						||
| 
								 | 
							
								            if (Event::handle('StartNoticeSave', array(&$notice))) {
							 | 
						||
| 
								 | 
							
								                $id = $notice->insert();
							 | 
						||
| 
								 | 
							
								                Event::handle('EndNoticeSave', array($notice));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            $notice->addToInboxes();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            common_log(LOG_INFO, __METHOD__ . ": saved notice {$notice->id} for entry $index of update to \"{$this->feeduri}\"");
							 | 
						||
| 
								 | 
							
								            $hits++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if ($hits == 0) {
							 | 
						||
| 
								 | 
							
								            common_log(LOG_INFO, __METHOD__ . ": no updates in packet for \"$this->feeduri\"! $xml");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 |