0812f0fe11c9f85e50c7f168ff80b1db64ed6c35
[stapibas.git] / src / stapibas / Feed / PingUrls.php
1 <?php
2 namespace stapibas;
3
4 /**
5  * Pings all URLs that have not been pinged yet
6  */
7 class Feed_PingUrls
8 {
9     public $db;
10     public $log;
11     public $pbc;
12
13     public function __construct(Dependencies $deps)
14     {
15         $this->deps = $deps;
16         $this->db   = $deps->db;
17         $this->log  = $deps->log;
18
19         $this->pbc = new \PEAR2\Services\Linkback\Client();
20
21         $req = new \HTTP_Request2();
22         $req->setConfig(
23             array(
24                 'ssl_verify_peer' => false,
25                 'ssl_verify_host' => false
26             )
27         );
28         $this->pbc->setRequest($req);
29         $this->pbc->setDebug(true);
30     }
31
32     public function pingAll()
33     {
34         $this->log->info('Pinging all URLs..');
35         $res = $this->db->query(
36             'SELECT fe_url, feu_id, feu_url, feu_tries'
37             . ' FROM feedentries, feedentryurls'
38             . ' WHERE fe_id = feu_fe_id' . $this->sqlNeedsUpdate()
39         );
40         $items = 0;
41         while ($row = $res->fetch(\PDO::FETCH_OBJ)) {
42             $this->log->info(
43                 'Pinging URL #%d: %s', $row->feu_id, $row->feu_url
44             );
45             $this->ping($row);
46             ++$items;
47         }
48         $this->log->info('Finished pinging %d URLs.', $items);
49     }
50
51     public function pingSome($urlOrIds)
52     {
53         $options = array();
54         foreach ($urlOrIds as $urlOrId) {
55             if (is_numeric($urlOrId)) {
56                 $options[] = 'feu_id = ' . intval($urlOrId);
57             } else {
58                 $options[] = 'feu_url = ' . $this->db->quote($urlOrId);
59             }
60         }
61
62         $this->log->info('Pinging %d URLs..', count($options));
63         $res = $this->db->query(
64             'SELECT fe_url, feu_id, feu_url, feu_tries'
65             . ' FROM feedentries, feedentryurls'
66             . ' WHERE fe_id = feu_fe_id'
67             . $this->sqlNeedsUpdate()
68             . ' AND (' . implode(' OR ', $options) . ')'
69         );
70         $items = 0;
71         while ($row = $res->fetch(\PDO::FETCH_OBJ)) {
72             $this->log->info(
73                 'Pinging URL #%d: %s', $row->feu_id, $row->feu_url
74             );
75             $this->ping($row);
76             ++$items;
77         }
78         $this->log->info('Finished pinging %d URLs.', $items);
79     }
80
81     public function ping($row)
82     {
83         $from = $row->fe_url;
84         $to   = $row->feu_url;
85
86         try {
87             $res = $this->pbc->send($from, $to);
88         } catch (\Exception $e) {
89             $this->log->err('Exception: ' . $e->getMessage());
90             $this->db->exec(
91                 'UPDATE feedentryurls SET'
92                 . '  feu_pinged = 1'
93                 . ', feu_updated = NOW()'
94                 . ', feu_error = 1'
95                 . ', feu_error_code = ' . $this->db->quote($e->getCode())
96                 . ', feu_error_message = ' . $this->db->quote($e->getMessage())
97                 . ', feu_tries = ' . $this->db->quote($row->feu_tries + 1)
98                 . ', feu_retry = ' . $this->sqlRetry($e)
99                 . ' WHERE feu_id = ' . $this->db->quote($row->feu_id)
100             );
101             return;
102         }
103
104         if (!$res->isError()) {
105             //all fine
106             $this->log->info('ok');
107             $this->db->exec(
108                 'UPDATE feedentryurls SET'
109                 . '  feu_pinged = 1'
110                 . ', feu_updated = NOW()'
111                 . ', feu_error = 0'
112                 . ', feu_error_code = ""'
113                 . ', feu_error_message = ""'
114                 . ', feu_tries = ' . $this->db->quote($row->feu_tries + 1)
115                 . ', feu_retry = 0'
116                 . ' WHERE feu_id = ' . $this->db->quote($row->feu_id)
117             );
118         } else {
119             //error
120             $code = $res->getCode();
121             $this->log->err('Error: ' . $code . ': ' . $res->getMessage());
122             $httpRes = $res->getResponse();
123             if ($httpRes) {
124                 $this->log->info(
125                     'Pingback response: Status code ' . $httpRes->getStatus()
126                     . ', headers: ' . print_r($httpRes->getHeader(), true)
127                 );
128                 if ($code == 100 || $code == 101 || $code == -32600) {
129                     $this->log->info('HTTP body: ' . $httpRes->getBody());
130                 }
131             }
132             $this->db->exec(
133                 'UPDATE feedentryurls SET'
134                 . '  feu_pinged = 1'
135                 . ', feu_updated = NOW()'
136                 . ', feu_error = 1'
137                 . ', feu_error_code = ' . $this->db->quote($res->getCode())
138                 . ', feu_error_message = ' . $this->db->quote($res->getMessage())
139                 . ', feu_tries = ' . $this->db->quote($row->feu_tries + 1)
140                 . ', feu_retry = ' . $this->sqlRetry($res)
141                 . ' WHERE feu_id = ' . $this->db->quote($row->feu_id)
142             );
143         }
144     }
145
146     protected function sqlNeedsUpdate()
147     {
148         if ($this->deps->options['force']) {
149             return '';
150         }
151         $sqlRetry = '(feu_retry = 1 AND feu_tries < 5)';
152         //FIXME: wait at least 1 hour before retrying
153
154         return ' AND feu_active = 1 AND (feu_pinged = 0 OR ' . $sqlRetry . ')';
155     }
156
157     /**
158      * Determines if it should be retried to pingback the URL after some time
159      *
160      * @param $obj mixed Exception or Pingback response
161      */
162     protected function sqlRetry($obj)
163     {
164         if ($obj instanceof \Exception) {
165             return '1';
166         }
167
168         switch ($obj->getCode()) {
169         case -32601:  //they have xmp-rpc, but do not support pingback
170         case 17:  //they think we don't link to them
171         case 18:  //they think we send out pingback spam
172         case 48:  //already registered
173         case 49:  //access denied
174         case 200: //pingback not supported
175         case 201: //Unvalid target URI
176             return '0';
177         }
178
179         return '1';
180     }
181 }
182 ?>