8 generate random verification token
10 sends a sub request to the hub...
13 hub sends confirmation back to us via GET
14 We verify the request, then echo back the challenge.
15 On our end, we save the time we subscribed and the lease expiration
18 hub sends us updates via POST
23 class FeedDBException extends FeedSubException
27 function __construct($obj)
29 parent::__construct('Database insert failure');
34 class Feedinfo extends Memcached_DataObject
36 public $__table = 'feedinfo';
45 // PuSH subscription data
54 public /*static*/ function staticGet($k, $v=null)
56 return parent::staticGet(__CLASS__, $k, $v);
60 * return table definition for DB_DataObject
62 * DB_DataObject needs to know something about the table to manipulate
63 * instances. This method provides all the DB_DataObject needs to know.
65 * @return array array of column definitions
70 return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
71 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
72 'feeduri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
73 'homeuri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
74 'huburi' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL,
75 'verify_token' => DB_DATAOBJECT_STR,
76 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
77 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME,
78 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
79 'lastupdate' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
82 static function schemaDef()
84 return array(new ColumnDef('id', 'integer',
90 /*auto_increment*/ true),
91 new ColumnDef('profile_id', 'integer',
93 new ColumnDef('feeduri', 'varchar',
95 new ColumnDef('homeuri', 'varchar',
97 new ColumnDef('huburi', 'varchar',
99 new ColumnDef('verify_token', 'varchar',
101 new ColumnDef('sub_start', 'datetime',
103 new ColumnDef('sub_end', 'datetime',
105 new ColumnDef('created', 'datetime',
107 new ColumnDef('lastupdate', 'datetime',
112 * return key definitions for DB_DataObject
114 * DB_DataObject needs to know about keys that the table has; this function
117 * @return array key definitions
122 return array('id' => 'P'); //?
126 * return key definitions for Memcached_DataObject
128 * Our caching system uses the same key definitions, but uses a different
129 * method to get them.
131 * @return array key definitions
136 return $this->keys();
140 * Fetch the StatusNet-side profile for this feed
143 public function getProfile()
145 return Profile::staticGet('id', $this->profile_id);
149 * @param FeedMunger $munger
152 public static function ensureProfile($munger)
154 $feedinfo = $munger->feedinfo();
156 $current = self::staticGet('feeduri', $feedinfo->feeduri);
158 // @fixme we should probably update info as necessary
162 $feedinfo->query('BEGIN');
165 $profile = $munger->profile();
166 $result = $profile->insert();
167 if (empty($result)) {
168 throw new FeedDBException($profile);
171 $feedinfo->profile_id = $profile->id;
172 $result = $feedinfo->insert();
173 if (empty($result)) {
174 throw new FeedDBException($feedinfo);
177 $feedinfo->query('COMMIT');
178 } catch (FeedDBException $e) {
179 common_log_db_error($e->obj, 'INSERT', __FILE__);
180 $feedinfo->query('ROLLBACK');
187 * Send a subscription request to the hub for this feed.
188 * The hub will later send us a confirmation POST to /feedsub/callback.
190 * @return bool true on success, false on failure
192 public function subscribe()
194 // @fixme use the verification token
195 #$token = md5(mt_rand() . ':' . $this->feeduri);
196 #$this->verify_token = $token;
197 #$this->update(); // @fixme
200 $callback = common_local_url('feedsubcallback', array('feed' => $this->id));
201 $headers = array('Content-Type: application/x-www-form-urlencoded');
202 $post = array('hub.mode' => 'subscribe',
203 'hub.callback' => $callback,
204 'hub.verify' => 'async',
205 //'hub.verify_token' => $token,
206 //'hub.lease_seconds' => 0,
207 'hub.topic' => $this->feeduri);
208 $client = new HTTPClient();
209 $response = $client->post($this->huburi, $headers, $post);
210 if ($response->getStatus() >= 200 && $response->getStatus() < 300) {
211 common_log(LOG_INFO, __METHOD__ . ': sub req ok');
214 common_log(LOG_INFO, __METHOD__ . ': sub req failed');
217 } catch (Exception $e) {
219 common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->feeduri");
225 * Read and post notices for updates from the feed.
226 * Currently assumes that all items in the feed are new,
227 * coming from a PuSH hub.
229 * @param string $xml source of Atom or RSS feed
231 public function postUpdates($xml)
233 common_log(LOG_INFO, __METHOD__ . ": packet for \"$this->feeduri\"! $xml");
234 require_once "XML/Feed/Parser.php";
235 $feed = new XML_Feed_Parser($xml, false, false, true);
236 $munger = new FeedMunger($feed);
239 foreach ($feed as $index => $entry) {
240 // @fixme this might sort in wrong order if we get multiple updates
242 $notice = $munger->notice($index);
243 $notice->profile_id = $this->profile_id;
245 // Double-check for oldies
246 // @fixme this could explode horribly for multiple feeds on a blog. sigh
247 $dupe = new Notice();
248 $dupe->uri = $notice->uri;
250 if ($dupe->fetch()) {
251 common_log(LOG_WARNING, __METHOD__ . ": tried to save dupe notice for entry {$notice->uri} of feed {$this->feeduri}");
255 if (Event::handle('StartNoticeSave', array(&$notice))) {
256 $id = $notice->insert();
257 Event::handle('EndNoticeSave', array($notice));
259 $notice->addToInboxes();
261 common_log(LOG_INFO, __METHOD__ . ": saved notice {$notice->id} for entry $index of update to \"{$this->feeduri}\"");
265 common_log(LOG_INFO, __METHOD__ . ": no updates in packet for \"$this->feeduri\"! $xml");