]> git.wh0rd.org Git - tt-rss.git/blob - classes/handler/public.php
9fccf884b45a3ea915855e57193662949f8c7978
[tt-rss.git] / classes / handler / public.php
1 <?php
2 class Handler_Public extends Handler {
3
4         private function generate_syndicated_feed($owner_uid, $feed, $is_cat,
5                 $limit, $offset, $search,
6                 $view_mode = false, $format = 'atom', $order = false, $orig_guid = false, $start_ts = false) {
7
8                 require_once "lib/MiniTemplator.class.php";
9
10                 $note_style =   "background-color : #fff7d5;
11                         border-width : 1px; ".
12                         "padding : 5px; border-style : dashed; border-color : #e7d796;".
13                         "margin-bottom : 1em; color : #9a8c59;";
14
15                 if (!$limit) $limit = 60;
16
17                 $date_sort_field = "date_entered DESC, updated DESC";
18
19                 if ($feed == -2 && !$is_cat) {
20                         $date_sort_field = "last_published DESC";
21                 } else if ($feed == -1 && !$is_cat) {
22                         $date_sort_field = "last_marked DESC";
23                 }
24
25                 switch ($order) {
26                 case "title":
27                         $date_sort_field = "ttrss_entries.title, date_entered, updated";
28                         break;
29                 case "date_reverse":
30                         $date_sort_field = "date_entered, updated";
31                         break;
32                 case "feed_dates":
33                         $date_sort_field = "updated DESC";
34                         break;
35                 }
36                 $params = array(
37                         "owner_uid" => $owner_uid,
38                         "feed" => $feed,
39                         "limit" => $limit,
40                         "view_mode" => $view_mode,
41                         "cat_view" => $is_cat,
42                         "search" => $search,
43                         "override_order" => $date_sort_field,
44                         "include_children" => true,
45                         "ignore_vfeed_group" => true,
46                         "offset" => $offset,
47                         "start_ts" => $start_ts
48                 );
49
50                 $qfh_ret = Feeds::queryFeedHeadlines($params);
51
52                 $result = $qfh_ret[0];
53                 $feed_title = htmlspecialchars($qfh_ret[1]);
54                 $feed_site_url = $qfh_ret[2];
55                 /* $last_error = $qfh_ret[3]; */
56
57                 $feed_self_url = get_self_url_prefix() .
58                         "/public.php?op=rss&id=$feed&key=" .
59                         get_feed_access_key($feed, false, $owner_uid);
60
61                 if (!$feed_site_url) $feed_site_url = get_self_url_prefix();
62
63                 if ($format == 'atom') {
64                         $tpl = new MiniTemplator;
65
66                         $tpl->readTemplateFromFile("templates/generated_feed.txt");
67
68                         $tpl->setVariable('FEED_TITLE', $feed_title, true);
69                         $tpl->setVariable('VERSION', VERSION, true);
70                         $tpl->setVariable('FEED_URL', htmlspecialchars($feed_self_url), true);
71
72                         $tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()), true);
73                         while ($line = $result->fetch()) {
74
75                                 $line["content_preview"] = sanitize(truncate_string(strip_tags($line["content"]), 100, '...'));
76
77                                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
78                                         $line = $p->hook_query_headlines($line);
79                                 }
80
81                                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_EXPORT_FEED) as $p) {
82                                         $line = $p->hook_article_export_feed($line, $feed, $is_cat);
83                                 }
84
85                                 $tpl->setVariable('ARTICLE_ID',
86                                         htmlspecialchars($orig_guid ? $line['link'] :
87                                                         $this->make_article_tag_uri($line['id'], $line['date_entered'])), true);
88                                 $tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link']), true);
89                                 $tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title']), true);
90                                 $tpl->setVariable('ARTICLE_EXCERPT', $line["content_preview"], true);
91
92                                 $content = sanitize($line["content"], false, $owner_uid,
93                                         $feed_site_url, false, $line["id"]);
94
95                                 if ($line['note']) {
96                                         $content = "<div style=\"$note_style\">Article note: " . $line['note'] . "</div>" .
97                                                 $content;
98                                         $tpl->setVariable('ARTICLE_NOTE', htmlspecialchars($line['note']), true);
99                                 }
100
101                                 $tpl->setVariable('ARTICLE_CONTENT', $content, true);
102
103                                 $tpl->setVariable('ARTICLE_UPDATED_ATOM',
104                                         date('c', strtotime($line["updated"])), true);
105                                 $tpl->setVariable('ARTICLE_UPDATED_RFC822',
106                                         date(DATE_RFC822, strtotime($line["updated"])), true);
107
108                                 $tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author']), true);
109
110                                 $tpl->setVariable('ARTICLE_SOURCE_LINK', htmlspecialchars($line['site_url'] ? $line["site_url"] : get_self_url_prefix()), true);
111                                 $tpl->setVariable('ARTICLE_SOURCE_TITLE', htmlspecialchars($line['feed_title'] ? $line['feed_title'] : $feed_title), true);
112
113                                 $tags = Article::get_article_tags($line["id"], $owner_uid);
114
115                                 foreach ($tags as $tag) {
116                                         $tpl->setVariable('ARTICLE_CATEGORY', htmlspecialchars($tag), true);
117                                         $tpl->addBlock('category');
118                                 }
119
120                                 $enclosures = Article::get_article_enclosures($line["id"]);
121
122                                 foreach ($enclosures as $e) {
123                                         $type = htmlspecialchars($e['content_type']);
124                                         $url = htmlspecialchars($e['content_url']);
125                                         $length = $e['duration'] ? $e['duration'] : 1;
126
127                                         $tpl->setVariable('ARTICLE_ENCLOSURE_URL', $url, true);
128                                         $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', $type, true);
129                                         $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', $length, true);
130
131                                         $tpl->addBlock('enclosure');
132                                 }
133
134                                 $tpl->addBlock('entry');
135                         }
136
137                         $tmp = "";
138
139                         $tpl->addBlock('feed');
140                         $tpl->generateOutputToString($tmp);
141
142                         if (@!$_REQUEST["noxml"]) {
143                                 header("Content-Type: text/xml; charset=utf-8");
144                         } else {
145                                 header("Content-Type: text/plain; charset=utf-8");
146                         }
147
148                         print $tmp;
149                 } else if ($format == 'json') {
150
151                         $feed = array();
152
153                         $feed['title'] = $feed_title;
154                         $feed['version'] = VERSION;
155                         $feed['feed_url'] = $feed_self_url;
156
157                         $feed['self_url'] = get_self_url_prefix();
158
159                         $feed['articles'] = array();
160
161                         while ($line = $result->fetch()) {
162
163                                 $line["content_preview"] = sanitize(truncate_string(strip_tags($line["content_preview"]), 100, '...'));
164
165                                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
166                                         $line = $p->hook_query_headlines($line, 100);
167                                 }
168
169                                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_EXPORT_FEED) as $p) {
170                                         $line = $p->hook_article_export_feed($line, $feed, $is_cat);
171                                 }
172
173                                 $article = array();
174
175                                 $article['id'] = $line['link'];
176                                 $article['link']        = $line['link'];
177                                 $article['title'] = $line['title'];
178                                 $article['excerpt'] = $line["content_preview"];
179                                 $article['content'] = sanitize($line["content"], false, $owner_uid, $feed_site_url, false, $line["id"]);
180                                 $article['updated'] = date('c', strtotime($line["updated"]));
181
182                                 if ($line['note']) $article['note'] = $line['note'];
183                                 if ($article['author']) $article['author'] = $line['author'];
184
185                                 $tags = Article::get_article_tags($line["id"], $owner_uid);
186
187                                 if (count($tags) > 0) {
188                                         $article['tags'] = array();
189
190                                         foreach ($tags as $tag) {
191                                                 array_push($article['tags'], $tag);
192                                         }
193                                 }
194
195                                 $enclosures = Article::get_article_enclosures($line["id"]);
196
197                                 if (count($enclosures) > 0) {
198                                         $article['enclosures'] = array();
199
200                                         foreach ($enclosures as $e) {
201                                                 $type = $e['content_type'];
202                                                 $url = $e['content_url'];
203                                                 $length = $e['duration'];
204
205                                                 array_push($article['enclosures'], array("url" => $url, "type" => $type, "length" => $length));
206                                         }
207                                 }
208
209                                 array_push($feed['articles'], $article);
210                         }
211
212                         header("Content-Type: text/json; charset=utf-8");
213                         print json_encode($feed);
214
215                 } else {
216                         header("Content-Type: text/plain; charset=utf-8");
217                         print json_encode(array("error" => array("message" => "Unknown format")));
218                 }
219         }
220
221         function getUnread() {
222                 $login = $_REQUEST["login"];
223                 $fresh = $_REQUEST["fresh"] == "1";
224
225                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_users WHERE login = ?");
226                 $sth->execute([$login]);
227
228                 if ($row = $sth->fetch()) {
229                         $uid = $row["id"];
230
231                         print Feeds::getGlobalUnread($uid);
232
233                         if ($fresh) {
234                                 print ";";
235                                 print Feeds::getFeedArticles(-3, false, true, $uid);
236                         }
237
238                 } else {
239                         print "-1;User not found";
240                 }
241         }
242
243         function getProfiles() {
244                 $login = $_REQUEST["login"];
245
246                 $sth = $this->pdo->prepare("SELECT ttrss_settings_profiles.* FROM ttrss_settings_profiles,ttrss_users
247                         WHERE ttrss_users.id = ttrss_settings_profiles.owner_uid AND login = ? ORDER BY title");
248                 $sth->execute([$login]);
249
250                 print "<select dojoType='dijit.form.Select' style='width : 220px; margin : 0px' name='profile'>";
251
252                 print "<option value='0'>" . __("Default profile") . "</option>";
253
254                 while ($line = $sth->fetch()) {
255                         $id = $line["id"];
256                         $title = $line["title"];
257
258                         print "<option value='$id'>$title</option>";
259                 }
260
261                 print "</select>";
262         }
263
264         function logout() {
265                 logout_user();
266                 header("Location: index.php");
267         }
268
269         function share() {
270                 $uuid = $_REQUEST["key"];
271
272                 $sth = $this->pdo->prepare("SELECT ref_id, owner_uid FROM ttrss_user_entries WHERE
273                         uuid = ?");
274                 $sth->execute([$uuid]);
275
276                 if ($row = $sth->fetch()) {
277                         header("Content-Type: text/html");
278
279                         $id = $row["ref_id"];
280                         $owner_uid = $row["owner_uid"];
281
282                         $article = Article::format_article($id, false, true, $owner_uid);
283
284                         print_r($article['content']);
285
286                 } else {
287                         print "Article not found.";
288                 }
289
290         }
291
292         function rss() {
293                 $feed = $_REQUEST["id"];
294                 $key = $_REQUEST["key"];
295                 $is_cat = $_REQUEST["is_cat"];
296                 $limit = (int)$_REQUEST["limit"];
297                 $offset = (int)$_REQUEST["offset"];
298
299                 $search = $_REQUEST["q"];
300                 $view_mode = $_REQUEST["view-mode"];
301                 $order = $_REQUEST["order"];
302                 $start_ts = $_REQUEST["ts"];
303
304                 $format = $_REQUEST['format'];
305                 $orig_guid = $_REQUEST["orig_guid"];
306
307                 if (!$format) $format = 'atom';
308
309                 if (SINGLE_USER_MODE) {
310                         authenticate_user("admin", null);
311                 }
312
313                 $owner_id = false;
314
315                 if ($key) {
316                         $sth = $this->pdo->prepare("SELECT owner_uid FROM
317                                 ttrss_access_keys WHERE access_key = ? AND feed_id = ?");
318                         $sth->execute([$key, $feed]);
319
320                         if ($row = $sth->fetch())
321                                 $owner_id = $row["owner_uid"];
322                 }
323
324                 if ($owner_id) {
325                         $this->generate_syndicated_feed($owner_id, $feed, $is_cat, $limit,
326                                 $offset, $search, $view_mode, $format, $order, $orig_guid, $start_ts);
327                 } else {
328                         header('HTTP/1.1 403 Forbidden');
329                 }
330         }
331
332         function updateTask() {
333                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", false);
334         }
335
336         function housekeepingTask() {
337                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING, "hook_house_keeping", false);
338         }
339
340         function globalUpdateFeeds() {
341                 RPC::updaterandomfeed_real($this->dbh);
342
343                 PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", false);
344         }
345
346         function sharepopup() {
347                 if (SINGLE_USER_MODE) {
348                         login_sequence();
349                 }
350
351                 header('Content-Type: text/html; charset=utf-8');
352                 print "<html><head><title>Tiny Tiny RSS</title>
353                 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
354                 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">";
355
356                 echo stylesheet_tag("css/utility.css");
357                 echo stylesheet_tag("css/default.css");
358                 echo javascript_tag("lib/prototype.js");
359                 echo javascript_tag("lib/scriptaculous/scriptaculous.js?load=effects,controls");
360                 print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
361                         </head><body id='sharepopup'>";
362
363                 $action = $_REQUEST["action"];
364
365                 if ($_SESSION["uid"]) {
366
367                         if ($action == 'share') {
368
369                                 $title = strip_tags($_REQUEST["title"]);
370                                 $url = strip_tags($_REQUEST["url"]);
371                                 $content = strip_tags($_REQUEST["content"]);
372                                 $labels = strip_tags($_REQUEST["labels"]);
373
374                                 Article::create_published_article($title, $url, $content, $labels,
375                                         $_SESSION["uid"]);
376
377                                 print "<script type='text/javascript'>";
378                                 print "window.close();";
379                                 print "</script>";
380
381                         } else {
382                                 $title = htmlspecialchars($_REQUEST["title"]);
383                                 $url = htmlspecialchars($_REQUEST["url"]);
384
385                                 ?>
386
387                                 <table height='100%' width='100%'><tr><td colspan='2'>
388                                 <h1><?php echo __("Share with Tiny Tiny RSS") ?></h1>
389                                 </td></tr>
390
391                                 <form id='share_form' name='share_form'>
392
393                                 <input type="hidden" name="op" value="sharepopup">
394                                 <input type="hidden" name="action" value="share">
395
396                                 <tr><td align='right'><?php echo __("Title:") ?></td>
397                                 <td width='80%'><input name='title' value="<?php echo $title ?>"></td></tr>
398                                 <tr><td align='right'><?php echo __("URL:") ?></td>
399                                 <td><input name='url' value="<?php echo $url ?>"></td></tr>
400                                 <tr><td align='right'><?php echo __("Content:") ?></td>
401                                 <td><input name='content' value=""></td></tr>
402                                 <tr><td align='right'><?php echo __("Labels:") ?></td>
403                                 <td><input name='labels' id="labels_value"
404                                         placeholder='Alpha, Beta, Gamma' value="">
405                                 </td></tr>
406
407                                 <tr><td>
408                                         <div class="autocomplete" id="labels_choices"
409                                                 style="display : block"></div></td></tr>
410
411                                 <script type='text/javascript'>document.forms[0].title.focus();</script>
412
413                                 <script type='text/javascript'>
414                                         new Ajax.Autocompleter('labels_value', 'labels_choices',
415                                    "backend.php?op=rpc&method=completeLabels",
416                                    { tokens: ',', paramName: "search" });
417                                 </script>
418
419                                 <tr><td colspan='2'>
420                                         <div style='float : right' class='insensitive-small'>
421                                         <?php echo __("Shared article will appear in the Published feed.") ?>
422                                         </div>
423                                         <button type="submit"><?php echo __('Share') ?></button>
424                                         <button onclick="return window.close()"><?php echo __('Cancel') ?></button>
425                                         </td>
426
427                                 </form>
428                                 </td></tr></table>
429                                 </body></html>
430                                 <?php
431
432                         }
433
434                 } else {
435
436                         $return = urlencode($_SERVER["REQUEST_URI"])
437                         ?>
438
439                         <form action="public.php?return=<?php echo $return ?>"
440                                 method="POST" id="loginForm" name="loginForm">
441
442                         <input type="hidden" name="op" value="login">
443
444                         <table height='100%' width='100%'><tr><td colspan='2'>
445                         <h1><?php echo __("Not logged in") ?></h1></td></tr>
446
447                         <tr><td align="right"><?php echo __("Login:") ?></td>
448                         <td align="right"><input name="login"
449                                 value="<?php echo $_SESSION["fake_login"] ?>"></td></tr>
450                                 <tr><td align="right"><?php echo __("Password:") ?></td>
451                                 <td align="right"><input type="password" name="password"
452                                 value="<?php echo $_SESSION["fake_password"] ?>"></td></tr>
453                         <tr><td colspan='2'>
454                                 <button type="submit">
455                                         <?php echo __('Log in') ?></button>
456
457                                 <button onclick="return window.close()">
458                                         <?php echo __('Cancel') ?></button>
459                         </td></tr>
460                         </table>
461
462                         </form>
463                         <?php
464                 }
465         }
466
467         function login() {
468                 if (!SINGLE_USER_MODE) {
469
470                         $login = $_POST["login"];
471                         $password = $_POST["password"];
472                         $remember_me = $_POST["remember_me"];
473
474                         if ($remember_me) {
475                                 session_set_cookie_params(SESSION_COOKIE_LIFETIME);
476                         } else {
477                                 session_set_cookie_params(0);
478                         }
479
480                         @session_start();
481
482                         if (authenticate_user($login, $password)) {
483                                 $_POST["password"] = "";
484
485                                 if (get_schema_version() >= 120) {
486                                         $_SESSION["language"] = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
487                                 }
488
489                                 $_SESSION["ref_schema_version"] = get_schema_version(true);
490                                 $_SESSION["bw_limit"] = !!$_POST["bw_limit"];
491
492                                 if ($_POST["profile"]) {
493
494                                         $profile = $_POST["profile"];
495
496                                         $sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles
497                                                 WHERE id = ? AND owner_uid = ?");
498                                         $sth->execute([$profile, $_SESSION['uid']]);
499
500                                         if ($sth->fetch()) {
501                                                 $_SESSION["profile"] = $profile;
502                                         }
503                                 }
504                         } else {
505                                 $_SESSION["login_error_msg"] = __("Incorrect username or password");
506                                 user_error("Failed login attempt for $login from {$_SERVER['REMOTE_ADDR']}", E_USER_WARNING);
507                         }
508
509                         if ($_REQUEST['return']) {
510                                 header("Location: " . $_REQUEST['return']);
511                         } else {
512                                 header("Location: " . get_self_url_prefix());
513                         }
514                 }
515         }
516
517         /* function subtest() {
518                 header("Content-type: text/plain; charset=utf-8");
519
520                 $url = $_REQUEST["url"];
521
522                 print "$url\n\n";
523
524
525                 print_r(get_feeds_from_html($url, fetch_file_contents($url)));
526
527         } */
528
529         function subscribe() {
530                 if (SINGLE_USER_MODE) {
531                         login_sequence();
532                 }
533
534                 if ($_SESSION["uid"]) {
535
536                         $feed_url = trim($_REQUEST["feed_url"]);
537
538                         header('Content-Type: text/html; charset=utf-8');
539                         print "<html>
540                                 <head>
541                                         <title>Tiny Tiny RSS</title>";
542             print stylesheet_tag("css/utility.css");
543                         print stylesheet_tag("css/default.css");
544
545             print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
546                 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
547                 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
548
549                                 </head>
550                                 <body class='claro'>
551                                 <img class=\"floatingLogo\" src=\"images/logo_small.png\"
552                                         alt=\"Tiny Tiny RSS\"/>
553                                         <h1>".__("Subscribe to feed...")."</h1><div class='content'>";
554
555                         $rc = Feeds::subscribe_to_feed($feed_url);
556
557                         switch ($rc['code']) {
558                         case 0:
559                                 print_warning(T_sprintf("Already subscribed to <b>%s</b>.", $feed_url));
560                                 break;
561                         case 1:
562                                 print_notice(T_sprintf("Subscribed to <b>%s</b>.", $feed_url));
563                                 break;
564                         case 2:
565                                 print_error(T_sprintf("Could not subscribe to <b>%s</b>.", $feed_url));
566                                 break;
567                         case 3:
568                                 print_error(T_sprintf("No feeds found in <b>%s</b>.", $feed_url));
569                                 break;
570                         case 4:
571                                 print_notice(__("Multiple feed URLs found."));
572                                 $feed_urls = $rc["feeds"];
573                                 break;
574                         case 5:
575                                 print_error(T_sprintf("Could not subscribe to <b>%s</b>.<br>Can't download the Feed URL.", $feed_url));
576                                 break;
577                         }
578
579                         if ($feed_urls) {
580
581                                 print "<form action=\"public.php\">";
582                                 print "<input type=\"hidden\" name=\"op\" value=\"subscribe\">";
583
584                                 print "<select name=\"feed_url\">";
585
586                                 foreach ($feed_urls as $url => $name) {
587                                         $url = htmlspecialchars($url);
588                                         $name = htmlspecialchars($name);
589
590                                         print "<option value=\"$url\">$name</option>";
591                                 }
592
593                                 print "<input type=\"submit\" value=\"".__("Subscribe to selected feed").
594                                         "\">";
595
596                                 print "</form>";
597                         }
598
599                         $tp_uri = get_self_url_prefix() . "/prefs.php";
600                         $tt_uri = get_self_url_prefix();
601
602                         if ($rc['code'] <= 2){
603                             $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
604                                         feed_url = ? AND owner_uid = ?");
605                             $sth->execute([$feed_url, $_SESSION['uid']]);
606                             $row = $sth->fetch();
607
608                                 $feed_id = $row["id"];
609                         } else {
610                                 $feed_id = 0;
611                         }
612                         print "<p>";
613
614                         if ($feed_id) {
615                                 print "<form method=\"GET\" style='display: inline'
616                                         action=\"$tp_uri\">
617                                         <input type=\"hidden\" name=\"tab\" value=\"feedConfig\">
618                                         <input type=\"hidden\" name=\"method\" value=\"editFeed\">
619                                         <input type=\"hidden\" name=\"methodparam\" value=\"$feed_id\">
620                                         <input type=\"submit\" value=\"".__("Edit subscription options")."\">
621                                         </form>";
622                         }
623
624                         print "<form style='display: inline' method=\"GET\" action=\"$tt_uri\">
625                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
626                                 </form></p>";
627
628                         print "</div></body></html>";
629
630                 } else {
631                         render_login_form();
632                 }
633         }
634
635         function index() {
636                 header("Content-Type: text/plain");
637                 print error_json(13);
638         }
639
640         function forgotpass() {
641                 startup_gettext();
642
643                 @$hash = $_REQUEST["hash"];
644
645                 header('Content-Type: text/html; charset=utf-8');
646                 print "<html><head><title>Tiny Tiny RSS</title>
647                 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
648                 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">";
649
650                 echo stylesheet_tag("css/utility.css");
651                 echo javascript_tag("lib/prototype.js");
652
653                 print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
654                         </head><body id='forgotpass'>";
655
656                 print '<div class="floatingLogo"><img src="images/logo_small.png"></div>';
657                 print "<h1>".__("Password recovery")."</h1>";
658                 print "<div class='content'>";
659
660                 @$method = $_POST['method'];
661
662                 if ($hash) {
663                         $login = $_REQUEST["login"];
664
665                         if ($login) {
666                                 $sth = $this->pdo->prepare("SELECT id, resetpass_token FROM ttrss_users
667                                         WHERE login = ?");
668                                 $sth->execute([$login]);
669
670                                 if ($row = $sth->fetch()) {
671                                         $id = $row["id"];
672                                         $resetpass_token_full = $row["resetpass_token"];
673                                         list($timestamp, $resetpass_token) = explode(":", $resetpass_token_full);
674
675                                         if ($timestamp && $resetpass_token &&
676                                                 $timestamp >= time() - 15*60*60 &&
677                                                 $resetpass_token == $hash) {
678
679                                                         $sth = $this->pdo->prepare("UPDATE ttrss_users SET resetpass_token = NULL
680                                                                 WHERE id = ?");
681                                                         $sth->execute([$id]);
682
683                                                         Pref_Users::resetUserPassword($id, true);
684
685                                                         print "<p>"."Completed."."</p>";
686
687                                         } else {
688                                                 print_error("Some of the information provided is missing or incorrect.");
689                                         }
690                                 } else {
691                                         print_error("Some of the information provided is missing or incorrect.");
692                                 }
693                         } else {
694                                 print_error("Some of the information provided is missing or incorrect.");
695                         }
696
697                         print "<form method=\"GET\" action=\"index.php\">
698                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
699                                 </form>";
700
701                 } else if (!$method) {
702                         print_notice(__("You will need to provide valid account name and email. A password reset link will be sent to your email address."));
703
704                         print "<form method='POST' action='public.php'>";
705                         print "<input type='hidden' name='method' value='do'>";
706                         print "<input type='hidden' name='op' value='forgotpass'>";
707
708                         print "<fieldset>";
709                         print "<label>".__("Login:")."</label>";
710                         print "<input type='text' name='login' value='' required>";
711                         print "</fieldset>";
712
713                         print "<fieldset>";
714                         print "<label>".__("Email:")."</label>";
715                         print "<input type='email' name='email' value='' required>";
716                         print "</fieldset>";
717
718                         print "<fieldset>";
719                         print "<label>".__("How much is two plus two:")."</label>";
720                         print "<input type='text' name='test' value='' required>";
721                         print "</fieldset>";
722
723                         print "<p/>";
724                         print "<button type='submit'>".__("Reset password")."</button>";
725
726                         print "</form>";
727                 } else if ($method == 'do') {
728
729                         $login = $_POST["login"];
730                         $email = $_POST["email"];
731                         $test = $_POST["test"];
732
733                         if (($test != 4 && $test != 'four') || !$email || !$login) {
734                                 print_error(__('Some of the required form parameters are missing or incorrect.'));
735
736                                 print "<form method=\"GET\" action=\"public.php\">
737                                         <input type=\"hidden\" name=\"op\" value=\"forgotpass\">
738                                         <input type=\"submit\" value=\"".__("Go back")."\">
739                                         </form>";
740
741                         } else {
742
743                                 print_notice("Password reset instructions are being sent to your email address.");
744
745                                 $sth = $this->pdo->prepare("SELECT id FROM ttrss_users
746                                         WHERE login = ? AND email = ?");
747                                 $sth->execute([$login, $email]);
748
749                                 if ($row = $sth->fetch()) {
750                                         $id = $row["id"];
751
752                                         if ($id) {
753                                                 $resetpass_token = sha1(get_random_bytes(128));
754                                                 $resetpass_link = get_self_url_prefix() . "/public.php?op=forgotpass&hash=" . $resetpass_token .
755                                                         "&login=" . urlencode($login);
756
757                                                 require_once 'classes/ttrssmailer.php';
758                                                 require_once "lib/MiniTemplator.class.php";
759
760                                                 $tpl = new MiniTemplator;
761
762                                                 $tpl->readTemplateFromFile("templates/resetpass_link_template.txt");
763
764                                                 $tpl->setVariable('LOGIN', $login);
765                                                 $tpl->setVariable('RESETPASS_LINK', $resetpass_link);
766
767                                                 $tpl->addBlock('message');
768
769                                                 $message = "";
770
771                                                 $tpl->generateOutputToString($message);
772
773                                                 $mail = new ttrssMailer();
774
775                                                 $rc = $mail->quickMail($email, $login,
776                                                         __("[tt-rss] Password reset request"),
777                                                         $message, false);
778
779                                                 if (!$rc) print_error($mail->ErrorInfo);
780
781                                                 $resetpass_token_full = time() . ":" . $resetpass_token;
782
783                                                 $sth = $this->pdo->prepare("UPDATE ttrss_users
784                                                         SET resetpass_token = ?
785                                                         WHERE login = ? AND email = ?");
786
787                                                 $sth->execute([$resetpass_token_full, $login, $email]);
788
789                                                 //Pref_Users::resetUserPassword($id, false);
790
791                                                 print "<p>";
792
793                                                 print "<p>"."Completed."."</p>";
794                                         } else {
795                                                 print_error("User ID not found.");
796                                         }
797
798                                         print "<form method=\"GET\" action=\"index.php\">
799                                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
800                                                 </form>";
801
802                                 } else {
803                                         print_error(__("Sorry, login and email combination not found."));
804
805                                         print "<form method=\"GET\" action=\"public.php\">
806                                                 <input type=\"hidden\" name=\"op\" value=\"forgotpass\">
807                                                 <input type=\"submit\" value=\"".__("Go back")."\">
808                                                 </form>";
809
810                                 }
811                         }
812
813                 }
814
815                 print "</div>";
816                 print "</body>";
817                 print "</html>";
818
819         }
820
821         function dbupdate() {
822                 startup_gettext();
823
824                 if (!SINGLE_USER_MODE && $_SESSION["access_level"] < 10) {
825                         $_SESSION["login_error_msg"] = __("Your access level is insufficient to run this script.");
826                         render_login_form();
827                         exit;
828                 }
829
830                 ?><html>
831                         <head>
832                         <title>Database Updater</title>
833                         <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
834                         <link rel="stylesheet" type="text/css" href="css/utility.css"/>
835                         <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
836                         <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
837                         </head>
838                         <style type="text/css">
839                                 span.ok { color : #009000; font-weight : bold; }
840                                 span.err { color : #ff0000; font-weight : bold; }
841                         </style>
842                 <body>
843                         <script type='text/javascript'>
844                         function confirmOP() {
845                                 return confirm("Update the database?");
846                         }
847                         </script>
848
849                         <div class="floatingLogo"><img src="images/logo_small.png"></div>
850
851                         <h1><?php echo __("Database Updater") ?></h1>
852
853                         <div class="content">
854
855                         <?php
856                                 @$op = $_REQUEST["subop"];
857                                 $updater = new DbUpdater(Db::pdo(), DB_TYPE, SCHEMA_VERSION);
858
859                                 if ($op == "performupdate") {
860                                         if ($updater->isUpdateRequired()) {
861
862                                                 print "<h2>Performing updates</h2>";
863
864                                                 print "<h3>Updating to schema version " . SCHEMA_VERSION . "</h3>";
865
866                                                 print "<ul>";
867
868                                                 for ($i = $updater->getSchemaVersion() + 1; $i <= SCHEMA_VERSION; $i++) {
869                                                         print "<li>Performing update up to version $i...";
870
871                                                         $result = $updater->performUpdateTo($i, true);
872
873                                                         if (!$result) {
874                                                                 print "<span class='err'>FAILED!</span></li></ul>";
875
876                                                                 print_warning("One of the updates failed. Either retry the process or perform updates manually.");
877                                                                 print "<p><form method=\"GET\" action=\"index.php\">
878                                                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
879                                                                 </form>";
880
881                                                                 return;
882                                                         } else {
883                                                                 print "<span class='ok'>OK!</span></li>";
884                                                         }
885                                                 }
886
887                                                 print "</ul>";
888
889                                                 print_notice("Your Tiny Tiny RSS database is now updated to the latest version.");
890
891                                                 print "<p><form method=\"GET\" action=\"index.php\">
892                                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
893                                                 </form>";
894
895                                         } else {
896                                                 print "<h2>Your database is up to date.</h2>";
897
898                                                 print "<p><form method=\"GET\" action=\"index.php\">
899                                                 <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
900                                                 </form>";
901                                         }
902                                 } else {
903                                         if ($updater->isUpdateRequired()) {
904
905                                                 print "<h2>Database update required</h2>";
906
907                                                 print_notice("<h4>".
908                                                 sprintf("Your Tiny Tiny RSS database needs update to the latest version: %d to %d.",
909                                                         $updater->getSchemaVersion(), SCHEMA_VERSION).
910                                                 "</h4>");
911
912                                                 print_warning("Please backup your database before proceeding.");
913
914                                                 print "<form method='POST'>
915                                                         <input type='hidden' name='subop' value='performupdate'>
916                                                         <input type='submit' onclick='return confirmOP()' value='".__("Perform updates")."'>
917                                                 </form>";
918
919                                         } else {
920
921                                                 print_notice("Tiny Tiny RSS database is up to date.");
922
923                                                 print "<p><form method=\"GET\" action=\"index.php\">
924                                                         <input type=\"submit\" value=\"".__("Return to Tiny Tiny RSS")."\">
925                                                 </form>";
926
927                                         }
928                                 }
929                         ?>
930
931                         </div>
932                         </body>
933                         </html>
934                 <?php
935         }
936
937         function cached_url() {
938                 @$hash = basename($_GET['hash']);
939
940                 // we don't need an extension to find the file, hash is a complete URL
941                 $hash = preg_replace("/\.[^\.]*$/", "", $hash);
942
943                 if ($hash) {
944
945                         $filename = CACHE_DIR . '/images/' . $hash;
946
947                         if (file_exists($filename)) {
948                                 header("Content-Disposition: inline; filename=\"$hash\"");
949
950                                 send_local_file($filename);
951
952                         } else {
953                                 header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
954                                 echo "File not found.";
955                         }
956                 }
957         }
958
959         private function make_article_tag_uri($id, $timestamp) {
960
961                 $timestamp = date("Y-m-d", strtotime($timestamp));
962
963                 return "tag:" . parse_url(get_self_url_prefix(), PHP_URL_HOST) . ",$timestamp:/$id";
964         }
965
966         // this should be used very carefully because this endpoint is exposed to unauthenticated users
967         // plugin data is not loaded because there's no user context and owner_uid/session may or may not be available
968         // in general, don't do anything user-related in here and do not modify $_SESSION
969         public function pluginhandler() {
970                 $host = new PluginHost();
971
972                 $plugin = basename($_REQUEST["plugin"]);
973                 $method = $_REQUEST["pmethod"];
974
975                 $host->load($plugin, PluginHost::KIND_USER, 0);
976                 $host->load_data();
977
978                 $pclass = $host->get_plugin($plugin);
979
980                 if ($pclass) {
981                         if (method_exists($pclass, $method)) {
982                                 if ($pclass->is_public_method($method)) {
983                                         $pclass->$method();
984                                 } else {
985                                         header("Content-Type: text/json");
986                                         print error_json(6);
987                                 }
988                         } else {
989                                 header("Content-Type: text/json");
990                                 print error_json(13);
991                         }
992                 } else {
993                         header("Content-Type: text/json");
994                         print error_json(14);
995                 }
996         }
997 }
998 ?>