]> git.wh0rd.org Git - tt-rss.git/blob - classes/rpc.php
rpc: switch to PDO
[tt-rss.git] / classes / rpc.php
1 <?php
2 class RPC extends Handler_Protected {
3
4         function csrf_ignore($method) {
5                 $csrf_ignored = array("sanitycheck", "completelabels", "saveprofile");
6
7                 return array_search($method, $csrf_ignored) !== false;
8         }
9
10         function setprofile() {
11                 $_SESSION["profile"] = $_REQUEST["id"];
12         }
13
14         function remprofiles() {
15                 $ids = explode(",", trim($_REQUEST["ids"]));
16
17                 foreach ($ids as $id) {
18                         if ($_SESSION["profile"] != $id) {
19                                 $sth = $this->pdo->prepare("DELETE FROM ttrss_settings_profiles WHERE id = ? AND
20                                                         owner_uid = ?");
21                                 $sth->execute([$id, $_SESSION['uid']]);
22                         }
23                 }
24         }
25
26         // Silent
27         function addprofile() {
28                 $title = trim($_REQUEST["title"]);
29
30                 if ($title) {
31                         $this->pdo->beginTransaction();
32
33                         $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles
34                                 WHERE title = ? AND owner_uid = ?");
35                         $sth->execute([$title, $_SESSION['uid']]);
36
37                         if (!$sth->fetch()) {
38
39                                 $sth = $this->pdo->prepare("INSERT INTO ttrss_settings_profiles (title, owner_uid)
40                                                         VALUES (?, ?)");
41
42                                 $sth->execute([$title, $_SESSION['uid']]);
43
44                                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles WHERE
45                                         title = ? AND owner_uid = ?");
46                                 $sth->execute([$title, $_SESSION['uid']]);
47
48                                 if ($row = $sth->fetch()) {
49                                         $profile_id = $row['id'];
50
51                                         if ($profile_id) {
52                                                 initialize_user_prefs($_SESSION["uid"], $profile_id);
53                                         }
54                                 }
55                         }
56
57                         $this->pdo->commit();
58                 }
59         }
60
61         function saveprofile() {
62                 $id = $_REQUEST["id"];
63                 $title = trim($_REQUEST["value"]);
64
65                 if ($id == 0) {
66                         print __("Default profile");
67                         return;
68                 }
69
70                 if ($title) {
71                         $sth = $this->pdo->prepare("UPDATE ttrss_settings_profiles
72                                 SET title = ? WHERE id = ? AND
73                                         owner_uid = ?");
74
75                         $sth->execute([$title, $id, $_SESSION['uid']]);
76                         print $title;
77                 }
78         }
79
80         // Silent
81         function remarchive() {
82                 $ids = explode(",", $_REQUEST["ids"]);
83
84                 $sth = $this->pdo->prepare("DELETE FROM ttrss_archived_feeds WHERE
85                                 (SELECT COUNT(*) FROM ttrss_user_entries
86                                         WHERE orig_feed_id = :id) = 0 AND
87                                                 id = :id AND owner_uid = :uid");
88
89                 foreach ($ids as $id) {
90                         $sth->execute([":id" => $id, ":uid" => $_SESSION['uid']]);
91                 }
92         }
93
94         function addfeed() {
95                 $feed = $_REQUEST['feed'];
96                 $cat = $_REQUEST['cat'];
97                 $login = $_REQUEST['login'];
98                 $pass = trim($_REQUEST['pass']);
99
100                 $rc = Feeds::subscribe_to_feed($feed, $cat, $login, $pass);
101
102                 print json_encode(array("result" => $rc));
103         }
104
105         function togglepref() {
106                 $key = $_REQUEST["key"];
107                 set_pref($key, !get_pref($key));
108                 $value = get_pref($key);
109
110                 print json_encode(array("param" =>$key, "value" => $value));
111         }
112
113         function setpref() {
114                 // set_pref escapes input, so no need to double escape it here
115                 $key = $_REQUEST['key'];
116                 $value = str_replace("\n", "<br/>", $_REQUEST['value']);
117
118                 set_pref($key, $value, false, $key != 'USER_STYLESHEET');
119
120                 print json_encode(array("param" =>$key, "value" => $value));
121         }
122
123         function mark() {
124                 $mark = $_REQUEST["mark"];
125                 $id = $_REQUEST["id"];
126
127                 $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET marked = ?,
128                                         last_marked = NOW()
129                                         WHERE ref_id = ? AND owner_uid = ?");
130
131                 $sth->execute([$mark, $id, $_SESSION['uid']]);
132
133                 print json_encode(array("message" => "UPDATE_COUNTERS"));
134         }
135
136         function delete() {
137                 $ids = explode(",", $_REQUEST["ids"]);
138                 $ids_qmarks = arr_qmarks($ids);
139
140                 $sth = $this->pdo->prepare("DELETE FROM ttrss_user_entries
141                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
142                 $sth->execute(array_merge($ids, [$_SESSION['uid']]));
143
144                 Article::purge_orphans();
145
146                 print json_encode(array("message" => "UPDATE_COUNTERS"));
147         }
148
149         function unarchive() {
150                 $ids = explode(",", $_REQUEST["ids"]);
151
152                 foreach ($ids as $id) {
153                         $this->pdo->beginTransaction();
154
155                         $sth = $this->pdo->prepare("SELECT feed_url,site_url,title FROM ttrss_archived_feeds
156                                 WHERE id = (SELECT orig_feed_id FROM ttrss_user_entries WHERE ref_id = :id
157                                 AND owner_uid = :uid) AND owner_uid = :uid");
158                         $sth->execute([":uid" => $_SESSION['uid'], ":id" => $id]);
159
160                         if ($row = $sth->fetch()) {
161                                 $feed_url = $row['feed_url'];
162                                 $site_url = $row['site_url'];
163                                 $title = $row['title'];
164
165                                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ?
166                                         AND owner_uid = ?");
167                                 $sth->execute([$feed_url, $_SESSION['uid']]);
168
169                                 if ($row = $sth->fetch()) {
170                                         $feed_id = $row["id"];
171                                 } else {
172                                         if (!$title) $title = '[Unknown]';
173
174                                         $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
175                                                         (owner_uid,feed_url,site_url,title,cat_id,auth_login,auth_pass,update_method)
176                                                         VALUES (?, ?, ?, ?, NULL, '', '', 0)");
177                                         $sth->execute([$_SESSION['uid'], $feed_url, $site_url, $title]);
178
179                                         $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ?
180                                                 AND owner_uid = ?");
181                                         $sth->execute([$feed_url, $_SESSION['uid']]);
182
183                                         if ($row = $sth->fetch()) {
184                                                 $feed_id = $row['id'];
185                                         }
186                                 }
187
188                                 if ($feed_id) {
189                                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries
190                                                 SET feed_id = ?, orig_feed_id = NULL
191                                                 WHERE ref_id = ? AND owner_uid = ?");
192                                         $sth->execute([$feed_id, $id, $_SESSION['uid']]);
193                                 }
194                         }
195
196                         $this->pdo->commit();
197                 }
198
199                 print json_encode(array("message" => "UPDATE_COUNTERS"));
200         }
201
202         function archive() {
203                 $ids = explode(",", $_REQUEST["ids"]);
204
205                 foreach ($ids as $id) {
206                         $this->archive_article($id, $_SESSION["uid"]);
207                 }
208
209                 print json_encode(array("message" => "UPDATE_COUNTERS"));
210         }
211
212         private function archive_article($id, $owner_uid) {
213                 $this->pdo->beginTransaction();
214
215                 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
216
217                 $sth = $this->pdo->prepare("SELECT feed_id FROM ttrss_user_entries
218                         WHERE ref_id = ? AND owner_uid = ?");
219                 $sth->execute([$id, $owner_uid]);
220
221                 if ($row = $sth->fetch()) {
222
223                         /* prepare the archived table */
224
225                         $feed_id = (int) $row['feed_id'];
226
227                         if ($feed_id) {
228                                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_archived_feeds
229                                         WHERE id = ? AND owner_uid = ?");
230                                 $sth->execute([$feed_id, $owner_uid]);
231
232                                 if ($row = $sth->fetch()) {
233                                         $new_feed_id = $row['id'];
234                                 } else {
235                                         $row = $this->pdo->query("SELECT MAX(id) AS id FROM ttrss_archived_feeds")->fetch();
236                                         $new_feed_id = (int)$row['id'] + 1;
237
238                                         $sth = $this->pdo->prepare("INSERT INTO ttrss_archived_feeds
239                                                 (id, owner_uid, title, feed_url, site_url)
240                                                         SELECT ?, owner_uid, title, feed_url, site_url from ttrss_feeds
241                                                                 WHERE id = ?");
242
243                                         $sth->execute([$new_feed_id, $feed_id]);
244                                 }
245
246                                 $sth = $this->pdo->prepare("UPDATE ttrss_user_entries
247                                         SET orig_feed_id = ?, feed_id = NULL
248                                         WHERE ref_id = ? AND owner_uid = ?");
249                                 $sth->execute([$new_feed_id, $id, $owner_uid]);
250                         }
251                 }
252
253                 $this->pdo->commit();
254         }
255
256         function publ() {
257                 $pub = $_REQUEST["pub"];
258                 $id = $_REQUEST["id"];
259
260                 $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
261                         published = ?, last_published = NOW()
262                         WHERE ref_id = ? AND owner_uid = ?");
263
264                 $sth->execute([$pub, $id, $_SESSION['uid']]);
265
266                 print json_encode(array("message" => "UPDATE_COUNTERS"));
267         }
268
269         function getAllCounters() {
270                 $last_article_id = (int) $_REQUEST["last_article_id"];
271
272                 $reply = array();
273
274                 if (!empty($_REQUEST['seq'])) $reply['seq'] = (int) $_REQUEST['seq'];
275
276                 if ($last_article_id != Article::getLastArticleId()) {
277                         $reply['counters'] = Counters::getAllCounters();
278                 }
279
280                 $reply['runtime-info'] = make_runtime_info();
281
282                 print json_encode($reply);
283         }
284
285         /* GET["cmode"] = 0 - mark as read, 1 - as unread, 2 - toggle */
286         function catchupSelected() {
287                 $ids = explode(",", $_REQUEST["ids"]);
288                 $cmode = sprintf("%d", $_REQUEST["cmode"]);
289
290                 Article::catchupArticlesById($ids, $cmode);
291
292                 print json_encode(array("message" => "UPDATE_COUNTERS", "ids" => $ids));
293         }
294
295         function markSelected() {
296                 $ids = explode(",", $_REQUEST["ids"]);
297                 $cmode = (int)$_REQUEST["cmode"];
298
299                 $this->markArticlesById($ids, $cmode);
300
301                 print json_encode(array("message" => "UPDATE_COUNTERS"));
302         }
303
304         function publishSelected() {
305                 $ids = explode(",", $_REQUEST["ids"]);
306                 $cmode = (int)$_REQUEST["cmode"];
307
308                 $this->publishArticlesById($ids, $cmode);
309
310                 print json_encode(array("message" => "UPDATE_COUNTERS"));
311         }
312
313         function sanityCheck() {
314                 $_SESSION["hasAudio"] = $_REQUEST["hasAudio"] === "true";
315                 $_SESSION["hasSandbox"] = $_REQUEST["hasSandbox"] === "true";
316                 $_SESSION["hasMp3"] = $_REQUEST["hasMp3"] === "true";
317                 $_SESSION["clientTzOffset"] = $_REQUEST["clientTzOffset"];
318
319                 $reply = array();
320
321                 $reply['error'] = sanity_check();
322
323                 if ($reply['error']['code'] == 0) {
324                         $reply['init-params'] = make_init_params();
325                         $reply['runtime-info'] = make_runtime_info(true);
326                 }
327
328                 print json_encode($reply);
329         }
330
331         function completeLabels() {
332                 $search = $_REQUEST["search"];
333
334                 $sth = $this->pdo->query("SELECT DISTINCT caption FROM
335                                 ttrss_labels2
336                                 WHERE owner_uid = ? AND
337                                 LOWER(caption) LIKE LOWER(?) ORDER BY caption
338                                 LIMIT 5");
339                 $sth->execute([$_SESSION['uid'], "%$search%"]);
340
341                 print "<ul>";
342                 while ($line = $sth->fetch()) {
343                         print "<li>" . $line["caption"] . "</li>";
344                 }
345                 print "</ul>";
346         }
347
348         function purge() {
349                 $ids = explode(",", $_REQUEST["ids"]);
350                 $days = (int) $_REQUEST["days"];
351
352                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
353                                 id = ? AND owner_uid = ?");
354
355                 foreach ($ids as $id) {
356                         $sth->execute([$id, $_SESSION['uid']]);
357
358                         if ($sth->fetch()) {
359                                 purge_feed($id, $days);
360                         }
361                 }
362         }
363
364         function updateFeedBrowser() {
365                 if (defined('_DISABLE_FEED_BROWSER') && _DISABLE_FEED_BROWSER) return;
366
367                 $search = $_REQUEST["search"];
368                 $limit = $_REQUEST["limit"];
369                 $mode = (int) $_REQUEST["mode"];
370
371                 require_once "feedbrowser.php";
372
373                 print json_encode(array("content" =>
374                         make_feed_browser($search, $limit, $mode),
375                                 "mode" => $mode));
376         }
377
378         // Silent
379         function massSubscribe() {
380
381                 $payload = json_decode($_REQUEST["payload"], false);
382                 $mode = $_REQUEST["mode"];
383
384                 if (!$payload || !is_array($payload)) return;
385
386                 if ($mode == 1) {
387                         foreach ($payload as $feed) {
388
389                                 $title = $feed[0];
390                                 $feed_url = $feed[1];
391
392                                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
393                                         feed_url = ? AND owner_uid = ?");
394                                 $sth->execute([$feed_url, $_SESSION['uid']]);
395
396                                 if (!$sth->fetch()) {
397                                         $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
398                                                                         (owner_uid,feed_url,title,cat_id,site_url)
399                                                                         VALUES (?, ?, ?, NULL, '')");
400
401                                         $sth->execute([$_SESSION['uid'], $feed_url, $title]);
402                                 }
403                         }
404                 } else if ($mode == 2) {
405                         // feed archive
406                         foreach ($payload as $id) {
407                                 $sth = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds
408                                         WHERE id = ? AND owner_uid = ?");
409                                 $sth->execute([$id, $_SESSION['uid']]);
410
411                                 if ($row = $sth->fetch()) {
412                                         $site_url = $row['site_url'];
413                                         $feed_url = $row['feed_url'];
414                                         $title = $row['title'];
415
416                                         $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
417                                                 feed_url = ? AND owner_uid = ?");
418                                         $sth->execute([$feed_url, $_SESSION['uid']]);
419
420                                         if (!$sth->fetch()) {
421                                                 $sth = $this->pdo->prepare("INSERT INTO ttrss_feeds
422                                                                 (owner_uid,feed_url,title,cat_id,site_url)
423                                                                         VALUES (?, ?, ?, NULL, ?)");
424
425                                                 $sth->execute([$_SESSION['uid'], $feed_url, $title, $site_url]);
426                                         }
427                                 }
428                         }
429                 }
430         }
431
432         function catchupFeed() {
433                 $feed_id = $_REQUEST['feed_id'];
434                 $is_cat = $_REQUEST['is_cat'] == "true";
435                 $mode = $_REQUEST['mode'];
436                 $search_query = $_REQUEST['search_query'];
437                 $search_lang = $_REQUEST['search_lang'];
438
439                 Feeds::catchup_feed($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]);
440
441                 print json_encode(array("message" => "UPDATE_COUNTERS"));
442         }
443
444         function setpanelmode() {
445                 $wide = (int) $_REQUEST["wide"];
446
447                 setcookie("ttrss_widescreen", $wide,
448                         time() + COOKIE_LIFETIME_LONG);
449
450                 print json_encode(array("wide" => $wide));
451         }
452
453         static function updaterandomfeed_real() {
454
455                 // Test if the feed need a update (update interval exceded).
456                 if (DB_TYPE == "pgsql") {
457                         $update_limit_qpart = "AND ((
458                                         ttrss_feeds.update_interval = 0
459                                         AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
460                                 ) OR (
461                                         ttrss_feeds.update_interval > 0
462                                         AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)
463                                 ) OR ttrss_feeds.last_updated IS NULL
464                                 OR last_updated = '1970-01-01 00:00:00')";
465                 } else {
466                         $update_limit_qpart = "AND ((
467                                         ttrss_feeds.update_interval = 0
468                                         AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)
469                                 ) OR (
470                                         ttrss_feeds.update_interval > 0
471                                         AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)
472                                 ) OR ttrss_feeds.last_updated IS NULL
473                                 OR last_updated = '1970-01-01 00:00:00')";
474                 }
475
476                 // Test if feed is currently being updated by another process.
477                 if (DB_TYPE == "pgsql") {
478                         $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')";
479                 } else {
480                         $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))";
481                 }
482
483                 $random_qpart = sql_random_function();
484
485                 $pdo = Db::pdo();
486
487                 // we could be invoked from public.php with no active session
488                 if ($_SESSION["uid"]) {
489                         $owner_check_qpart = "AND ttrss_feeds.owner_uid = ".$pdo->quote($_SESSION["uid"]);
490                 } else {
491                         $owner_check_qpart = "";
492                 }
493
494                 // We search for feed needing update.
495                 $res = $pdo->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id
496                         FROM
497                                 ttrss_feeds, ttrss_users, ttrss_user_prefs
498                         WHERE
499                                 ttrss_feeds.owner_uid = ttrss_users.id
500                                 AND ttrss_users.id = ttrss_user_prefs.owner_uid
501                                 AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'
502                                 $owner_check_qpart
503                                 $update_limit_qpart
504                                 $updstart_thresh_qpart
505                         ORDER BY $random_qpart LIMIT 30");
506
507                 $num_updated = 0;
508
509                 $tstart = time();
510
511                 while ($line = $res->fetch()) {
512                         $feed_id = $line["id"];
513
514                         if (time() - $tstart < ini_get("max_execution_time") * 0.7) {
515                                 RSSUtils::update_rss_feed($feed_id, true);
516                                 ++$num_updated;
517                         } else {
518                                 break;
519                         }
520                 }
521
522                 // Purge orphans and cleanup tags
523                 Article::purge_orphans();
524                 //cleanup_tags(14, 50000);
525
526                 if ($num_updated > 0) {
527                         print json_encode(array("message" => "UPDATE_COUNTERS",
528                                 "num_updated" => $num_updated));
529                 } else {
530                         print json_encode(array("message" => "NOTHING_TO_UPDATE"));
531                 }
532
533         }
534
535         function updaterandomfeed() {
536                 RPC::updaterandomfeed_real();
537         }
538
539         private function markArticlesById($ids, $cmode) {
540
541                 $ids_qmarks = arr_qmarks($ids);
542
543                 if ($cmode == 0) {
544                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
545                                 marked = false, last_marked = NOW()
546                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
547                 } else if ($cmode == 1) {
548                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
549                                 marked = true, last_marked = NOW()
550                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
551                 } else {
552                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
553                                 marked = NOT marked,last_marked = NOW()
554                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
555                 }
556
557                 $sth->execute(array_merge($ids, [$_SESSION['uid']]));
558         }
559
560         private function publishArticlesById($ids, $cmode) {
561
562                 $ids_qmarks = arr_qmarks($ids);
563
564                 if ($cmode == 0) {
565                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
566                                 published = false, last_published = NOW()
567                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
568                 } else if ($cmode == 1) {
569                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
570                                 published = true, last_published = NOW()
571                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
572                 } else {
573                         $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
574                                 published = NOT published,last_published = NOW()
575                                         WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
576                 }
577
578                 $sth->execute(array_merge($ids, [$_SESSION['uid']]));
579         }
580
581         function getlinktitlebyid() {
582                 $id = $_REQUEST['id'];
583
584                 $sth = $this->pdo->prepare("SELECT link, title FROM ttrss_entries, ttrss_user_entries
585                         WHERE ref_id = ? AND ref_id = id AND owner_uid = ?");
586                 $sth->execute([$id, $_SESSION['uid']]);
587
588                 if ($row = $sth->fetch()) {
589                         $link = $row['link'];
590                         $title = $row['title'];
591
592                         echo json_encode(array("link" => $link, "title" => $title));
593                 } else {
594                         echo json_encode(array("error" => "ARTICLE_NOT_FOUND"));
595                 }
596         }
597
598         function log() {
599                 $msg = $_REQUEST['msg'];
600                 $file = basename($_REQUEST['file']);
601                 $line = (int) $_REQUEST['line'];
602                 $context = $_REQUEST['context'];
603
604                 if ($msg) {
605                         Logger::get()->log_error(E_USER_WARNING,
606                                 $msg, 'client-js:' . $file, $line, $context);
607
608                         echo json_encode(array("message" => "HOST_ERROR_LOGGED"));
609                 } else {
610                         echo json_encode(array("error" => "MESSAGE_NOT_FOUND"));
611                 }
612
613         }
614 }