backend.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <?php
  2. /* remove ill effects of magic quotes */
  3. if (get_magic_quotes_gpc()) {
  4. function stripslashes_deep($value) {
  5. $value = is_array($value) ?
  6. array_map('stripslashes_deep', $value) : stripslashes($value);
  7. return $value;
  8. }
  9. $_POST = array_map('stripslashes_deep', $_POST);
  10. $_GET = array_map('stripslashes_deep', $_GET);
  11. $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
  12. $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
  13. }
  14. require_once "functions.php";
  15. require_once "sessions.php";
  16. require_once "modules/backend-rpc.php";
  17. require_once "sanity_check.php";
  18. require_once "config.php";
  19. require_once "db.php";
  20. require_once "db-prefs.php";
  21. no_cache_incantation();
  22. if (ENABLE_TRANSLATIONS == true) {
  23. startup_gettext();
  24. }
  25. $script_started = getmicrotime();
  26. $link = db_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
  27. if (!$link) {
  28. if (DB_TYPE == "mysql") {
  29. print mysql_error();
  30. }
  31. // PG seems to display its own errors just fine by default.
  32. return;
  33. }
  34. init_connection($link);
  35. $op = $_REQUEST["op"];
  36. $subop = $_REQUEST["subop"];
  37. $mode = $_REQUEST["mode"];
  38. $print_exec_time = false;
  39. if ((!$op || $op == "rpc" || $op == "rss" ||
  40. $op == "digestSend" || $op == "dlg" ||
  41. $op == "globalUpdateFeeds") && !$_REQUEST["noxml"]) {
  42. header("Content-Type: application/xml; charset=utf-8");
  43. } else {
  44. if (!$_REQUEST["noxml"]) {
  45. header("Content-Type: text/html; charset=utf-8");
  46. } else {
  47. header("Content-Type: text/plain; charset=utf-8");
  48. }
  49. }
  50. if (ENABLE_GZIP_OUTPUT) {
  51. ob_start("ob_gzhandler");
  52. }
  53. if (SINGLE_USER_MODE) {
  54. authenticate_user($link, "admin", null);
  55. }
  56. if (!($_SESSION["uid"] && validate_session($link)) && $op != "globalUpdateFeeds" &&
  57. $op != "rss" && $op != "getUnread" && $op != "getProfiles") {
  58. header("Content-Type: text/plain");
  59. print json_encode(array("error" => array("code" => 6)));
  60. return;
  61. }
  62. $purge_intervals = array(
  63. 0 => __("Use default"),
  64. -1 => __("Never purge"),
  65. 5 => __("1 week old"),
  66. 14 => __("2 weeks old"),
  67. 31 => __("1 month old"),
  68. 60 => __("2 months old"),
  69. 90 => __("3 months old"));
  70. $update_intervals = array(
  71. 0 => __("Default interval"),
  72. -1 => __("Disable updates"),
  73. 15 => __("Each 15 minutes"),
  74. 30 => __("Each 30 minutes"),
  75. 60 => __("Hourly"),
  76. 240 => __("Each 4 hours"),
  77. 720 => __("Each 12 hours"),
  78. 1440 => __("Daily"),
  79. 10080 => __("Weekly"));
  80. $update_intervals_nodefault = array(
  81. -1 => __("Disable updates"),
  82. 15 => __("Each 15 minutes"),
  83. 30 => __("Each 30 minutes"),
  84. 60 => __("Hourly"),
  85. 240 => __("Each 4 hours"),
  86. 720 => __("Each 12 hours"),
  87. 1440 => __("Daily"),
  88. 10080 => __("Weekly"));
  89. $update_methods = array(
  90. 0 => __("Default"),
  91. 1 => __("Magpie"),
  92. 2 => __("SimplePie"),
  93. 3 => __("Twitter OAuth"));
  94. if (DEFAULT_UPDATE_METHOD == "1") {
  95. $update_methods[0] .= ' (SimplePie)';
  96. } else {
  97. $update_methods[0] .= ' (Magpie)';
  98. }
  99. $access_level_names = array(
  100. 0 => __("User"),
  101. 5 => __("Power User"),
  102. 10 => __("Administrator"));
  103. require_once "modules/pref-prefs.php";
  104. require_once "modules/popup-dialog.php";
  105. require_once "modules/help.php";
  106. require_once "modules/pref-feeds.php";
  107. require_once "modules/pref-filters.php";
  108. require_once "modules/pref-labels.php";
  109. require_once "modules/pref-users.php";
  110. $error = sanity_check($link);
  111. if ($error['code'] != 0) {
  112. print json_encode(array("error" => $error));
  113. return;
  114. }
  115. switch($op) { // Select action according to $op value.
  116. case "rpc":
  117. // Handle remote procedure calls.
  118. handle_rpc_request($link);
  119. break; // rpc
  120. case "feeds":
  121. $subop = $_REQUEST["subop"];
  122. $root = (bool)$_REQUEST["root"];
  123. switch($subop) {
  124. case "catchupAll":
  125. db_query($link, "UPDATE ttrss_user_entries SET
  126. last_read = NOW(),unread = false WHERE owner_uid = " . $_SESSION["uid"]);
  127. ccache_zero_all($link, $_SESSION["uid"]);
  128. break;
  129. case "collapse":
  130. $cat_id = db_escape_string($_REQUEST["cid"]);
  131. $mode = (int) db_escape_string($_REQUEST['mode']);
  132. toggle_collapse_cat($link, $cat_id, $mode);
  133. return;
  134. break;
  135. }
  136. if (!$root) {
  137. print json_encode(outputFeedList($link));
  138. } else {
  139. $feeds = outputFeedList($link, false);
  140. $root = array();
  141. $root['id'] = 'root';
  142. $root['name'] = __('Feeds');
  143. $root['items'] = $feeds['items'];
  144. $fl = array();
  145. $fl['identifier'] = 'id';
  146. $fl['label'] = 'name';
  147. $fl['items'] = array($root);
  148. print json_encode($fl);
  149. }
  150. break; // feeds
  151. case "la":
  152. $id = db_escape_string($_REQUEST['id']);
  153. $result = db_query($link, "SELECT link FROM ttrss_entries, ttrss_user_entries
  154. WHERE id = '$id' AND id = ref_id AND owner_uid = '".$_SESSION['uid']."'");
  155. if (db_num_rows($result) == 1) {
  156. $article_url = db_fetch_result($result, 0, 'link');
  157. $article_url = str_replace("\n", "", $article_url);
  158. header("Location: $article_url");
  159. return;
  160. } else {
  161. print_error(__("Article not found."));
  162. }
  163. break;
  164. case "view":
  165. $id = db_escape_string($_REQUEST["id"]);
  166. $cids = split(",", db_escape_string($_REQUEST["cids"]));
  167. $mode = db_escape_string($_REQUEST["mode"]);
  168. $omode = db_escape_string($_REQUEST["omode"]);
  169. // in prefetch mode we only output requested cids, main article
  170. // just gets marked as read (it already exists in client cache)
  171. $articles = array();
  172. if ($mode == "") {
  173. array_push($articles, format_article($link, $id, false));
  174. } else if ($mode == "zoom") {
  175. array_push($articles, format_article($link, $id, false, true, true));
  176. } else {
  177. catchupArticleById($link, $id, 0);
  178. }
  179. if (!$_SESSION["bw_limit"]) {
  180. foreach ($cids as $cid) {
  181. if ($cid) {
  182. array_push($articles, format_article($link, $cid, false, false));
  183. }
  184. }
  185. }
  186. print json_encode($articles);
  187. break; // view
  188. case "viewfeed":
  189. $timing_info = getmicrotime();
  190. $reply = array();
  191. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("0", $timing_info);
  192. $omode = db_escape_string($_REQUEST["omode"]);
  193. $feed = db_escape_string($_REQUEST["feed"]);
  194. $subop = db_escape_string($_REQUEST["subop"]);
  195. $view_mode = db_escape_string($_REQUEST["view_mode"]);
  196. $limit = (int) get_pref($link, "DEFAULT_ARTICLE_LIMIT");
  197. @$cat_view = db_escape_string($_REQUEST["cat"]);
  198. @$next_unread_feed = db_escape_string($_REQUEST["nuf"]);
  199. @$offset = db_escape_string($_REQUEST["skip"]);
  200. @$vgroup_last_feed = db_escape_string($_REQUEST["vgrlf"]);
  201. $order_by = db_escape_string($_REQUEST["order_by"]);
  202. /* Feed -5 is a special case: it is used to display auxiliary information
  203. * when there's nothing to load - e.g. no stuff in fresh feed */
  204. if ($feed == -5) {
  205. print json_encode(generate_dashboard_feed($link));
  206. return;
  207. }
  208. $result = false;
  209. if ($feed < -10) {
  210. $label_feed = -11-$feed;
  211. $result = db_query($link, "SELECT id FROM ttrss_labels2 WHERE
  212. id = '$label_feed' AND owner_uid = " . $_SESSION['uid']);
  213. } else if (!$cat_view && $feed > 0) {
  214. $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE
  215. id = '$feed' AND owner_uid = " . $_SESSION['uid']);
  216. } else if ($cat_view && $feed > 0) {
  217. $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE
  218. id = '$feed' AND owner_uid = " . $_SESSION['uid']);
  219. }
  220. if ($result && db_num_rows($result) == 0) {
  221. print json_encode(generate_error_feed($link, __("Feed not found.")));
  222. return;
  223. }
  224. /* Updating a label ccache means recalculating all of the caches
  225. * so for performance reasons we don't do that here */
  226. if ($feed >= 0) {
  227. ccache_update($link, $feed, $_SESSION["uid"], $cat_view);
  228. }
  229. set_pref($link, "_DEFAULT_VIEW_MODE", $view_mode);
  230. set_pref($link, "_DEFAULT_VIEW_LIMIT", $limit);
  231. set_pref($link, "_DEFAULT_VIEW_ORDER_BY", $order_by);
  232. if (!$cat_view && preg_match("/^[0-9][0-9]*$/", $feed)) {
  233. db_query($link, "UPDATE ttrss_feeds SET last_viewed = NOW()
  234. WHERE id = '$feed' AND owner_uid = ".$_SESSION["uid"]);
  235. }
  236. $reply['headlines'] = array();
  237. if (!$next_unread_feed)
  238. $reply['headlines']['id'] = $feed;
  239. else
  240. $reply['headlines']['id'] = $next_unread_feed;
  241. $reply['headlines']['is_cat'] = (bool) $cat_view;
  242. $override_order = false;
  243. if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
  244. $date_sort_field = "updated";
  245. } else {
  246. $date_sort_field = "date_entered";
  247. }
  248. switch ($order_by) {
  249. case "date":
  250. if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
  251. $override_order = "$date_sort_field";
  252. } else {
  253. $override_order = "$date_sort_field DESC";
  254. }
  255. break;
  256. case "title":
  257. if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
  258. $override_order = "title DESC, $date_sort_field";
  259. } else {
  260. $override_order = "title, $date_sort_field DESC";
  261. }
  262. break;
  263. case "score":
  264. if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
  265. $override_order = "score, $date_sort_field";
  266. } else {
  267. $override_order = "score DESC, $date_sort_field DESC";
  268. }
  269. break;
  270. }
  271. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("04", $timing_info);
  272. $ret = format_headlines_list($link, $feed, $subop,
  273. $view_mode, $limit, $cat_view, $next_unread_feed, $offset,
  274. $vgroup_last_feed, $override_order);
  275. $topmost_article_ids = $ret[0];
  276. $headlines_count = $ret[1];
  277. $returned_feed = $ret[2];
  278. $disable_cache = $ret[3];
  279. $vgroup_last_feed = $ret[4];
  280. $reply['headlines']['content'] = $ret[5];
  281. $reply['headlines']['toolbar'] = $ret[6];
  282. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("05", $timing_info);
  283. $headlines_unread = ccache_find($link, $returned_feed, $_SESSION["uid"],
  284. $cat_view, true);
  285. if ($headlines_unread == -1) {
  286. $headlines_unread = getFeedUnread($link, $returned_feed, $cat_view);
  287. }
  288. $reply['headlines-info'] = array("count" => (int) $headlines_count,
  289. "vgroup_last_feed" => $vgroup_last_feed,
  290. "unread" => (int) $headlines_unread,
  291. "disable_cache" => (bool) $disable_cache);
  292. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("20", $timing_info);
  293. if (is_array($topmost_article_ids) && !get_pref($link, 'COMBINED_DISPLAY_MODE') && !$_SESSION["bw_limit"]) {
  294. $articles = array();
  295. foreach ($topmost_article_ids as $id) {
  296. array_push($articles, format_article($link, $id, $feed, false));
  297. }
  298. $reply['articles'] = $articles;
  299. }
  300. if ($subop) {
  301. $reply['counters'] = getAllCounters($link, $omode, $feed);
  302. }
  303. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("30", $timing_info);
  304. $reply['runtime-info'] = make_runtime_info($link);
  305. print json_encode($reply);
  306. break; // viewfeed
  307. case "pref-feeds":
  308. module_pref_feeds($link);
  309. break; // pref-feeds
  310. case "pref-filters":
  311. module_pref_filters($link);
  312. break; // pref-filters
  313. case "pref-labels":
  314. module_pref_labels($link);
  315. break; // pref-labels
  316. case "pref-prefs":
  317. module_pref_prefs($link);
  318. break; // pref-prefs
  319. case "pref-users":
  320. module_pref_users($link);
  321. break; // prefs-users
  322. case "help":
  323. module_help($link);
  324. break; // help
  325. case "dlg":
  326. module_popup_dialog($link);
  327. break; // dlg
  328. case "pref-pub-items":
  329. module_pref_pub_items($link);
  330. break; // pref-pub-items
  331. case "globalUpdateFeeds":
  332. // update feeds of all users, may be used anonymously
  333. print "<!--";
  334. // Update all feeds needing a update.
  335. update_daemon_common($link, 0, true, true);
  336. print " -->";
  337. print "<rpc-reply>
  338. <message msg=\"All feeds updated\"/>
  339. </rpc-reply>";
  340. break; // globalUpdateFeeds
  341. case "pref-feed-browser":
  342. module_pref_feed_browser($link);
  343. break; // pref-feed-browser
  344. case "rss":
  345. $feed = db_escape_string($_REQUEST["id"]);
  346. $key = db_escape_string($_REQUEST["key"]);
  347. $is_cat = $_REQUEST["is_cat"] != false;
  348. $limit = (int)db_escape_string($_REQUEST["limit"]);
  349. $search = db_escape_string($_REQUEST["q"]);
  350. $match_on = db_escape_string($_REQUEST["m"]);
  351. $search_mode = db_escape_string($_REQUEST["smode"]);
  352. $view_mode = db_escape_string($_REQUEST["view-mode"]);
  353. if (SINGLE_USER_MODE) {
  354. authenticate_user($link, "admin", null);
  355. }
  356. $owner_id = false;
  357. if ($key) {
  358. $result = db_query($link, "SELECT owner_uid FROM
  359. ttrss_access_keys WHERE access_key = '$key' AND feed_id = '$feed'");
  360. if (db_num_rows($result) == 1)
  361. $owner_id = db_fetch_result($result, 0, "owner_uid");
  362. }
  363. if ($owner_id) {
  364. $_SESSION['uid'] = $owner_id;
  365. generate_syndicated_feed($link, 0, $feed, $is_cat, $limit,
  366. $search, $search_mode, $match_on, $view_mode);
  367. } else {
  368. header('HTTP/1.1 403 Forbidden');
  369. }
  370. break; // rss
  371. case "getUnread":
  372. $login = db_escape_string($_REQUEST["login"]);
  373. $fresh = $_REQUEST["fresh"] == "1";
  374. header("Content-Type: text/plain; charset=utf-8");
  375. $result = db_query($link, "SELECT id FROM ttrss_users WHERE login = '$login'");
  376. if (db_num_rows($result) == 1) {
  377. $uid = db_fetch_result($result, 0, "id");
  378. print getGlobalUnread($link, $uid);
  379. if ($fresh) {
  380. print ";";
  381. print getFeedArticles($link, -3, false, true, $uid);
  382. }
  383. } else {
  384. print "-1;User not found";
  385. }
  386. $print_exec_time = false;
  387. break; // getUnread
  388. case "digestTest":
  389. header("Content-Type: text/plain");
  390. print_r(prepare_headlines_digest($link, $_SESSION["uid"]));
  391. $print_exec_time = false;
  392. break; // digestTest
  393. case "digestSend":
  394. header("Content-Type: text/plain");
  395. send_headlines_digests($link);
  396. $print_exec_time = false;
  397. break; // digestSend
  398. case "loading":
  399. print __("Loading, please wait...") . " " .
  400. "<img src='images/indicator_tiny.gif'>";
  401. case "getProfiles":
  402. $login = db_escape_string($_REQUEST["login"]);
  403. $password = db_escape_string($_REQUEST["password"]);
  404. if (authenticate_user($link, $login, $password)) {
  405. $result = db_query($link, "SELECT * FROM ttrss_settings_profiles
  406. WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY title");
  407. print "<select style='width: 100%' name='profile'>";
  408. print "<option value='0'>" . __("Default profile") . "</option>";
  409. while ($line = db_fetch_assoc($result)) {
  410. $id = $line["id"];
  411. $title = $line["title"];
  412. print "<option value='$id'>$title</option>";
  413. }
  414. print "</select>";
  415. $_SESSION = array();
  416. }
  417. break;
  418. } // Select action according to $op value.
  419. // We close the connection to database.
  420. db_close($link);
  421. ?>
  422. <?php if ($print_exec_time) { ?>
  423. <!-- <?php echo sprintf("Backend execution time: %.4f seconds", getmicrotime() - $script_started) ?> -->
  424. <?php } ?>