script to renew websub subscriptions
[phinde.git] / src / phinde / Subscriptions.php
1 <?php
2 namespace phinde;
3
4 /**
5  * Database table containing information about Pubsubhubbub subscriptions
6  */
7 class Subscriptions
8 {
9     protected $db;
10
11     public function __construct()
12     {
13         $this->db = new \PDO(
14             $GLOBALS['phinde']['db_dsn'],
15             $GLOBALS['phinde']['db_user'],
16             $GLOBALS['phinde']['db_pass']
17         );
18         $this->db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); 
19     }
20
21     /**
22      * Fetch a topic
23      *
24      * @param string $topic Topic URL
25      *
26      * @return false|object False if the row does not exist
27      */
28     public function get($topic)
29     {
30         $stmt = $this->db->prepare(
31             'SELECT * FROM subscriptions'
32             . ' WHERE sub_topic = :topic'
33         );
34         $stmt->execute([':topic' => $topic]);
35
36         //fetchObject() itself returns FALSE on failure
37         return $stmt->fetchObject();
38     }
39
40     /**
41      * Count number of subscriptions
42      *
43      * @return array Array of keys with different status, number as value
44      */
45     public function count()
46     {
47         $stmt = $this->db->prepare(
48             'SELECT COUNT(*) as count, sub_status FROM subscriptions'
49             . ' GROUP BY sub_status'
50             . ' ORDER BY sub_status'
51         );
52         $stmt->execute();
53
54         $res = [];
55         foreach ($stmt as $row) {
56             $res[$row['sub_status']] = $row['count'];
57         }
58
59         return $res;
60     }
61
62     /**
63      * Get all topics that either expired or expire soon
64      *
65      * @return \PDOStatement Result iterator
66      */
67     public function getExpiring()
68     {
69         $stmt = $this->db->prepare(
70             'SELECT * FROM subscriptions'
71             . ' WHERE sub_status IN ("active", "expired")'
72             . ' AND DATEDIFF(sub_expires, NOW()) <= 2'
73         );
74         $stmt->execute();
75
76         return $stmt;
77     }
78
79     /**
80      * Create a new subscription entry in database.
81      * Automatically generates secret, capkey and lease seconds.
82      *
83      * This method does NOT:
84      * - check for duplicates (do it yourself)
85      * - return the object (fetch it yourself)
86      * - send subscription requests to the hub
87      *
88      * @param string $topic URL to subscribe to
89      *
90      * @return void
91      */
92     public function create($topic)
93     {
94         $stmt = $this->db->prepare(
95             'INSERT INTO subscriptions'
96             . ' (sub_topic, sub_status, sub_lease_seconds, sub_expires'
97             . ', sub_secret, sub_capkey, sub_created, sub_updated'
98             . ', sub_pings, sub_lastping, sub_statusmessage)'
99             . ' VALUES '
100             . ' (:topic, "subscribing", :lease_seconds, "0000-00-00 00:00:00"'
101             . ', :secret, :capkey, NOW(), NOW()'
102             . ', 0, "0000-00-00 00:00:00", "")'
103         );
104         $stmt->execute(
105             [
106                 ':topic'         => $topic,
107                 ':lease_seconds' => 86400 * 30,
108                 ':secret'        => bin2hex(openssl_random_pseudo_bytes(16)),
109                 ':capkey'        => bin2hex(openssl_random_pseudo_bytes(16)),
110             ]
111         );
112     }
113
114     /**
115      * Renew a subscription: Set its status to "subscribing"
116      *
117      * @param integer $subId Subscription ID
118      *
119      * @return void
120      */
121     public function renew($subId)
122     {
123         $this->db->prepare(
124             'UPDATE subscriptions'
125             . ' SET sub_status  = "subscribing"'
126             . '   , sub_updated = NOW()'
127             . ' WHERE sub_id = :id'
128         )->execute([':id' => $subId]);
129     }
130
131     /**
132      * A subscription has been confirmed by the hub - mark it as active.
133      *
134      * @param integer $subId        Subscription ID
135      * @param integer $leaseSeconds Number of seconds until subscription expires
136      *
137      * @return void
138      */
139     public function subscribed($subId, $leaseSeconds)
140     {
141         $this->db->prepare(
142             'UPDATE subscriptions'
143             . ' SET sub_status        = "active"'
144             . '   , sub_lease_seconds = :leaseSeconds'
145             . '   , sub_expires       = :expires'
146             . '   , sub_updated       = NOW()'
147             . ' WHERE sub_id = :id'
148         )->execute(
149             [
150                 ':leaseSeconds' => $leaseSeconds,
151                 ':expires' => gmdate('Y-m-d H:i:s', time() + $leaseSeconds),
152                 ':id' => $subId,
153             ]
154         );
155     }
156
157     /**
158      * Mark a subscription as "unsubscribed"
159      *
160      * @param integer $subId Subscription ID
161      *
162      * @return void
163      */
164     public function unsubscribed($subId)
165     {
166         $this->db->prepare(
167             'UPDATE subscriptions'
168             . ' SET sub_status = "unsubscribed"'
169             . '   , sub_updated = NOW()'
170             . ' WHERE sub_id = :id'
171         )->execute([':id' => $subId]);
172     }
173
174     /**
175      * Subscription has been cancelled/denied for some reason
176      *
177      * @param integer $subId  Subscription ID
178      * @param string  $reason Cancellation reason
179      *
180      * @return void
181      */
182     public function denied($subId, $reason)
183     {
184         $this->db->prepare(
185             'UPDATE subscriptions'
186             . ' SET sub_status = "denied"'
187             . '   , sub_statusmessage = :reason'
188             . '   , sub_updated = NOW()'
189             . ' WHERE sub_id = :id'
190         )->execute([':id' => $subId, ':reason' => $reason]);
191     }
192
193     /**
194      * Topic update notification has been received
195      *
196      * @param integer $subId  Subscription ID
197      *
198      * @return void
199      */
200     public function pinged($subId)
201     {
202         $this->db->prepare(
203             'UPDATE subscriptions'
204             . ' SET sub_pings    = sub_pings + 1'
205             . '   , sub_lastping = NOW()'
206             . '   , sub_updated  = NOW()'
207             . ' WHERE sub_id = :id'
208         )->execute([':id' => $subId]);
209     }
210
211     /**
212      * Detect the hub for the given topic URL
213      *
214      * @param string $url Topic URL
215      *
216      * @return array Topic URL and hub URL. Hub URL is NULL if there is none.
217      */
218     public function detectHub($url)
219     {
220         $hue = new HubUrlExtractor();
221         $hue->setRequestTemplate(new HttpRequest());
222         $urls = $hue->getUrls($url);
223         //we violate the spec by not requiring a self URL
224         $topicUrl = isset($urls['self']) ? $urls['self'] : $url;
225         $hubUrl   = isset($urls['hub'])  ? $urls['hub'] : null;
226
227         return array($topicUrl, $hubUrl);
228     }
229 }
230 ?>