]> git.wh0rd.org - tt-rss.git/blame - include/functions.php
Merge branch 'master' of github.com:gothfox/Tiny-Tiny-RSS
[tt-rss.git] / include / functions.php
CommitLineData
1d3a17c7 1<?php
6e658547 2 define('EXPECTED_CONFIG_VERSION', 26);
923b5c8b 3 define('SCHEMA_VERSION', 115);
545ca067 4
f822a8e5 5 define('LABEL_BASE_INDEX', -1024);
a413f53e 6 define('PLUGIN_FEED_BASE_INDEX', -128);
f822a8e5 7
23d2471c 8 $fetch_last_error = false;
7a01dc77 9 $fetch_last_error_code = false;
19b3992b 10 $pluginhost = false;
23d2471c 11
a48d8533 12 function __autoload($class) {
8c0496f7 13 $class_file = str_replace("_", "/", strtolower(basename($class)));
a48d8533 14
8c0496f7 15 $file = dirname(__FILE__)."/../classes/$class_file.php";
a48d8533 16
8c0496f7
AD
17 if (file_exists($file)) {
18 require $file;
a48d8533 19 }
8c0496f7 20
a48d8533 21 }
0d421af8 22
d68629dc 23 mb_internal_encoding("UTF-8");
324944f3 24 date_default_timezone_set('UTC');
8a7f5767
CW
25 if (defined('E_DEPRECATED')) {
26 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
27 } else {
28 error_reporting(E_ALL & ~E_NOTICE);
29 }
cce28758 30
40d13c28 31 require_once 'config.php';
cc17c205 32
046ec657
BK
33 /**
34 * Define a constant if not already defined
35 *
36 * @param string $name The constant name.
37 * @param mixed $value The constant value.
38 * @access public
39 * @return boolean True if defined successfully or not.
40 */
41 function define_default($name, $value) {
046ec657
BK
42 defined($name) or define($name, $value);
43 }
44
9ef8798b
BK
45 ///// Some defaults that you can override in config.php //////
46
47 define_default('FEED_FETCH_TIMEOUT', 45);
48 // How may seconds to wait for response when requesting feed from a site
49 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
50 // How may seconds to wait for response when requesting feed from a
51 // site when that feed wasn't cached before
52 define_default('FILE_FETCH_TIMEOUT', 45);
53 // Default timeout when fetching files from remote sites
54 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
55 // How many seconds to wait for initial response from website when
56 // fetching files from remote sites
046ec657 57
fc2b26a6
AD
58 if (DB_TYPE == "pgsql") {
59 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
60 } else {
61 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
62 }
63
9632f884
AD
64 /**
65 * Return available translations names.
8d505d78 66 *
9632f884
AD
67 * @access public
68 * @return array A array of available translations.
69 */
f8c612d4 70 function get_translations() {
6a214f92 71 $tr = array(
8d505d78 72 "auto" => "Detect automatically",
a3162add 73 "ca_CA" => "Català",
a06b79c4 74 "cs_CZ" => "Česky",
6a214f92 75 "en_US" => "English",
36d0510c 76 "es_ES" => "Español",
a927fe7b 77 "de_DE" => "Deutsch",
6a214f92 78 "fr_FR" => "Français",
e78fd196 79 "hu_HU" => "Magyar (Hungarian)",
bb5d3960 80 "it_IT" => "Italiano",
1d004f12 81 "ja_JP" => "日本語 (Japanese)",
7b6c1ca7 82 "lv_LV" => "Latviešu",
592535d7 83 "nb_NO" => "Norwegian bokmål",
9e7f1f12 84 "nl_NL" => "Dutch",
ea45791a 85 "pl_PL" => "Polski",
d3b923c9 86 "ru_RU" => "Русский",
9a063469 87 "pt_BR" => "Portuguese/Brazil",
5d608138 88 "zh_CN" => "Simplified Chinese",
2324f153 89 "sv_SE" => "Svenska",
5d608138 90 "fi_FI" => "Suomi");
f8c612d4
AD
91
92 return $tr;
93 }
94
7b26a148
AD
95 require_once "lib/accept-to-gettext.php";
96 require_once "lib/gettext/gettext.inc";
aba609e0 97
87d7e850 98
7b26a148 99 function startup_gettext() {
8d505d78 100
7b26a148
AD
101 # Get locale from Accept-Language header
102 $lang = al2gt(array_keys(get_translations()), "text/html");
89cb787e 103
7b26a148
AD
104 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
105 $lang = _TRANSLATION_OVERRIDE_DEFAULT;
106 }
89cb787e 107
d98e76d9 108 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
afc3cf55 109 $lang = $_SESSION["language"];
7b26a148 110 }
7c33dbd4 111
7b26a148
AD
112 if ($lang) {
113 if (defined('LC_MESSAGES')) {
114 _setlocale(LC_MESSAGES, $lang);
115 } else if (defined('LC_ALL')) {
116 _setlocale(LC_ALL, $lang);
8d039718 117 }
aba609e0 118
d98e76d9 119 _bindtextdomain("messages", "locale");
865220a4 120
7b26a148
AD
121 _textdomain("messages");
122 _bind_textdomain_codeset("messages", "UTF-8");
865220a4 123 }
7b26a148
AD
124 }
125
126 startup_gettext();
cc17c205 127
b619ff15 128 require_once 'db-prefs.php';
8911ac8b 129 require_once 'version.php';
87d7e850
AD
130 require_once 'ccache.php';
131 require_once 'labels.php';
40d13c28 132
fb850eec 133 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
500943a4
AD
134 ini_set('user_agent', SELF_USER_AGENT);
135
b0f379df 136 require_once 'lib/pubsubhubbub/publisher.php';
010efc9b 137
7d96bfcd
AD
138 $tz_offset = -1;
139 $utc_tz = new DateTimeZone('UTC');
140 $schema_version = false;
141
45004d43
AD
142 /**
143 * Print a timestamped debug message.
8d505d78 144 *
45004d43
AD
145 * @param string $msg The debug message.
146 * @return void
147 */
6f9e33e4
AD
148 function _debug($msg) {
149 $ts = strftime("%H:%M:%S", time());
2a6a9395
AD
150 if (function_exists('posix_getpid')) {
151 $ts = "$ts/" . posix_getpid();
152 }
2191eb7a
AD
153
154 if (!(defined('QUIET') && QUIET)) {
155 print "[$ts] $msg\n";
156 }
157
158 if (defined('LOGFILE')) {
159 $fp = fopen(LOGFILE, 'a+');
160
161 if ($fp) {
162 fputs($fp, "[$ts] $msg\n");
163 fclose($fp);
164 }
165 }
166
45004d43 167 } // function _debug
6f9e33e4 168
9632f884
AD
169 /**
170 * Purge a feed old posts.
8d505d78 171 *
9632f884
AD
172 * @param mixed $link A database connection.
173 * @param mixed $feed_id The id of the purged feed.
174 * @param mixed $purge_interval Olderness of purged posts.
175 * @param boolean $debug Set to True to enable the debug. False by default.
176 * @access public
177 * @return void
178 */
ad507f85
AD
179 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
180
07d0efe9 181 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
8d505d78 182
ad507f85 183 $rows = -1;
4c193675 184
8d505d78 185 $result = db_query($link,
07d0efe9
AD
186 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
187
188 $owner_uid = false;
189
190 if (db_num_rows($result) == 1) {
191 $owner_uid = db_fetch_result($result, 0, "owner_uid");
192 }
193
ab954dff
AD
194 if ($purge_interval == -1 || !$purge_interval) {
195 if ($owner_uid) {
196 ccache_update($link, $feed_id, $owner_uid);
197 }
198 return;
199 }
200
07d0efe9
AD
201 if (!$owner_uid) return;
202
3907ef71
AD
203 if (FORCE_ARTICLE_PURGE == 0) {
204 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
205 $owner_uid, false);
206 } else {
207 $purge_unread = true;
208 $purge_interval = FORCE_ARTICLE_PURGE;
209 }
07d0efe9
AD
210
211 if (!$purge_unread) $query_limit = " unread = false AND ";
212
fefa6ca3 213 if (DB_TYPE == "pgsql") {
6e7f8d26
AD
214 $pg_version = get_pgsql_version($link);
215
216 if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) {
1e59ae35 217
8d505d78
AD
218 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
219 ttrss_entries.id = ref_id AND
220 marked = false AND
221 feed_id = '$feed_id' AND
07d0efe9 222 $query_limit
25ea2805 223 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
1e59ae35
AD
224
225 } else {
226
8d505d78
AD
227 $result = db_query($link, "DELETE FROM ttrss_user_entries
228 USING ttrss_entries
229 WHERE ttrss_entries.id = ref_id AND
230 marked = false AND
231 feed_id = '$feed_id' AND
07d0efe9 232 $query_limit
25ea2805 233 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
1e59ae35 234 }
ad507f85 235
8c0496f7
AD
236 $rows = pg_affected_rows($result);
237
fefa6ca3 238 } else {
8d505d78 239
30f1746f 240/* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
fefa6ca3 241 marked = false AND feed_id = '$feed_id' AND
8d505d78 242 (SELECT date_updated FROM ttrss_entries WHERE
30f1746f
AD
243 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
244
8d505d78
AD
245 $result = db_query($link, "DELETE FROM ttrss_user_entries
246 USING ttrss_user_entries, ttrss_entries
247 WHERE ttrss_entries.id = ref_id AND
248 marked = false AND
249 feed_id = '$feed_id' AND
07d0efe9 250 $query_limit
25ea2805 251 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
8d505d78 252
8c0496f7
AD
253 $rows = mysql_affected_rows($link);
254
ad507f85
AD
255 }
256
ced46404
AD
257 ccache_update($link, $feed_id, $owner_uid);
258
ad507f85 259 if ($debug) {
6f9e33e4 260 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
fefa6ca3 261 }
2ea09bde
AD
262
263 return $rows;
9632f884 264 } // function purge_feed
fefa6ca3 265
07d0efe9
AD
266 function feed_purge_interval($link, $feed_id) {
267
8d505d78 268 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
07d0efe9
AD
269 WHERE id = '$feed_id'");
270
271 if (db_num_rows($result) == 1) {
272 $purge_interval = db_fetch_result($result, 0, "purge_interval");
273 $owner_uid = db_fetch_result($result, 0, "owner_uid");
274
8d505d78 275 if ($purge_interval == 0) $purge_interval = get_pref($link,
863be6ca 276 'PURGE_OLD_DAYS', $owner_uid);
07d0efe9
AD
277
278 return $purge_interval;
279
280 } else {
281 return -1;
282 }
283 }
284
a2d79981
AD
285 function purge_orphans($link, $do_output = false) {
286
71604ca4 287 // purge orphaned posts in main content table
8d505d78 288 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
71604ca4 289 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
a2d79981
AD
290
291 if ($do_output) {
292 $rows = db_affected_rows($link, $result);
293 _debug("Purged $rows orphaned posts.");
294 }
c3a8d71a
AD
295 }
296
c7d57b66
AD
297 function get_feed_update_interval($link, $feed_id) {
298 $result = db_query($link, "SELECT owner_uid, update_interval FROM
299 ttrss_feeds WHERE id = '$feed_id'");
300
301 if (db_num_rows($result) == 1) {
302 $update_interval = db_fetch_result($result, 0, "update_interval");
303 $owner_uid = db_fetch_result($result, 0, "owner_uid");
304
305 if ($update_interval != 0) {
306 return $update_interval;
307 } else {
308 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
309 }
310
311 } else {
312 return -1;
313 }
314 }
315
7a01dc77 316 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
8d505d78 317
23d2471c 318 global $fetch_last_error;
7a01dc77 319 global $fetch_last_error_code;
23d2471c 320
6470aaec 321 if (!defined('NO_CURL') && !function_exists('curl_init') && !ini_get("open_basedir")) {
b799dc8b
AD
322
323 if (ini_get("safe_mode")) {
324 $ch = curl_init(geturl($url));
325 } else {
326 $ch = curl_init($url);
327 }
a1af1574 328
7a01dc77
AD
329 if ($timestamp) {
330 curl_setopt($ch, CURLOPT_HTTPHEADER,
331 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
332 }
333
8401101d
BK
334 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
335 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
81153e6b 336 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("safe_mode"));
a1af1574
AD
337 curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
338 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
339 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
8d505d78 340 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
5f6804bc 341 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
19929bbe 342 curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
268a06dc 343 curl_setopt($ch, CURLOPT_ENCODING , "gzip");
48b657fc 344 curl_setopt($ch, CURLOPT_REFERER, $url);
8d505d78 345
ae5f7bb1
AD
346 if ($post_query) {
347 curl_setopt($ch, CURLOPT_POST, true);
348 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
349 }
350
8d505d78
AD
351 if ($login && $pass)
352 curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
a1af1574 353
fb074239 354 $contents = @curl_exec($ch);
268a06dc 355
48b657fc
AD
356 if (curl_errno($ch) === 23 || curl_errno($ch) === 61) {
357 curl_setopt($ch, CURLOPT_ENCODING, 'none');
358 $contents = @curl_exec($ch);
fb850eec
AD
359 }
360
a1af1574 361 if ($contents === false) {
fb850eec 362 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
a1af1574
AD
363 curl_close($ch);
364 return false;
4065b60b
AD
365 }
366
8d505d78 367 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
a1af1574 368 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
4065b60b 369
7a01dc77
AD
370 $fetch_last_error_code = $http_code;
371
8d505d78 372 if ($http_code != 200 || $type && strpos($content_type, "$type") === false) {
fb850eec
AD
373 if (curl_errno($ch) != 0) {
374 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
375 } else {
376 $fetch_last_error = "HTTP Code: $http_code";
377 }
378 curl_close($ch);
a1af1574
AD
379 return false;
380 }
4065b60b 381
fb850eec
AD
382 curl_close($ch);
383
a1af1574 384 return $contents;
4065b60b 385 } else {
d3911f80 386 if ($login && $pass){
8d505d78
AD
387 $url_parts = array();
388
389 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
390
d3911f80
AD
391 $pass = urlencode($pass);
392
8d505d78
AD
393 if ($url_parts[1] && $url_parts[2]) {
394 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
395 }
396 }
397
23d2471c
AD
398 $data = @file_get_contents($url);
399
400 if (!$data && function_exists('error_get_last')) {
401 $error = error_get_last();
402 $fetch_last_error = $error["message"];
403 }
404 return $data;
4065b60b
AD
405 }
406
407 }
78800912 408
9632f884
AD
409 /**
410 * Try to determine the favicon URL for a feed.
411 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
412 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
8d505d78 413 *
9632f884
AD
414 * @param string $url A feed or page URL
415 * @access public
416 * @return mixed The favicon URL, or false if none was found.
417 */
1bd11fdf 418 function get_favicon_url($url) {
99331724 419
1bd11fdf 420 $favicon_url = false;
ed214298 421
4065b60b 422 if ($html = @fetch_file_contents($url)) {
78800912 423
ed214298 424 libxml_use_internal_errors(true);
c798704b 425
ed214298
AD
426 $doc = new DOMDocument();
427 $doc->loadHTML($html);
428 $xpath = new DOMXPath($doc);
717f5e64 429
a712429e
AD
430 $base = $xpath->query('/html/head/base');
431 foreach ($base as $b) {
432 $url = $b->getAttribute("href");
433 break;
434 }
435
1bd11fdf 436 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
ed214298
AD
437 if (count($entries) > 0) {
438 foreach ($entries as $entry) {
1bd11fdf
AD
439 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
440 break;
ed214298 441 }
8d505d78 442 }
4065b60b 443 }
c798704b 444
1bd11fdf
AD
445 if (!$favicon_url)
446 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
447
448 return $favicon_url;
449 } // function get_favicon_url
450
451 function check_feed_favicon($site_url, $feed, $link) {
882311d9 452# print "FAVICON [$site_url]: $favicon_url\n";
4065b60b 453
1bd11fdf
AD
454 $icon_file = ICONS_DIR . "/$feed.ico";
455
456 if (!file_exists($icon_file)) {
457 $favicon_url = get_favicon_url($site_url);
458
459 if ($favicon_url) {
460 // Limiting to "image" type misses those served with text/plain
461 $contents = fetch_file_contents($favicon_url); // , "image");
462
463 if ($contents) {
464 // Crude image type matching.
465 // Patterns gleaned from the file(1) source code.
466 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
467 // 0 string \000\000\001\000 MS Windows icon resource
468 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
469 }
470 elseif (preg_match('/^GIF8/', $contents)) {
471 // 0 string GIF8 GIF image data
472 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
473 }
474 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
475 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
476 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
477 }
478 elseif (preg_match('/^\xff\xd8/', $contents)) {
479 // 0 beshort 0xffd8 JPEG image data
480 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
481 }
482 else {
483 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
484 $contents = "";
485 }
486 }
487
488 if ($contents) {
489 $fp = @fopen($icon_file, "w");
490
491 if ($fp) {
492 fwrite($fp, $contents);
493 fclose($fp);
494 chmod($icon_file, 0644);
495 }
496 }
497 }
78800912
AD
498 }
499 }
500
f175937c 501 function print_select($id, $default, $values, $attributes = "") {
79f3553b 502 print "<select name=\"$id\" id=\"$id\" $attributes>";
a0d53889
AD
503 foreach ($values as $v) {
504 if ($v == $default)
60807300 505 $sel = "selected=\"1\"";
a0d53889
AD
506 else
507 $sel = "";
8d505d78 508
e88c1943
AD
509 $v = trim($v);
510
60807300 511 print "<option value=\"$v\" $sel>$v</option>";
a0d53889
AD
512 }
513 print "</select>";
514 }
40d13c28 515
79f3553b
AD
516 function print_select_hash($id, $default, $values, $attributes = "") {
517 print "<select name=\"$id\" id='$id' $attributes>";
673d54ca
AD
518 foreach (array_keys($values) as $v) {
519 if ($v == $default)
74d5c8fa 520 $sel = 'selected="selected"';
673d54ca
AD
521 else
522 $sel = "";
8d505d78 523
e88c1943
AD
524 $v = trim($v);
525
673d54ca
AD
526 print "<option $sel value=\"$v\">".$values[$v]."</option>";
527 }
528
529 print "</select>";
530 }
531
f541eb78 532 function print_radio($id, $default, $true_is, $values, $attributes = "") {
77e96719 533 foreach ($values as $v) {
8d505d78 534
77e96719 535 if ($v == $default)
5da169d9 536 $sel = "checked";
77e96719 537 else
5da169d9
AD
538 $sel = "";
539
f541eb78 540 if ($v == $true_is) {
5da169d9
AD
541 $sel .= " value=\"1\"";
542 } else {
543 $sel .= " value=\"0\"";
544 }
8d505d78
AD
545
546 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
69654950 547 type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
77e96719
AD
548
549 }
550 }
551
d9084cf2 552 function initialize_user_prefs($link, $uid, $profile = false) {
ff485f1d 553
3972bf59 554 $uid = db_escape_string($link, $uid);
ff485f1d 555
d9084cf2
AD
556 if (!$profile) {
557 $profile = "NULL";
f9aa6a89 558 $profile_qpart = "AND profile IS NULL";
d9084cf2 559 } else {
f9aa6a89 560 $profile_qpart = "AND profile = '$profile'";
d9084cf2
AD
561 }
562
f9aa6a89
AD
563 if (get_schema_version($link) < 63) $profile_qpart = "";
564
ff485f1d
AD
565 db_query($link, "BEGIN");
566
567 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
8d505d78
AD
568
569 $u_result = db_query($link, "SELECT pref_name
f9aa6a89 570 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
ff485f1d
AD
571
572 $active_prefs = array();
573
574 while ($line = db_fetch_assoc($u_result)) {
8d505d78 575 array_push($active_prefs, $line["pref_name"]);
ff485f1d
AD
576 }
577
578 while ($line = db_fetch_assoc($result)) {
579 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
580// print "adding " . $line["pref_name"] . "<br>";
581
d296ba50
AD
582 $line["def_value"] = db_escape_string($link, $line["def_value"]);
583 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
584
f9aa6a89
AD
585 if (get_schema_version($link) < 63) {
586 db_query($link, "INSERT INTO ttrss_user_prefs
8d505d78 587 (owner_uid,pref_name,value) VALUES
f9aa6a89
AD
588 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
589
590 } else {
591 db_query($link, "INSERT INTO ttrss_user_prefs
8d505d78 592 (owner_uid,pref_name,value, profile) VALUES
f9aa6a89
AD
593 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
594 }
ff485f1d
AD
595
596 }
597 }
598
599 db_query($link, "COMMIT");
600
601 }
956c7629 602
8de8bfb8
AD
603 function get_ssl_certificate_id() {
604 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
605 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
606 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
607 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
608 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
609 }
610 return "";
611 }
612
0d421af8 613 function authenticate_user($link, $login, $password, $check_only = false) {
c8437f35 614
131b01b3 615 if (!SINGLE_USER_MODE) {
0d421af8 616 $user_id = false;
0f28f81f
AD
617
618 global $pluginhost;
619 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER) as $plugin) {
620
621 $user_id = (int) $plugin->authenticate($login, $password);
622
623 if ($user_id) {
624 $_SESSION["auth_module"] = strtolower(get_class($plugin));
625 break;
626 }
461766f3
AD
627 }
628
0d421af8 629 if ($user_id && !$check_only) {
70400171
AD
630 @session_start();
631
0d421af8
AD
632 $_SESSION["uid"] = $user_id;
633
634 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
635 WHERE id = '$user_id'");
8d505d78 636
131b01b3
AD
637 $_SESSION["name"] = db_fetch_result($result, 0, "login");
638 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
8484ce22 639 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
8d505d78
AD
640
641 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
131b01b3 642 $_SESSION["uid"]);
8d505d78 643
131b01b3 644 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
837ec70e 645 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
1a9f4d3c 646 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
91c5f229
AD
647
648 $_SESSION["last_version_check"] = time();
8d505d78 649
131b01b3 650 initialize_user_prefs($link, $_SESSION["uid"]);
8d505d78 651
131b01b3
AD
652 return true;
653 }
8d505d78 654
131b01b3 655 return false;
503eb349 656
131b01b3 657 } else {
503eb349 658
131b01b3
AD
659 $_SESSION["uid"] = 1;
660 $_SESSION["name"] = "admin";
787e5ebc 661 $_SESSION["access_level"] = 10;
21e42e5f 662
0d421af8
AD
663 $_SESSION["hide_hello"] = true;
664 $_SESSION["hide_logout"] = true;
665
d5fd183d
AD
666 $_SESSION["auth_module"] = false;
667
21e42e5f
AD
668 if (!$_SESSION["csrf_token"]) {
669 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
670 }
f557cd78 671
0bbba72d 672 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
8d505d78 673
0bbba72d 674 initialize_user_prefs($link, $_SESSION["uid"]);
8d505d78 675
c8437f35
AD
676 return true;
677 }
c8437f35
AD
678 }
679
e6cb77a0
AD
680 function make_password($length = 8) {
681
85db6213
AD
682 $password = "";
683 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
684
685 $i = 0;
686
687 while ($i < $length) {
688 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
689
690 if (!strstr($password, $char)) {
691 $password .= $char;
692 $i++;
693 }
694 }
695 return $password;
e6cb77a0
AD
696 }
697
698 // this is called after user is created to initialize default feeds, labels
699 // or whatever else
8d505d78 700
e6cb77a0
AD
701 // user preferences are checked on every login, not here
702
703 function initialize_user($link, $uid) {
704
e6cb77a0 705 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
74bff337 706 values ('$uid', 'Tiny Tiny RSS: New Releases',
b6d486a3 707 'http://tt-rss.org/releases.rss')");
3b0feb9b 708
cd2cd415
AD
709 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
710 values ('$uid', 'Tiny Tiny RSS: Forum',
f0855b88 711 'http://tt-rss.org/forum/rss.php')");
3b0feb9b 712 }
e6cb77a0 713
b8aa49bc 714 function logout_user() {
5ccc1cf5
AD
715 session_destroy();
716 if (isset($_COOKIE[session_name()])) {
717 setcookie(session_name(), '', time()-42000, '/');
718 }
b8aa49bc
AD
719 }
720
8484ce22
AD
721 function validate_csrf($csrf_token) {
722 return $csrf_token == $_SESSION['csrf_token'];
723 }
724
de612e7a
AD
725 function load_user_plugins($link, $owner_uid) {
726 if ($owner_uid) {
727 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
728
729 global $pluginhost;
d8a1d2a2 730 $pluginhost->load($plugins, $pluginhost::KIND_USER, $owner_uid);
e9c04fd4
AD
731
732 if (get_schema_version($link) > 100) {
733 $pluginhost->load_data();
734 }
de612e7a
AD
735 }
736 }
737
d98e76d9 738 function login_sequence($link) {
75a316ab
AD
739 $_SESSION["prefs_cache"] = false;
740
97acbaf1 741 if (SINGLE_USER_MODE) {
25db6c51 742 @session_start();
de612e7a 743 authenticate_user($link, "admin", null);
0a117b86 744 cache_prefs($link);
de612e7a 745 load_user_plugins($link, $_SESSION["uid"]);
97acbaf1
AD
746 } else {
747 if (!$_SESSION["uid"] || !validate_session($link)) {
748
749 if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) {
750 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
751 } else {
752 authenticate_user($link, null, null, true);
753 }
754
d98e76d9 755 if (!$_SESSION["uid"]) render_login_form($link);
97acbaf1
AD
756
757 } else {
758 /* bump login timestamp */
759 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
760 $_SESSION["uid"]);
06b0777f 761 $_SESSION["last_login_update"] = time();
01a87dff
AD
762 }
763
afc3cf55
AD
764 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
765 setcookie("ttrss_lang", $_SESSION["language"],
766 time() + SESSION_COOKIE_LIFETIME);
b8aa49bc 767 }
de612e7a
AD
768
769 if ($_SESSION["uid"]) {
0a117b86 770 cache_prefs($link);
de612e7a 771 load_user_plugins($link, $_SESSION["uid"]);
b1b1d25f
AD
772
773 /* cleanup ccache */
774
775 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
776 $_SESSION["uid"] . " AND
777 (SELECT COUNT(id) FROM ttrss_feeds WHERE
778 ttrss_feeds.id = feed_id) = 0");
779
780 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
781 $_SESSION["uid"] . " AND
782 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
783 ttrss_feed_categories.id = feed_id) = 0");
784
de612e7a 785 }
b1b1d25f 786
b8aa49bc 787 }
afc3cf55 788 }
3547842a 789
411fe209 790 function truncate_string($str, $max_len, $suffix = '&hellip;') {
12db369c 791 if (mb_strlen($str, "utf-8") > $max_len - 3) {
411fe209 792 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
3547842a
AD
793 } else {
794 return $str;
795 }
796 }
54a60e1a 797
ab4b768f
AD
798 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
799
800 try {
801 $source_tz = new DateTimeZone($source_tz);
802 } catch (Exception $e) {
803 $source_tz = new DateTimeZone('UTC');
804 }
805
806 try {
807 $dest_tz = new DateTimeZone($dest_tz);
808 } catch (Exception $e) {
809 $dest_tz = new DateTimeZone('UTC');
810 }
811
812 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
813 return $dt->format('U') + $dest_tz->getOffset($dt);
814 }
815
324944f3
AD
816 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
817 $no_smart_dt = false) {
818
819 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
820 if (!$timestamp) $timestamp = '1970-01-01 0:00';
821
7d96bfcd
AD
822 global $utc_tz;
823 global $tz_offset;
324944f3 824
7d96bfcd
AD
825 # We store date in UTC internally
826 $dt = new DateTime($timestamp, $utc_tz);
827
828 if ($tz_offset == -1) {
829
830 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
831
832 try {
833 $user_tz = new DateTimeZone($user_tz_string);
834 } catch (Exception $e) {
835 $user_tz = $utc_tz;
836 }
837
838 $tz_offset = $user_tz->getOffset($dt);
324944f3
AD
839 }
840
7d96bfcd 841 $user_timestamp = $dt->format('U') + $tz_offset;
324944f3 842
1dc52ae7 843 if (!$no_smart_dt) {
8d505d78 844 return smart_date_time($link, $user_timestamp,
7d96bfcd 845 $tz_offset, $owner_uid);
324944f3
AD
846 } else {
847 if ($long)
848 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
849 else
850 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
851
852 return date($format, $user_timestamp);
853 }
854 }
855
2a5c136e
AD
856 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
857 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
858
859 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) {
be773442 860 return date("G:i", $timestamp);
2a5c136e
AD
861 } else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) {
862 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
863 return date($format, $timestamp);
be773442 864 } else {
2a5c136e
AD
865 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
866 return date($format, $timestamp);
be773442
AD
867 }
868 }
869
e3c99f3b 870 function sql_bool_to_bool($s) {
9955a134 871 if ($s == "t" || $s == "1" || strtolower($s) == "true") {
e3c99f3b
AD
872 return true;
873 } else {
874 return false;
875 }
876 }
8d505d78 877
badac687
AD
878 function bool_to_sql_bool($s) {
879 if ($s) {
880 return "true";
881 } else {
882 return "false";
883 }
884 }
e3c99f3b 885
fcfa9ef1
AD
886 // Session caching removed due to causing wrong redirects to upgrade
887 // script when get_schema_version() is called on an obsolete session
888 // created on a previous schema version.
199db684 889 function get_schema_version($link, $nocache = false) {
7d96bfcd
AD
890 global $schema_version;
891
892 if (!$schema_version) {
199db684
AD
893 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
894 $version = db_fetch_result($result, 0, "schema_version");
7d96bfcd 895 $schema_version = $version;
199db684 896 return $version;
7d96bfcd
AD
897 } else {
898 return $schema_version;
899 }
e4c51a6c
AD
900 }
901
6043fb7e 902 function sanity_check($link) {
31303c6b 903 require_once 'errors.php';
ebb948c2 904
6043fb7e 905 $error_code = 0;
7d96bfcd 906 $schema_version = get_schema_version($link, true);
6043fb7e
AD
907
908 if ($schema_version != SCHEMA_VERSION) {
909 $error_code = 5;
910 }
911
aec3ce39
AD
912 if (DB_TYPE == "mysql") {
913 $result = db_query($link, "SELECT true", false);
914 if (db_num_rows($result) != 1) {
915 $error_code = 10;
916 }
917 }
918
3972bf59 919 if (db_escape_string($link, "testTEST") != "testTEST") {
f29ba148
AD
920 $error_code = 12;
921 }
922
ebb948c2 923 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
6043fb7e
AD
924 }
925
27981ca3 926 function file_is_locked($filename) {
31a6d42d 927 if (function_exists('flock')) {
fb074239 928 $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
31a6d42d
AD
929 if ($fp) {
930 if (flock($fp, LOCK_EX | LOCK_NB)) {
931 flock($fp, LOCK_UN);
932 fclose($fp);
933 return false;
934 }
27981ca3 935 fclose($fp);
31a6d42d 936 return true;
e89aed7b
AD
937 } else {
938 return false;
27981ca3 939 }
27981ca3 940 }
c1fb4a5e 941 return true; // consider the file always locked and skip the test
27981ca3
AD
942 }
943
fcb4c0c9 944 function make_lockfile($filename) {
cfa43e02 945 $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
fcb4c0c9 946
a44bfcfd 947 if ($fp && flock($fp, LOCK_EX | LOCK_NB)) {
4c59adb1
AD
948 if (function_exists('posix_getpid')) {
949 fwrite($fp, posix_getpid() . "\n");
950 }
fcb4c0c9
AD
951 return $fp;
952 } else {
953 return false;
954 }
955 }
956
bf7fcde8 957 function make_stampfile($filename) {
cfa43e02 958 $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
bf7fcde8 959
8e00ae9b 960 if (flock($fp, LOCK_EX | LOCK_NB)) {
bf7fcde8 961 fwrite($fp, time() . "\n");
8e00ae9b 962 flock($fp, LOCK_UN);
bf7fcde8
AD
963 fclose($fp);
964 return true;
965 } else {
966 return false;
967 }
968 }
969
894ebcf5 970 function sql_random_function() {
8c0496f7 971 if (DB_TYPE == "mysql") {
894ebcf5
AD
972 return "RAND()";
973 } else {
974 return "RANDOM()";
975 }
976 }
977
c8b693cf 978 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
c7e51de1
AD
979
980 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
88040f57 981
37c03d3a 982 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
22fdebff 983
c8b693cf
AD
984 // Todo: all this interval stuff needs some generic generator function
985
986 $date_qpart = "false";
987
988 switch ($mode) {
989 case "1day":
990 if (DB_TYPE == "pgsql") {
94828a8b 991 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
c8b693cf 992 } else {
d5381016 993 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
c8b693cf
AD
994 }
995 break;
996 case "1week":
997 if (DB_TYPE == "pgsql") {
94828a8b 998 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
c8b693cf 999 } else {
94828a8b 1000 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
c8b693cf
AD
1001 }
1002 break;
1003 case "2weeks":
1004 if (DB_TYPE == "pgsql") {
94828a8b 1005 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
c8b693cf 1006 } else {
94828a8b 1007 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
c8b693cf
AD
1008 }
1009 break;
1010 default:
1011 $date_qpart = "true";
1012 }
1013
37c03d3a 1014 if (is_numeric($feed)) {
23aa0d16
AD
1015 if ($cat_view) {
1016
72a2f4f5 1017 if ($feed >= 0) {
f9fca8cb
AD
1018
1019 if ($feed > 0) {
bda6afa2
AD
1020 $children = getChildCategories($link, $feed, $owner_uid);
1021 array_push($children, $feed);
1022
1023 $children = join(",", $children);
1024
1025 $cat_qpart = "cat_id IN ($children)";
f9fca8cb
AD
1026 } else {
1027 $cat_qpart = "cat_id IS NULL";
1028 }
8d505d78 1029
bda6afa2 1030 db_query($link, "UPDATE ttrss_user_entries
c8b693cf
AD
1031 SET unread = false, last_read = NOW() WHERE ref_id IN
1032 (SELECT id FROM
1033 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1034 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1035 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
23aa0d16 1036
f9fca8cb 1037 } else if ($feed == -2) {
23aa0d16 1038
8d505d78
AD
1039 db_query($link, "UPDATE ttrss_user_entries
1040 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1041 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
c8b693cf 1042 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
23aa0d16
AD
1043 }
1044
1045 } else if ($feed > 0) {
1046
8d505d78 1047 db_query($link, "UPDATE ttrss_user_entries
c8b693cf
AD
1048 SET unread = false, last_read = NOW() WHERE ref_id IN
1049 (SELECT id FROM
1050 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1051 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
8d505d78 1052
f822a8e5 1053 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred
23aa0d16
AD
1054
1055 if ($feed == -1) {
8d505d78 1056 db_query($link, "UPDATE ttrss_user_entries
c8b693cf
AD
1057 SET unread = false, last_read = NOW() WHERE ref_id IN
1058 (SELECT id FROM
1059 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1060 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
23aa0d16 1061 }
e4f4b46f
AD
1062
1063 if ($feed == -2) {
8d505d78 1064 db_query($link, "UPDATE ttrss_user_entries
c8b693cf
AD
1065 SET unread = false, last_read = NOW() WHERE ref_id IN
1066 (SELECT id FROM
1067 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1068 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
e4f4b46f
AD
1069 }
1070
2d24f032
AD
1071 if ($feed == -3) {
1072
c1d7e6c3
AD
1073 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1074
2d24f032 1075 if (DB_TYPE == "pgsql") {
8d505d78 1076 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
2d24f032 1077 } else {
8d505d78 1078 $match_part = "updated > DATE_SUB(NOW(),
c1d7e6c3 1079 INTERVAL $intl HOUR) ";
2d24f032
AD
1080 }
1081
c8b693cf
AD
1082 db_query($link, "UPDATE ttrss_user_entries
1083 SET unread = false, last_read = NOW() WHERE ref_id IN
1084 (SELECT id FROM
1085 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1086 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $match_part) as tmp)");
2d24f032
AD
1087 }
1088
3584cb11 1089 if ($feed == -4) {
8d505d78 1090 db_query($link, "UPDATE ttrss_user_entries
c8b693cf
AD
1091 SET unread = false, last_read = NOW() WHERE ref_id IN
1092 (SELECT id FROM
1093 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1094 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
3584cb11
AD
1095 }
1096
f822a8e5 1097 } else if ($feed < LABEL_BASE_INDEX) { // label
23aa0d16 1098
f822a8e5 1099 $label_id = feed_to_label_id($feed);
23aa0d16 1100
c8b693cf
AD
1101 db_query($link, "UPDATE ttrss_user_entries
1102 SET unread = false, last_read = NOW() WHERE ref_id IN
1103 (SELECT id FROM
1104 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1105 AND label_id = '$label_id' AND ref_id = article_id
1106 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
23aa0d16 1107
23aa0d16 1108 }
ad0056a8 1109
c7e51de1 1110 ccache_update($link, $feed, $owner_uid, $cat_view);
ad0056a8 1111
23aa0d16 1112 } else { // tag
c8b693cf
AD
1113 db_query($link, "UPDATE ttrss_user_entries
1114 SET unread = false, last_read = NOW() WHERE ref_id IN
1115 (SELECT id FROM
1116 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1117 AND post_int_id = int_id AND tag_name = '$feed'
1118 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
23aa0d16 1119
23aa0d16
AD
1120 }
1121 }
1122
5b55e9e2 1123 function getAllCounters($link) {
6a7817c1 1124 $data = getGlobalCounters($link);
8d505d78 1125
6a7817c1 1126 $data = array_merge($data, getVirtCounters($link));
5b55e9e2
AD
1127 $data = array_merge($data, getLabelCounters($link));
1128 $data = array_merge($data, getFeedCounters($link, $active_feed));
1129 $data = array_merge($data, getCategoryCounters($link));
6a7817c1
AD
1130
1131 return $data;
8d505d78 1132 }
a9cb1f83 1133
79178062
AD
1134 function getCategoryTitle($link, $cat_id) {
1135
1136 if ($cat_id == -1) {
1137 return __("Special");
1138 } else if ($cat_id == -2) {
1139 return __("Labels");
1140 } else {
1141
1142 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1143 id = '$cat_id'");
1144
1145 if (db_num_rows($result) == 1) {
1146 return db_fetch_result($result, 0, "title");
1147 } else {
f99759da 1148 return __("Uncategorized");
79178062
AD
1149 }
1150 }
1151 }
1152
1153
a9cb1f83 1154 function getCategoryCounters($link) {
6a7817c1 1155 $ret_arr = array();
bba7c4bf 1156
6a7817c1 1157 /* Labels category */
bba7c4bf 1158
8acc449c 1159 $cv = array("id" => -2, "kind" => "cat",
6a7817c1 1160 "counter" => getCategoryUnread($link, -2));
bba7c4bf 1161
6a7817c1 1162 array_push($ret_arr, $cv);
bba7c4bf 1163
41759103 1164 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
2c5f231e
AD
1165 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1166 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
8d505d78
AD
1167 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1168 WHERE ttrss_cat_counters_cache.feed_id = id AND
fc9de939 1169 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
31375163 1170 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
a9cb1f83
AD
1171
1172 while ($line = db_fetch_assoc($result)) {
22fdebff 1173 $line["cat_id"] = (int) $line["cat_id"];
8a4c759e 1174
2c5f231e 1175 if ($line["num_children"] > 0) {
99c9e91a 1176 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
2c5f231e
AD
1177 } else {
1178 $child_counter = 0;
1179 }
1180
8acc449c 1181 $cv = array("id" => $line["cat_id"], "kind" => "cat",
0ef32f48 1182 "counter" => $line["unread"] + $child_counter);
6a7817c1
AD
1183
1184 array_push($ret_arr, $cv);
a9cb1f83 1185 }
d232a40f
AD
1186
1187 /* Special case: NULL category doesn't actually exist in the DB */
1188
9798b2b4 1189 $cv = array("id" => 0, "kind" => "cat",
12e6de72 1190 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
d232a40f 1191
6a7817c1
AD
1192 array_push($ret_arr, $cv);
1193
1194 return $ret_arr;
a9cb1f83
AD
1195 }
1196
2c5f231e 1197 // only accepts real cats (>= 0)
99c9e91a 1198 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
2c5f231e
AD
1199 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1200
1201 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1202 AND owner_uid = $owner_uid");
1203
1204 $unread = 0;
1205
1206 while ($line = db_fetch_assoc($result)) {
1207 $unread += getCategoryUnread($link, $line["id"], $owner_uid);
99c9e91a 1208 $unread += getCategoryChildrenUnread($link, $line["id"], $owner_uid);
2c5f231e
AD
1209 }
1210
1211 return $unread;
1212 }
1213
b6d486a3
AD
1214 function getCategoryUnread($link, $cat, $owner_uid = false) {
1215
1216 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
f295c368 1217
bba7c4bf 1218 if ($cat >= 0) {
18664970 1219
bba7c4bf
AD
1220 if ($cat != 0) {
1221 $cat_query = "cat_id = '$cat'";
1222 } else {
1223 $cat_query = "cat_id IS NULL";
1224 }
14073c0a 1225
8d505d78 1226 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
b6d486a3 1227 AND owner_uid = " . $owner_uid);
8d505d78 1228
bba7c4bf
AD
1229 $cat_feeds = array();
1230 while ($line = db_fetch_assoc($result)) {
1231 array_push($cat_feeds, "feed_id = " . $line["id"]);
1232 }
8d505d78 1233
bba7c4bf 1234 if (count($cat_feeds) == 0) return 0;
8d505d78 1235
bba7c4bf 1236 $match_part = implode(" OR ", $cat_feeds);
8d505d78
AD
1237
1238 $result = db_query($link, "SELECT COUNT(int_id) AS unread
687bb90d
AD
1239 FROM ttrss_user_entries
1240 WHERE unread = true AND ($match_part)
1241 AND owner_uid = " . $owner_uid);
8d505d78 1242
bba7c4bf 1243 $unread = 0;
8d505d78 1244
bba7c4bf
AD
1245 # this needs to be rewritten
1246 while ($line = db_fetch_assoc($result)) {
1247 $unread += $line["unread"];
1248 }
8d505d78 1249
bba7c4bf
AD
1250 return $unread;
1251 } else if ($cat == -1) {
59e15af4 1252 return getFeedUnread($link, -1) + getFeedUnread($link, -2) + getFeedUnread($link, -3) + getFeedUnread($link, 0);
bba7c4bf 1253 } else if ($cat == -2) {
f295c368 1254
b2531a28 1255 $result = db_query($link, "
8d505d78 1256 SELECT COUNT(unread) AS unread FROM
687bb90d
AD
1257 ttrss_user_entries, ttrss_user_labels2
1258 WHERE article_id = ref_id AND unread = true
b2531a28 1259 AND ttrss_user_entries.owner_uid = '$owner_uid'");
ceb30ba4 1260
b2531a28 1261 $unread = db_fetch_result($result, 0, "unread");
f295c368 1262
b2531a28 1263 return $unread;
f295c368 1264
8d505d78 1265 }
f295c368
AD
1266 }
1267
1268 function getFeedUnread($link, $feed, $is_cat = false) {
2627f2d0 1269 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
bdb7369b
AD
1270 }
1271
ceb30ba4
AD
1272 function getLabelUnread($link, $label_id, $owner_uid = false) {
1273 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1274
f360b028
AD
1275 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1276 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
ceb30ba4
AD
1277
1278 if (db_num_rows($result) != 0) {
1279 return db_fetch_result($result, 0, "unread");
1280 } else {
1281 return 0;
1282 }
1283 }
1284
2627f2d0
AD
1285 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1286 $owner_uid = false) {
1287
22fdebff 1288 $n_feed = (int) $feed;
687bb90d 1289 $need_entries = false;
f295c368 1290
2627f2d0
AD
1291 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1292
bdb7369b
AD
1293 if ($unread_only) {
1294 $unread_qpart = "unread = true";
1295 } else {
1296 $unread_qpart = "true";
1297 }
1298
f295c368 1299 if ($is_cat) {
8d505d78 1300 return getCategoryUnread($link, $n_feed, $owner_uid);
5417fbd7
AD
1301 } else if ($n_feed == -6) {
1302 return 0;
1303 } else if ($feed != "0" && $n_feed == 0) {
326469fc 1304
3972bf59 1305 $feed = db_escape_string($link, $feed);
c5701e70 1306
326469fc 1307 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
8d505d78 1308 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
687bb90d 1309 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
326469fc
AD
1310 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1311 return db_fetch_result($result, 0, "count");
1312
f295c368 1313 } else if ($n_feed == -1) {
a9cb1f83 1314 $match_part = "marked = true";
e4f4b46f
AD
1315 } else if ($n_feed == -2) {
1316 $match_part = "published = true";
2d24f032 1317 } else if ($n_feed == -3) {
cd2cc43d 1318 $match_part = "unread = true AND score >= 0";
2d24f032 1319
b71e188e 1320 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
c1d7e6c3 1321
2d24f032 1322 if (DB_TYPE == "pgsql") {
8d505d78 1323 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
2d24f032 1324 } else {
7608b38a 1325 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2d24f032 1326 }
687bb90d
AD
1327
1328 $need_entries = true;
1329
b2531a28
AD
1330 } else if ($n_feed == -4) {
1331 $match_part = "true";
e04c18a2 1332 } else if ($n_feed >= 0) {
831ff047 1333
6e63a7c3
AD
1334 if ($n_feed != 0) {
1335 $match_part = "feed_id = '$n_feed'";
831ff047 1336 } else {
6e63a7c3 1337 $match_part = "feed_id IS NULL";
831ff047 1338 }
6e63a7c3 1339
f822a8e5 1340 } else if ($feed < LABEL_BASE_INDEX) {
318260cc 1341
f822a8e5 1342 $label_id = feed_to_label_id($feed);
a9cb1f83 1343
ceb30ba4 1344 return getLabelUnread($link, $label_id, $owner_uid);
a9cb1f83 1345
a9cb1f83
AD
1346 }
1347
1348 if ($match_part) {
e04c18a2 1349
687bb90d 1350 if ($need_entries) {
e04c18a2 1351 $from_qpart = "ttrss_user_entries,ttrss_entries";
687bb90d
AD
1352 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1353 } else {
1354 $from_qpart = "ttrss_user_entries";
e04c18a2
AD
1355 }
1356
8d505d78 1357 $query = "SELECT count(int_id) AS unread
e04c18a2 1358 FROM $from_qpart WHERE
687bb90d
AD
1359 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1360
1361 //echo "[$feed/$query]\n";
dbfc4365
AD
1362
1363 $result = db_query($link, $query);
8d505d78 1364
a9cb1f83 1365 } else {
8d505d78 1366
a9cb1f83 1367 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
8d505d78
AD
1368 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1369 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
687bb90d 1370 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
a9cb1f83 1371 }
8d505d78 1372
a9cb1f83 1373 $unread = db_fetch_result($result, 0, "unread");
cfb02131 1374
a9cb1f83
AD
1375 return $unread;
1376 }
1377
f3acc32e
AD
1378 function getGlobalUnread($link, $user_id = false) {
1379
1380 if (!$user_id) {
1381 $user_id = $_SESSION["uid"];
1382 }
1383
8a4c759e
AD
1384 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1385 WHERE owner_uid = '$user_id' AND feed_id > 0");
1386
8d505d78 1387 $c_id = db_fetch_result($result, 0, "c_id");
8a4c759e 1388
a9cb1f83
AD
1389 return $c_id;
1390 }
1391
1392 function getGlobalCounters($link, $global_unread = -1) {
6a7817c1
AD
1393 $ret_arr = array();
1394
8d505d78 1395 if ($global_unread == -1) {
a9cb1f83
AD
1396 $global_unread = getGlobalUnread($link);
1397 }
6a7817c1 1398
8d505d78 1399 $cv = array("id" => "global-unread",
12e6de72 1400 "counter" => (int) $global_unread);
6a7817c1
AD
1401
1402 array_push($ret_arr, $cv);
7bf7e4d3 1403
8d505d78 1404 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
7bf7e4d3
AD
1405 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1406
1407 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1408
8d505d78 1409 $cv = array("id" => "subscribed-feeds",
12e6de72 1410 "counter" => (int) $subscribed_feeds);
7bf7e4d3 1411
6a7817c1
AD
1412 array_push($ret_arr, $cv);
1413
1414 return $ret_arr;
a9cb1f83
AD
1415 }
1416
6a7817c1 1417 function getVirtCounters($link) {
a9cb1f83 1418
ef393de7 1419 $ret_arr = array();
bdb7369b 1420
e04c18a2 1421 for ($i = 0; $i >= -4; $i--) {
bdb7369b 1422
ceb30ba4 1423 $count = getFeedUnread($link, $i);
6a7817c1
AD
1424
1425 $cv = array("id" => $i,
12e6de72 1426 "counter" => (int) $count);
8d505d78 1427
296c8134
AD
1428// if (get_pref($link, 'EXTENDED_FEEDLIST'))
1429// $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
bdb7369b 1430
6a7817c1 1431 array_push($ret_arr, $cv);
8d505d78 1432 }
0a6e5382 1433
a413f53e
AD
1434 global $pluginhost;
1435
1436 if ($pluginhost) {
1437 $feeds = $pluginhost->get_feeds(-1);
1438
0196ef94
AD
1439 if (is_array($feeds)) {
1440 foreach ($feeds as $feed) {
1441 $cv = array("id" => PluginHost::pfeed_to_feed_id($feed['id']),
1442 "counter" => $feed['sender']->get_unread($feed['id']));
a413f53e 1443
0196ef94
AD
1444 array_push($ret_arr, $cv);
1445 }
a413f53e 1446 }
a413f53e
AD
1447 }
1448
0a6e5382
AD
1449 return $ret_arr;
1450 }
1451
11232703 1452 function getLabelCounters($link, $descriptions = false) {
6a7817c1
AD
1453
1454 $ret_arr = array();
0a6e5382 1455
3809b278 1456 $owner_uid = $_SESSION["uid"];
bdb7369b 1457
45942238
AD
1458 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1459 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1460 (ttrss_labels2.id = label_id)
1461 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
123a7643
AD
1462 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1463 ttrss_labels2.caption");
8d505d78 1464
3809b278 1465 while ($line = db_fetch_assoc($result)) {
2d24f032 1466
f822a8e5 1467 $id = label_to_feed_id($line["id"]);
e4f4b46f 1468
3809b278 1469 $label_name = $line["caption"];
45942238 1470 $count = $line["unread"];
3809b278 1471
6a7817c1 1472 $cv = array("id" => $id,
12e6de72 1473 "counter" => (int) $count);
11232703
AD
1474
1475 if ($descriptions)
1476 $cv["description"] = $label_name;
a9cb1f83 1477
296c8134
AD
1478// if (get_pref($link, 'EXTENDED_FEEDLIST'))
1479// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
ef393de7 1480
6a7817c1 1481 array_push($ret_arr, $cv);
3809b278 1482 }
8d505d78 1483
ef393de7 1484 return $ret_arr;
a9cb1f83
AD
1485 }
1486
3809b278 1487 function getFeedCounters($link, $active_feed = false) {
a9cb1f83 1488
6a7817c1
AD
1489 $ret_arr = array();
1490
41759103 1491 $query = "SELECT ttrss_feeds.id,
8a4c759e 1492 ttrss_feeds.title,
8d505d78 1493 ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated,
de0a2122
AD
1494 last_error, value AS count
1495 FROM ttrss_feeds, ttrss_counters_cache
8d505d78 1496 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
fc9de939 1497 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
55e01d7e 1498 AND ttrss_counters_cache.feed_id = id";
a9cb1f83 1499
14073c0a 1500 $result = db_query($link, $query);
a9cb1f83
AD
1501 $fctrs_modified = false;
1502
1503 while ($line = db_fetch_assoc($result)) {
8d505d78 1504
a9cb1f83 1505 $id = $line["id"];
de0a2122 1506 $count = $line["count"];
a9cb1f83 1507 $last_error = htmlspecialchars($line["last_error"]);
fb1fb4ab 1508
324944f3 1509 $last_updated = make_local_datetime($link, $line['last_updated'], false);
fb1fb4ab 1510
7defa089 1511 $has_img = feed_has_icon($id);
a9cb1f83 1512
428b704d
AD
1513 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1514 $last_updated = '';
1515
6a7817c1 1516 $cv = array("id" => $id,
21884958 1517 "updated" => $last_updated,
12e6de72 1518 "counter" => (int) $count,
6a7817c1 1519 "has_img" => (int) $has_img);
a9cb1f83 1520
6a7817c1
AD
1521 if ($last_error)
1522 $cv["error"] = $last_error;
4ffa126e 1523
296c8134
AD
1524// if (get_pref($link, 'EXTENDED_FEEDLIST'))
1525// $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
bdb7369b 1526
6a7817c1 1527 if ($active_feed && $id == $active_feed)
fbc95c5b 1528 $cv["title"] = truncate_string($line["title"], 30);
6a7817c1
AD
1529
1530 array_push($ret_arr, $cv);
a9cb1f83 1531
a9cb1f83 1532 }
6a7817c1
AD
1533
1534 return $ret_arr;
a9cb1f83
AD
1535 }
1536
6e7f8d26
AD
1537 function get_pgsql_version($link) {
1538 $result = db_query($link, "SELECT version() AS version");
9949bd15 1539 $version = explode(" ", db_fetch_result($result, 0, "version"));
6e7f8d26
AD
1540 return $version[1];
1541 }
1542
2b8290cd 1543 /**
23d2471c
AD
1544 * @return array (code => Status code, message => error message if available)
1545 *
2b8290cd
CW
1546 * 0 - OK, Feed already exists
1547 * 1 - OK, Feed added
1548 * 2 - Invalid URL
9a8ce956
CW
1549 * 3 - URL content is HTML, no feeds available
1550 * 4 - URL content is HTML which contains multiple feeds.
1551 * Here you should call extractfeedurls in rpc-backend
1552 * to get all possible feeds.
5414ad4c 1553 * 5 - Couldn't download the URL content.
2b8290cd 1554 */
8d505d78 1555 function subscribe_to_feed($link, $url, $cat_id = 0,
efc6553d 1556 $auth_login = '', $auth_pass = '') {
bb0f29a4 1557
23d2471c
AD
1558 global $fetch_last_error;
1559
2c08214a
AD
1560 require_once "include/rssfuncs.php";
1561
f0266f51 1562 $url = fix_url($url);
ec39a02c 1563
23d2471c 1564 if (!$url || !validate_feed_url($url)) return array("code" => 2);
a5819bb3 1565
759e5132
AD
1566 $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass);
1567
1568 if (!$contents) {
304aadb9 1569 return array("code" => 5, "message" => $fetch_last_error);
759e5132
AD
1570 }
1571
1572 if (is_html($contents)) {
1573 $feedUrls = get_feeds_from_html($url, $contents);
304aadb9 1574
304aadb9
AD
1575 if (count($feedUrls) == 0) {
1576 return array("code" => 3);
1577 } else if (count($feedUrls) > 1) {
759e5132 1578 return array("code" => 4, "feeds" => $feedUrls);
f6d8345b 1579 }
304aadb9
AD
1580 //use feed url as new URL
1581 $url = key($feedUrls);
1582 }
f6d8345b 1583
956c7629
AD
1584 if ($cat_id == "0" || !$cat_id) {
1585 $cat_qpart = "NULL";
1586 } else {
1587 $cat_qpart = "'$cat_id'";
1588 }
8d505d78 1589
956c7629 1590 $result = db_query($link,
8d505d78 1591 "SELECT id FROM ttrss_feeds
a5819bb3 1592 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
8d505d78 1593
956c7629 1594 if (db_num_rows($result) == 0) {
956c7629 1595 $result = db_query($link,
8d505d78
AD
1596 "INSERT INTO ttrss_feeds
1597 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1598 VALUES ('".$_SESSION["uid"]."', '$url',
19b3992b 1599 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
8d505d78 1600
956c7629 1601 $result = db_query($link,
8d505d78 1602 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
f27de515 1603 AND owner_uid = " . $_SESSION["uid"]);
8d505d78 1604
956c7629 1605 $feed_id = db_fetch_result($result, 0, "id");
8d505d78 1606
956c7629 1607 if ($feed_id) {
c633e370 1608 update_rss_feed($link, $feed_id, true);
956c7629
AD
1609 }
1610
23d2471c 1611 return array("code" => 1);
956c7629 1612 } else {
23d2471c 1613 return array("code" => 0);
956c7629
AD
1614 }
1615 }
1616
8d505d78 1617 function print_feed_select($link, $id, $default_id = "",
4c9d0490
AD
1618 $attributes = "", $include_all_feeds = true,
1619 $root_id = false, $nest_level = 0) {
1620
1621 if (!$root_id) {
1622 print "<select id=\"$id\" name=\"$id\" $attributes>";
1623 if ($include_all_feeds) {
1624 $is_selected = ("0" == $default_id) ? "selected=\"1\"" : "";
1625 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1626 }
673d54ca 1627 }
8d505d78 1628
4c9d0490 1629 if (get_pref($link, 'ENABLE_FEED_CATS')) {
673d54ca 1630
4c9d0490
AD
1631 if ($root_id)
1632 $parent_qpart = "parent_cat = '$root_id'";
1633 else
1634 $parent_qpart = "parent_cat IS NULL";
673d54ca 1635
4c9d0490
AD
1636 $result = db_query($link, "SELECT id,title,
1637 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1638 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1639 FROM ttrss_feed_categories
1640 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1641
1642 while ($line = db_fetch_assoc($result)) {
1643
1644 for ($i = 0; $i < $nest_level; $i++)
1645 $line["title"] = " - " . $line["title"];
1646
1647 $is_selected = ("CAT:".$line["id"] == $default_id) ? "selected=\"1\"" : "";
1648
1649 printf("<option $is_selected value='CAT:%d'>%s</option>",
1650 $line["id"], htmlspecialchars($line["title"]));
1651
1652 if ($line["num_children"] > 0)
1653 print_feed_select($link, $id, $default_id, $attributes,
1654 $include_all_feeds, $line["id"], $nest_level+1);
1655
1656 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1657 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1658
1659 while ($fline = db_fetch_assoc($feed_result)) {
1660 $is_selected = ($fline["id"] == $default_id) ? "selected=\"1\"" : "";
1661
1662 $fline["title"] = " + " . $fline["title"];
1663
1664 for ($i = 0; $i < $nest_level; $i++)
1665 $fline["title"] = " - " . $fline["title"];
1666
1667 printf("<option $is_selected value='%d'>%s</option>",
1668 $fline["id"], htmlspecialchars($fline["title"]));
1669 }
673d54ca 1670 }
b1710666 1671
4c9d0490
AD
1672 if (!$root_id) {
1673 $is_selected = ($default_id == "CAT:0") ? "selected=\"1\"" : "";
1674
1675 printf("<option $is_selected value='CAT:0'>%s</option>",
1676 __("Uncategorized"));
1677
1678 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1679 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1680
1681 while ($fline = db_fetch_assoc($feed_result)) {
1682 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ? "selected=\"1\"" : "";
1683
1684 $fline["title"] = " + " . $fline["title"];
1685
1686 for ($i = 0; $i < $nest_level; $i++)
1687 $fline["title"] = " - " . $fline["title"];
1688
1689 printf("<option $is_selected value='%d'>%s</option>",
1690 $fline["id"], htmlspecialchars($fline["title"]));
1691 }
1692 }
b1710666 1693
4c9d0490
AD
1694 } else {
1695 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1696 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1697
1698 while ($line = db_fetch_assoc($result)) {
1699
1700 $is_selected = ($line["id"] == $default_id) ? "selected=\"1\"" : "";
1701
1702 printf("<option $is_selected value='%d'>%s</option>",
1703 $line["id"], htmlspecialchars($line["title"]));
1704 }
673d54ca 1705 }
8d505d78 1706
4c9d0490
AD
1707 if (!$root_id) {
1708 print "</select>";
1709 }
673d54ca
AD
1710 }
1711
fbf85cf6
AD
1712 function print_feed_cat_select($link, $id, $default_id,
1713 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
8d505d78 1714
fbf85cf6
AD
1715 if (!$root_id) {
1716 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1717 }
673d54ca 1718
fbf85cf6
AD
1719 if ($root_id)
1720 $parent_qpart = "parent_cat = '$root_id'";
1721 else
1722 $parent_qpart = "parent_cat IS NULL";
673d54ca 1723
fbf85cf6
AD
1724 $result = db_query($link, "SELECT id,title,
1725 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1726 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1727 FROM ttrss_feed_categories
1728 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
673d54ca 1729
fbf85cf6
AD
1730 while ($line = db_fetch_assoc($result)) {
1731 if ($line["id"] == $default_id) {
1732 $is_selected = "selected=\"1\"";
1733 } else {
1734 $is_selected = "";
1735 }
673d54ca 1736
fbf85cf6
AD
1737 for ($i = 0; $i < $nest_level; $i++)
1738 $line["title"] = " - " . $line["title"];
c00907f2 1739
fbf85cf6
AD
1740 if ($line["title"])
1741 printf("<option $is_selected value='%d'>%s</option>",
1742 $line["id"], htmlspecialchars($line["title"]));
673d54ca 1743
fbf85cf6
AD
1744 if ($line["num_children"] > 0)
1745 print_feed_cat_select($link, $id, $default_id, $attributes,
1746 $include_all_cats, $line["id"], $nest_level+1);
1747 }
5c7c7da9 1748
fbf85cf6
AD
1749 if (!$root_id) {
1750 if ($include_all_cats) {
1751 if (db_num_rows($result) > 0) {
1752 print "<option disabled=\"1\">--------</option>";
1753 }
7e18f8e7
AD
1754
1755 if ($default_id == 0) {
1756 $is_selected = "selected=\"1\"";
1757 } else {
1758 $is_selected = "";
1759 }
1760
1761 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
fbf85cf6
AD
1762 }
1763 print "</select>";
1764 }
1765 }
8d505d78 1766
14f69488
AD
1767 function checkbox_to_sql_bool($val) {
1768 return ($val == "on") ? "true" : "false";
1769 }
86b682ce
AD
1770
1771 function getFeedCatTitle($link, $id) {
1772 if ($id == -1) {
d1db26aa 1773 return __("Special");
f822a8e5 1774 } else if ($id < LABEL_BASE_INDEX) {
d1db26aa 1775 return __("Labels");
86b682ce 1776 } else if ($id > 0) {
8d505d78 1777 $result = db_query($link, "SELECT ttrss_feed_categories.title
86b682ce
AD
1778 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1779 cat_id = ttrss_feed_categories.id");
1780 if (db_num_rows($result) == 1) {
1781 return db_fetch_result($result, 0, "title");
1782 } else {
d1db26aa 1783 return __("Uncategorized");
86b682ce
AD
1784 }
1785 } else {
1786 return "getFeedCatTitle($id) failed";
1787 }
1788
1789 }
1790
9299102f 1791 function getFeedIcon($id) {
af88c48a 1792 switch ($id) {
4bee8b5f
AD
1793 case 0:
1794 return "images/archive.png";
1795 break;
af88c48a 1796 case -1:
c2167866 1797 return "images/mark_set.svg";
af88c48a
AD
1798 break;
1799 case -2:
c2167866 1800 return "images/pub_set.svg";
af88c48a
AD
1801 break;
1802 case -3:
1803 return "images/fresh.png";
1804 break;
1805 case -4:
1806 return "images/tag.png";
1807 break;
5417fbd7
AD
1808 case -6:
1809 return "images/recently_read.png";
1810 break;
af88c48a 1811 default:
f822a8e5 1812 if ($id < LABEL_BASE_INDEX) {
4bee8b5f
AD
1813 return "images/label.png";
1814 } else {
8d505d78 1815 if (file_exists(ICONS_DIR . "/$id.ico"))
e2eda979 1816 return ICONS_URL . "/$id.ico";
4bee8b5f 1817 }
af88c48a
AD
1818 break;
1819 }
1820 }
1821
fd994f1a
AD
1822 function getFeedTitle($link, $id, $cat = false) {
1823 if ($cat) {
8add44ec 1824 return getCategoryTitle($link, $id);
fd994f1a 1825 } else if ($id == -1) {
d1db26aa 1826 return __("Starred articles");
945c243e
AD
1827 } else if ($id == -2) {
1828 return __("Published articles");
2d24f032
AD
1829 } else if ($id == -3) {
1830 return __("Fresh articles");
b2531a28
AD
1831 } else if ($id == -4) {
1832 return __("All articles");
80db1113 1833 } else if ($id === 0 || $id === "0") {
e04c18a2 1834 return __("Archived articles");
5417fbd7
AD
1835 } else if ($id == -6) {
1836 return __("Recently read");
f822a8e5
AD
1837 } else if ($id < LABEL_BASE_INDEX) {
1838 $label_id = feed_to_label_id($id);
ceb30ba4 1839 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
86b682ce 1840 if (db_num_rows($result) == 1) {
ceb30ba4 1841 return db_fetch_result($result, 0, "caption");
86b682ce
AD
1842 } else {
1843 return "Unknown label ($label_id)";
1844 }
1845
147f5632 1846 } else if (is_numeric($id) && $id > 0) {
86b682ce
AD
1847 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1848 if (db_num_rows($result) == 1) {
1849 return db_fetch_result($result, 0, "title");
1850 } else {
1851 return "Unknown feed ($id)";
1852 }
1853 } else {
22fdebff 1854 return $id;
86b682ce 1855 }
86b682ce 1856 }
3dd46f19 1857
d8221301 1858 function make_init_params($link) {
f1f3a642 1859 $params = array();
c9268ed5 1860
f1f3a642
AD
1861 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1862 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
f17cac6b 1863 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
30b6ee8c 1864 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
40496720 1865
c4f7ba80 1866 $params[strtolower($param)] = (int) get_pref($link, $param);
f1f3a642 1867 }
40496720 1868
c4f7ba80
AD
1869 $params["icons_url"] = ICONS_URL;
1870 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
1871 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1872 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1873 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
c4f7ba80 1874 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
88a41b64 1875 $params["label_base_index"] = (int) LABEL_BASE_INDEX;
59b223d7 1876
8cd576a1 1877 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
9b7ecc0a
AD
1878 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1879
8cd576a1
AD
1880 $max_feed_id = db_fetch_result($result, 0, "mid");
1881 $num_feeds = db_fetch_result($result, 0, "nf");
9b7ecc0a 1882
8cd576a1 1883 $params["max_feed_id"] = (int) $max_feed_id;
c4f7ba80 1884 $params["num_feeds"] = (int) $num_feeds;
8cd576a1 1885
c4f7ba80 1886 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
b8cb4d08 1887 $params["hotkeys"] = get_hotkeys_map($link);
9b7ecc0a 1888
8484ce22 1889 $params["csrf_token"] = $_SESSION["csrf_token"];
f03701fe 1890 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
8484ce22 1891
6b1a4ecd 1892 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
8b83bf5f 1893
d8221301 1894 return $params;
3ac2b520 1895 }
f54f515f 1896
b8cb4d08
AD
1897 function get_hotkeys_info($link) {
1898 $hotkeys = array(
1899 __("Navigation") => array(
1900 "next_feed" => __("Open next feed"),
1901 "prev_feed" => __("Open previous feed"),
1902 "next_article" => __("Open next article"),
1903 "prev_article" => __("Open previous article"),
c22580b5
AD
1904 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1905 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
b8cb4d08
AD
1906 "search_dialog" => __("Show search dialog")),
1907 __("Article") => array(
1908 "toggle_mark" => __("Toggle starred"),
1909 "toggle_publ" => __("Toggle published"),
1910 "toggle_unread" => __("Toggle unread"),
1911 "edit_tags" => __("Edit tags"),
1912 "dismiss_selected" => __("Dismiss selected"),
1913 "dismiss_read" => __("Dismiss read"),
1914 "open_in_new_window" => __("Open in new window"),
1915 "catchup_below" => __("Mark below as read"),
1916 "catchup_above" => __("Mark above as read"),
1917 "article_scroll_down" => __("Scroll down"),
1918 "article_scroll_up" => __("Scroll up"),
1919 "select_article_cursor" => __("Select article under cursor"),
1bcf8f45 1920 "email_article" => __("Email article"),
414191d4 1921 "close_article" => __("Close/collapse article"),
2ccc7b8e
AD
1922 "toggle_widescreen" => __("Toggle widescreen mode"),
1923 "toggle_embed_original" => __("Toggle embed original")),
b8cb4d08
AD
1924 __("Article selection") => array(
1925 "select_all" => __("Select all articles"),
1926 "select_unread" => __("Select unread"),
1927 "select_marked" => __("Select starred"),
1928 "select_published" => __("Select published"),
1929 "select_invert" => __("Invert selection"),
1930 "select_none" => __("Deselect everything")),
1931 __("Feed") => array(
1932 "feed_refresh" => __("Refresh current feed"),
1933 "feed_unhide_read" => __("Un/hide read feeds"),
1934 "feed_subscribe" => __("Subscribe to feed"),
1935 "feed_edit" => __("Edit feed"),
1936 "feed_catchup" => __("Mark as read"),
1937 "feed_reverse" => __("Reverse headlines"),
43f775de 1938 "feed_debug_update" => __("Debug feed update"),
b8cb4d08 1939 "catchup_all" => __("Mark all feeds as read"),
4b27f0c0 1940 "cat_toggle_collapse" => __("Un/collapse current category"),
60b88b25
AD
1941 "toggle_combined_mode" => __("Toggle combined mode"),
1942 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
b8cb4d08
AD
1943 __("Go to") => array(
1944 "goto_all" => __("All articles"),
1945 "goto_fresh" => __("Fresh"),
1946 "goto_marked" => __("Starred"),
1947 "goto_published" => __("Published"),
1948 "goto_tagcloud" => __("Tag cloud"),
1949 "goto_prefs" => __("Preferences")),
1950 __("Other") => array(
1951 "create_label" => __("Create label"),
1952 "create_filter" => __("Create filter"),
1953 "collapse_sidebar" => __("Un/collapse sidebar"),
1954 "help_dialog" => __("Show help dialog"))
1955 );
1956
47854200
DA
1957 global $pluginhost;
1958 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_INFO) as $plugin) {
1959 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
1960 }
1961
b8cb4d08
AD
1962 return $hotkeys;
1963 }
1964
1965 function get_hotkeys_map($link) {
a83b58f1 1966 $hotkeys = array(
e218c5f5
AD
1967// "navigation" => array(
1968 "k" => "next_feed",
1969 "j" => "prev_feed",
1970 "n" => "next_article",
1971 "p" => "prev_article",
e5e2cf3b
AD
1972 "(38)|up" => "prev_article",
1973 "(40)|down" => "next_article",
da15c140
AD
1974// "^(38)|Ctrl-up" => "prev_article_noscroll",
1975// "^(40)|Ctrl-down" => "next_article_noscroll",
e5e2cf3b 1976 "(191)|/" => "search_dialog",
e218c5f5
AD
1977// "article" => array(
1978 "s" => "toggle_mark",
5b18c936 1979 "*s" => "toggle_publ",
e218c5f5 1980 "u" => "toggle_unread",
5b18c936
AD
1981 "*t" => "edit_tags",
1982 "*d" => "dismiss_selected",
1983 "*x" => "dismiss_read",
e218c5f5
AD
1984 "o" => "open_in_new_window",
1985 "c p" => "catchup_below",
1986 "c n" => "catchup_above",
5b18c936
AD
1987 "*n" => "article_scroll_down",
1988 "*p" => "article_scroll_up",
d2db81a5
AD
1989 "*(38)|Shift+up" => "article_scroll_up",
1990 "*(40)|Shift+down" => "article_scroll_down",
5b18c936 1991 "a *w" => "toggle_widescreen",
2ccc7b8e 1992 "a e" => "toggle_embed_original",
e218c5f5 1993 "e" => "email_article",
2cda4314 1994 "a q" => "close_article",
e218c5f5
AD
1995// "article_selection" => array(
1996 "a a" => "select_all",
1997 "a u" => "select_unread",
5b18c936 1998 "a *u" => "select_marked",
e218c5f5
AD
1999 "a p" => "select_published",
2000 "a i" => "select_invert",
2001 "a n" => "select_none",
2002// "feed" => array(
2003 "f r" => "feed_refresh",
2004 "f a" => "feed_unhide_read",
2005 "f s" => "feed_subscribe",
2006 "f e" => "feed_edit",
2007 "f q" => "feed_catchup",
2008 "f x" => "feed_reverse",
5b18c936
AD
2009 "f *d" => "feed_debug_update",
2010 "f *c" => "toggle_combined_mode",
60b88b25 2011 "f c" => "toggle_cdm_expanded",
5b18c936 2012 "*q" => "catchup_all",
e218c5f5
AD
2013 "x" => "cat_toggle_collapse",
2014// "goto" => array(
2015 "g a" => "goto_all",
2016 "g f" => "goto_fresh",
2017 "g s" => "goto_marked",
2018 "g p" => "goto_published",
2019 "g t" => "goto_tagcloud",
5b18c936 2020 "g *p" => "goto_prefs",
e218c5f5 2021// "other" => array(
3fb40112 2022 "(9)|Tab" => "select_article_cursor", // tab
e218c5f5
AD
2023 "c l" => "create_label",
2024 "c f" => "create_filter",
2025 "c s" => "collapse_sidebar",
3fb40112 2026 "^(191)|Ctrl+/" => "help_dialog",
a83b58f1
AD
2027 );
2028
da15c140
AD
2029 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2030 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2031 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2032 }
2033
e218c5f5
AD
2034 global $pluginhost;
2035 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP) as $plugin) {
2036 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2037 }
2038
2039 $prefixes = array();
2040
2041 foreach (array_keys($hotkeys) as $hotkey) {
2042 $pair = explode(" ", $hotkey, 2);
2043
2044 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2045 array_push($prefixes, $pair[0]);
2046 }
2047 }
2048
2049 return array($prefixes, $hotkeys);
a83b58f1
AD
2050 }
2051
c4f7ba80 2052 function make_runtime_info($link) {
8cd576a1
AD
2053 $data = array();
2054
2055 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
9b7ecc0a
AD
2056 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2057
8cd576a1
AD
2058 $max_feed_id = db_fetch_result($result, 0, "mid");
2059 $num_feeds = db_fetch_result($result, 0, "nf");
9b7ecc0a 2060
8cd576a1
AD
2061 $data["max_feed_id"] = (int) $max_feed_id;
2062 $data["num_feeds"] = (int) $num_feeds;
c4f7ba80 2063
f8fb4498 2064 $data['last_article_id'] = getLastArticleId($link);
5ae8f858 2065 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
f8fb4498 2066
16314dda 2067 $data['dep_ts'] = calculate_dep_timestamp();
4cdb8173 2068 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
16314dda 2069
dbaa4e4a 2070 if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
c4f7ba80
AD
2071
2072 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
8e00ae9b 2073
9041f58b 2074 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
8e00ae9b 2075
fb074239 2076 $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
fbae93d8 2077
8e00ae9b 2078 if ($stamp) {
9041f58b
AD
2079 $stamp_delta = time() - $stamp;
2080
2081 if ($stamp_delta > 1800) {
f6854e44 2082 $stamp_check = 0;
8e00ae9b 2083 } else {
f6854e44
AD
2084 $stamp_check = 1;
2085 $_SESSION["daemon_stamp_check"] = time();
8e00ae9b
AD
2086 }
2087
c4f7ba80 2088 $data['daemon_stamp_ok'] = $stamp_check;
f6854e44 2089
8e00ae9b
AD
2090 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2091
c4f7ba80 2092 $data['daemon_stamp'] = $stamp_fmt;
8e00ae9b 2093 }
8e00ae9b 2094 }
71ad883b 2095 }
8e00ae9b 2096
63855db1 2097 if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
fb074239 2098 $new_version_details = @check_for_update($link);
d9fa39f1 2099
63855db1 2100 $data['new_version_available'] = (int) ($new_version_details != false);
d9fa39f1
AD
2101
2102 $_SESSION["last_version_check"] = time();
27211afe 2103 $_SESSION["version_data"] = $new_version_details;
d9fa39f1
AD
2104 }
2105
c4f7ba80 2106 return $data;
f54f515f 2107 }
ef393de7 2108
ca5d9be4 2109 function search_to_sql($link, $search) {
ef393de7 2110
88040f57 2111 $search_query_part = "";
e20c9d88 2112
9949bd15 2113 $keywords = explode(" ", $search);
88040f57 2114 $query_keywords = array();
e20c9d88 2115
ab4b768f
AD
2116 foreach ($keywords as $k) {
2117 if (strpos($k, "-") === 0) {
2118 $k = substr($k, 1);
2119 $not = "NOT";
2120 } else {
2121 $not = "";
88040f57 2122 }
e20c9d88 2123
9949bd15 2124 $commandpair = explode(":", mb_strtolower($k), 2);
53003548
AD
2125
2126 if ($commandpair[0] == "note" && $commandpair[1]) {
2127
2128 if ($commandpair[1] == "true")
2129 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2130 else
2131 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2132
2133 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2134
2135 if ($commandpair[1] == "true")
2136 array_push($query_keywords, "($not (marked = true))");
2137 else
2138 array_push($query_keywords, "($not (marked = false))");
2139
2140 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2141
2142 if ($commandpair[1] == "true")
2143 array_push($query_keywords, "($not (published = true))");
2144 else
2145 array_push($query_keywords, "($not (published = false))");
2146
2147 } else if (strpos($k, "@") === 0) {
e20c9d88 2148
ab4b768f
AD
2149 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2150 $orig_ts = strtotime(substr($k, 1));
ab4b768f 2151 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
8d505d78 2152
53003548
AD
2153 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2154
ab4b768f 2155 array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
ca5d9be4 2156 } else {
ab4b768f
AD
2157 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2158 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
88040f57
AD
2159 }
2160 }
2161
2162 $search_query_part = implode("AND", $query_keywords);
2163
2164 return $search_query_part;
2165 }
2166
67bd0b1f
AD
2167 function getParentCategories($link, $cat, $owner_uid) {
2168 $rv = array();
2169
2170 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2171 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2172
2173 while ($line = db_fetch_assoc($result)) {
2174 array_push($rv, $line["parent_cat"]);
2175 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2176 }
2177
2178 return $rv;
2179 }
2180
6d8d00e8
AD
2181 function getChildCategories($link, $cat, $owner_uid) {
2182 $rv = array();
2183
2184 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2185 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2186
2187 while ($line = db_fetch_assoc($result)) {
2188 array_push($rv, $line["id"]);
2189 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2190 }
2191
2192 return $rv;
2193 }
147f5632 2194
ca5d9be4 2195 function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false, $ignore_vfeed_group = false) {
c36bf4d5
AD
2196
2197 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
c1a0b534 2198
c3fddd05
AD
2199 $ext_tables_part = "";
2200
88040f57 2201 if ($search) {
e4f7f8df
AD
2202
2203 if (SPHINX_ENABLED) {
2204 $ids = join(",", @sphinx_search($search, 0, 500));
2205
8d505d78 2206 if ($ids)
e4f7f8df
AD
2207 $search_query_part = "ref_id IN ($ids) AND ";
2208 else
2209 $search_query_part = "ref_id = -1 AND ";
2210
2211 } else {
ca5d9be4 2212 $search_query_part = search_to_sql($link, $search);
e4f7f8df 2213 $search_query_part .= " AND ";
8d505d78 2214 }
e20c9d88 2215
ef393de7
AD
2216 } else {
2217 $search_query_part = "";
2218 }
2219
36184020 2220 if ($filter) {
4e02f582
AD
2221
2222 if (DB_TYPE == "pgsql") {
2223 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2224 } else {
2225 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2226 }
2227
2228 $override_order = "updated DESC";
2229
2680295b 2230 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
dd8c36af
AD
2231
2232 // Try to check if SQL regexp implementation chokes on a valid regexp
809c8e62 2233 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2680295b 2234 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
dd8c36af
AD
2235 WHERE $filter_query_part LIMIT 1", false);
2236
7726063c
AD
2237 if ($result) {
2238 $test = db_fetch_result($result, 0, "true_val");
dd8c36af 2239
7726063c
AD
2240 if (!$test) {
2241 $filter_query_part = "false AND";
2242 } else {
2243 $filter_query_part .= " AND";
2244 }
dd8c36af 2245 } else {
7726063c 2246 $filter_query_part = "false AND";
dd8c36af
AD
2247 }
2248
36184020
AD
2249 } else {
2250 $filter_query_part = "";
2251 }
2252
97e5dbb2
AD
2253 if ($since_id) {
2254 $since_id_part = "ttrss_entries.id > $since_id AND ";
2255 } else {
2256 $since_id_part = "";
2257 }
2258
ef393de7 2259 $view_query_part = "";
8d505d78 2260
9b523c01 2261 if ($view_mode == "adaptive") {
ef393de7
AD
2262 if ($search) {
2263 $view_query_part = " ";
2264 } else if ($feed != -1) {
6d8d00e8 2265
a6adb136 2266 $unread = getFeedUnread($link, $feed, $cat_view);
f4a2f12a 2267
a6adb136
AD
2268 if ($cat_view && $feed > 0 && $include_children)
2269 $unread += getCategoryChildrenUnread($link, $feed);
f4a2f12a 2270
a6adb136
AD
2271 if ($unread > 0)
2272 $view_query_part = " unread = true AND ";
f4a2f12a 2273
ef393de7
AD
2274 }
2275 }
8d505d78 2276
ef393de7
AD
2277 if ($view_mode == "marked") {
2278 $view_query_part = " marked = true AND ";
2279 }
23d72f39 2280
127aaaa0
AD
2281 if ($view_mode == "has_note") {
2282 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2283 }
2284
23d72f39
AD
2285 if ($view_mode == "published") {
2286 $view_query_part = " published = true AND ";
2287 }
2288
0bf65987 2289 if ($view_mode == "unread" && $feed != -6) {
ef393de7
AD
2290 $view_query_part = " unread = true AND ";
2291 }
8b09eac8 2292
ef393de7
AD
2293 if ($limit > 0) {
2294 $limit_query_part = "LIMIT " . $limit;
8d505d78 2295 }
ef393de7 2296
8361e724
AD
2297 $allow_archived = false;
2298
ef393de7 2299 $vfeed_query_part = "";
8d505d78 2300
ef393de7
AD
2301 // override query strategy and enable feed display when searching globally
2302 if ($search && $search_mode == "all_feeds") {
7032f2a5 2303 $query_strategy_part = "true";
8d505d78 2304 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
22fdebff 2305 /* tags */
75c648cf 2306 } else if (!is_numeric($feed)) {
7032f2a5 2307 $query_strategy_part = "true";
ef393de7
AD
2308 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2309 id = feed_id) as feed_title,";
7032f2a5 2310 } else if ($search && $search_mode == "this_cat") {
8d505d78 2311 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
0a6c4846 2312
7032f2a5
AD
2313 if ($feed > 0) {
2314 if ($include_children) {
2315 $subcats = getChildCategories($link, $feed, $owner_uid);
2316 array_push($subcats, $feed);
2317 $cats_qpart = join(",", $subcats);
2318 } else {
2319 $cats_qpart = $feed;
ef393de7 2320 }
8d505d78 2321
7032f2a5 2322 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
8d505d78 2323
ef393de7 2324 } else {
7032f2a5 2325 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
ef393de7 2326 }
8d505d78 2327
e04c18a2 2328 } else if ($feed > 0) {
8d505d78 2329
ef393de7 2330 if ($cat_view) {
5c365f60 2331
ef393de7 2332 if ($feed > 0) {
09101297
AD
2333 if ($include_children) {
2334 # sub-cats
2335 $subcats = getChildCategories($link, $feed, $owner_uid);
2336
7032f2a5
AD
2337 array_push($subcats, $feed);
2338 $query_strategy_part = "cat_id IN (".
09101297 2339 implode(",", $subcats).")";
7032f2a5 2340
6d8d00e8 2341 } else {
09101297 2342 $query_strategy_part = "cat_id = '$feed'";
6d8d00e8
AD
2343 }
2344
ef393de7
AD
2345 } else {
2346 $query_strategy_part = "cat_id IS NULL";
2347 }
8d505d78 2348
ef393de7 2349 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
5c365f60 2350
8d505d78 2351 } else {
6e63a7c3 2352 $query_strategy_part = "feed_id = '$feed'";
ef393de7 2353 }
bfe5ddfc 2354 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
e04c18a2 2355 $query_strategy_part = "feed_id IS NULL";
8361e724 2356 $allow_archived = true;
bfe5ddfc 2357 } else if ($feed == 0 && $cat_view) { // uncategorized
65dd90f2 2358 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
bfe5ddfc 2359 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
ef393de7
AD
2360 } else if ($feed == -1) { // starred virtual feed
2361 $query_strategy_part = "marked = true";
2362 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
835fb294
AD
2363 $allow_archived = true;
2364
1bd7413f 2365 if (!$override_order) {
b9a06a0e 2366 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
1bd7413f 2367 }
7873d588 2368
e6a38cde
AD
2369 } else if ($feed == -2) { // published virtual feed OR labels category
2370
2371 if (!$cat_view) {
2372 $query_strategy_part = "published = true";
2373 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
8361e724 2374 $allow_archived = true;
46b78149 2375
1bd7413f 2376 if (!$override_order) {
b9a06a0e 2377 $override_order = "last_published DESC, date_entered DESC, updated DESC";
1bd7413f
AD
2378 }
2379
e6a38cde
AD
2380 } else {
2381 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2382
2383 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
8d505d78 2384
e6a38cde
AD
2385 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2386 ttrss_user_labels2.article_id = ref_id";
2387
2388 }
5417fbd7 2389 } else if ($feed == -6) { // recently read
5089b30b 2390 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
5417fbd7 2391 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
835fb294 2392 $allow_archived = true;
46b78149
AD
2393
2394 if (!$override_order) $override_order = "last_read DESC";
2d24f032 2395 } else if ($feed == -3) { // fresh virtual feed
cd2cc43d 2396 $query_strategy_part = "unread = true AND score >= 0";
2d24f032 2397
7a22dc2a 2398 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
c1d7e6c3 2399
2d24f032 2400 if (DB_TYPE == "pgsql") {
be574731 2401 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2d24f032 2402 } else {
be574731 2403 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2d24f032
AD
2404 }
2405
b2531a28
AD
2406 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2407 } else if ($feed == -4) { // all articles virtual feed
2408 $query_strategy_part = "true";
e4f4b46f 2409 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
f822a8e5
AD
2410 } else if ($feed <= LABEL_BASE_INDEX) { // labels
2411 $label_id = feed_to_label_id($feed);
3de0261a 2412
ceb30ba4
AD
2413 $query_strategy_part = "label_id = '$label_id' AND
2414 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2415 ttrss_user_labels2.article_id = ref_id";
3de0261a 2416
ef393de7 2417 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
ceb30ba4 2418 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
835fb294 2419 $allow_archived = true;
8d505d78 2420
ef393de7 2421 } else {
835fb294 2422 $query_strategy_part = "true";
ef393de7 2423 }
d6e5706d 2424
b3990c92
AD
2425 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2426 $date_sort_field = "updated";
2427 } else {
2428 $date_sort_field = "date_entered";
2429 }
2430
b9a06a0e 2431 $order_by = "$date_sort_field DESC, updated DESC";
e939722a 2432
2e4faaac 2433 if ($view_mode == "unread_first") {
434bf856 2434 $order_by = "unread DESC, $order_by";
2e4faaac
AD
2435 }
2436
e939722a
AD
2437 if ($override_order) {
2438 $order_by = $override_order;
2439 }
8d505d78 2440
ef393de7
AD
2441 $feed_title = "";
2442
22fdebff 2443 if ($search) {
7032f2a5 2444 $feed_title = T_sprintf("Search results: %s", $search);
22fdebff 2445 } else {
ef393de7 2446 if ($cat_view) {
22fdebff 2447 $feed_title = getCategoryTitle($link, $feed);
ef393de7 2448 } else {
147f5632 2449 if (is_numeric($feed) && $feed > 0) {
8d505d78 2450 $result = db_query($link, "SELECT title,site_url,last_error
22fdebff 2451 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
8d505d78 2452
22fdebff
AD
2453 $feed_title = db_fetch_result($result, 0, "title");
2454 $feed_site_url = db_fetch_result($result, 0, "site_url");
2455 $last_error = db_fetch_result($result, 0, "last_error");
2456 } else {
2457 $feed_title = getFeedTitle($link, $feed);
8d505d78 2458 }
88040f57 2459 }
ef393de7
AD
2460 }
2461
87764a50 2462 $content_query_part = "content as content_preview, cached_content, ";
62129e67 2463
75c648cf 2464 if (is_numeric($feed)) {
8d505d78 2465
ef393de7
AD
2466 if ($feed >= 0) {
2467 $feed_kind = "Feeds";
2468 } else {
2469 $feed_kind = "Labels";
2470 }
8d505d78 2471
95a82c08
AD
2472 if ($limit_query_part) {
2473 $offset_query_part = "OFFSET $offset";
2474 }
2475
7fdf8eca 2476 // proper override_order applied above
6b3f228f 2477 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
6cfea5c7 2478 if (!$override_order) {
8d505d78 2479 $order_by = "ttrss_feeds.title, $order_by";
7fdf8eca
AD
2480 } else {
2481 $order_by = "ttrss_feeds.title, $override_order";
43fc671f 2482 }
6cfea5c7
AD
2483 }
2484
8361e724 2485 if (!$allow_archived) {
e04c18a2 2486 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
117335bf 2487 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
e04c18a2
AD
2488
2489 } else {
835fb294 2490 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
e04c18a2
AD
2491 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2492 }
2493
8d505d78 2494 $query = "SELECT DISTINCT
f9b2d27c 2495 date_entered,
1f64b1be 2496 guid,
ef393de7 2497 ttrss_entries.id,ttrss_entries.title,
46921916 2498 updated,
9c506873
AD
2499 label_cache,
2500 tag_cache,
c0644ee4 2501 always_display_enclosures,
d1fc2f92 2502 site_url,
c7e51de1 2503 note,
13992673
AD
2504 num_comments,
2505 comments,
db16ae50 2506 int_id,
bfd61d3f 2507 hide_images,
494a64ea 2508 unread,feed_id,marked,published,link,last_read,orig_feed_id,
7873d588 2509 last_marked, last_published,
ef393de7
AD
2510 $vfeed_query_part
2511 $content_query_part
ff6e357a 2512 author,score
ef393de7 2513 FROM
e04c18a2 2514 $from_qpart
ef393de7 2515 WHERE
e04c18a2 2516 $feed_check_qpart
ef393de7 2517 ttrss_user_entries.ref_id = ttrss_entries.id AND
c36bf4d5 2518 ttrss_user_entries.owner_uid = '$owner_uid' AND
ef393de7 2519 $search_query_part
36184020 2520 $filter_query_part
ef393de7 2521 $view_query_part
97e5dbb2 2522 $since_id_part
ef393de7 2523 $query_strategy_part ORDER BY $order_by
95a82c08 2524 $limit_query_part $offset_query_part";
4bc311fc 2525
b4e75b2a 2526 if ($_REQUEST["debug"]) print $query;
4bc311fc
AD
2527
2528 $result = db_query($link, $query);
8d505d78 2529
ef393de7
AD
2530 } else {
2531 // browsing by tag
8d505d78 2532
147f5632
CM
2533 $select_qpart = "SELECT DISTINCT " .
2534 "date_entered," .
2535 "guid," .
2536 "note," .
2537 "ttrss_entries.id as id," .
2538 "title," .
2539 "updated," .
2540 "unread," .
2541 "feed_id," .
2542 "orig_feed_id," .
2543 "marked," .
d1fc2f92
AD
2544 "num_comments, " .
2545 "comments, " .
c0644ee4
AD
2546 "tag_cache," .
2547 "label_cache," .
147f5632
CM
2548 "link," .
2549 "last_read," .
94a567df 2550 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
7873d588 2551 "last_marked, last_published, " .
97e5dbb2 2552 $since_id_part .
147f5632
CM
2553 $vfeed_query_part .
2554 $content_query_part .
147f5632
CM
2555 "score ";
2556
ef393de7 2557 $feed_kind = "Tags";
147f5632
CM
2558 $all_tags = explode(",", $feed);
2559 if ($search_mode == 'any') {
2560 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2561 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2562 $where_qpart = " WHERE " .
2563 "ref_id = ttrss_entries.id AND " .
2564 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2565 "post_int_id = int_id AND $tag_sql AND " .
2566 $view_query_part .
2567 $search_query_part .
2568 $query_strategy_part . " ORDER BY $order_by " .
2569 $limit_query_part;
8d505d78 2570
147f5632
CM
2571 } else {
2572 $i = 1;
2573 $sub_selects = array();
2574 $sub_ands = array();
2575 foreach ($all_tags as $term) {
2576 array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i");
2577 $i++;
2578 }
2579 if ($i > 2) {
2580 $x = 1;
2581 $y = 2;
2582 do {
2583 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2584 $x++;
2585 $y++;
2586 } while ($y < $i);
2587 }
2588 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2589 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2590 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2591 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2592 }
2593 // error_log("TAG SQL: " . $tag_sql);
2594 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2595
2596 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2597 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
ef393de7
AD
2598 }
2599
c7188969 2600 return array($result, $feed_title, $feed_site_url, $last_error);
8d505d78 2601
ef393de7
AD
2602 }
2603
bfd61d3f 2604 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
ceb0cab5
AD
2605 if (!$owner) $owner = $_SESSION["uid"];
2606
96811a55
AD
2607 $res = trim($str); if (!$res) return '';
2608
46137483
AD
2609 if (strpos($res, "href=") === false)
2610 $res = rewrite_urls($res);
533c0ea6 2611
8cc3c778
AD
2612 $charset_hack = '<head>
2613 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2614 </head>';
2615
96811a55
AD
2616 $res = trim($res); if (!$res) return '';
2617
8cc3c778
AD
2618 libxml_use_internal_errors(true);
2619
2620 $doc = new DOMDocument();
2621 $doc->loadHTML($charset_hack . $res);
2622 $xpath = new DOMXPath($doc);
8d505d78 2623
8cc3c778
AD
2624 $entries = $xpath->query('(//a[@href]|//img[@src])');
2625
2626 foreach ($entries as $entry) {
2627
2628 if ($site_url) {
2629
2630 if ($entry->hasAttribute('href'))
2631 $entry->setAttribute('href',
2632 rewrite_relative_url($site_url, $entry->getAttribute('href')));
8d505d78 2633
f0bd8e65
AD
2634 if ($entry->hasAttribute('src')) {
2635 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2636
2637 $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
2638
2639 if (file_exists($cached_filename)) {
2640 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
2641 }
2642
2643 $entry->setAttribute('src', $src);
2644 }
bfd61d3f
AD
2645
2646 if ($entry->nodeName == 'img') {
f0540b59 2647 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
ba79634c 2648 $force_remove_images || $_SESSION["bw_limit"]) {
bfd61d3f
AD
2649
2650 $p = $doc->createElement('p');
2651
2652 $a = $doc->createElement('a');
2653 $a->setAttribute('href', $entry->getAttribute('src'));
2654
2655 $a->appendChild(new DOMText($entry->getAttribute('src')));
2656 $a->setAttribute('target', '_blank');
2657
2658 $p->appendChild($a);
2659
2660 $entry->parentNode->replaceChild($p, $entry);
2661 }
2662 }
8cc3c778
AD
2663 }
2664
fa403733 2665 if (strtolower($entry->nodeName) == "a") {
c401d5c9 2666 $entry->setAttribute("target", "_blank");
fa403733 2667 }
8dccabed 2668 }
8d505d78 2669
254a3f56
AD
2670 $entries = $xpath->query('//iframe');
2671 foreach ($entries as $entry) {
4e404802
AD
2672 $entry->setAttribute('sandbox', 'allow-scripts');
2673
254a3f56 2674 }
8dccabed 2675
b70ccfe6
FE
2676 $allowed_elements = array('a', 'address', 'audio', 'article',
2677 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2678 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2679 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2680 'header', 'html', 'i', 'img', 'ins', 'kbd',
eb734e52 2681 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
b70ccfe6
FE
2682 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2683 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2684 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2685
2686 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2687
2688 $disallowed_attributes = array('id', 'style', 'class');
2689
e9b86f0a
AD
2690 global $pluginhost;
2691
2692 if (isset($pluginhost)) {
2693 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE) as $plugin) {
b70ccfe6
FE
2694 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2695 if (is_array($retval)) {
2696 $doc = $retval[0];
2697 $allowed_elements = $retval[1];
2698 $disallowed_attributes = $retval[2];
2699 } else {
2700 $doc = $retval;
2701 }
e9b86f0a
AD
2702 }
2703 }
2704
be124dc2 2705 $doc->removeChild($doc->firstChild); //remove doctype
b70ccfe6 2706 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
be124dc2 2707 $res = $doc->saveHTML();
254a3f56
AD
2708 return $res;
2709 }
16ad9085 2710
b70ccfe6 2711 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
254a3f56 2712 $entries = $doc->getElementsByTagName("*");
16ad9085 2713
254a3f56
AD
2714 foreach ($entries as $entry) {
2715 if (!in_array($entry->nodeName, $allowed_elements)) {
2716 $entry->parentNode->removeChild($entry);
2717 }
2718
2719 if ($entry->hasAttributes()) {
5f0081b0
AD
2720 $attrs_to_remove = array();
2721
2722 foreach ($entry->attributes as $attr) {
254a3f56
AD
2723
2724 if (strpos($attr->nodeName, 'on') === 0) {
5f0081b0 2725 array_push($attrs_to_remove, $attr);
254a3f56
AD
2726 }
2727
2728 if (in_array($attr->nodeName, $disallowed_attributes)) {
5f0081b0 2729 array_push($attrs_to_remove, $attr);
254a3f56
AD
2730 }
2731 }
5f0081b0
AD
2732
2733 foreach ($attrs_to_remove as $attr) {
2734 $entry->removeAttributeNode($attr);
2735 }
254a3f56
AD
2736 }
2737 }
2738
2739 return $doc;
183ad07b 2740 }
b72c3ef8 2741
73495fd1 2742 function check_for_update($link) {
63855db1 2743 if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
f6064662
AD
2744 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
2745 "&iid=" . sha1(SELF_URL_PATH);
b72c3ef8 2746
63855db1 2747 $version_data = @fetch_file_contents($version_url);
b72c3ef8 2748
63855db1
AD
2749 if ($version_data) {
2750 $version_data = json_decode($version_data, true);
8d505d78 2751 if ($version_data && $version_data['version']) {
f67d9754 2752
63855db1 2753 if (version_compare(VERSION, $version_data['version']) == -1) {
e91ad1e9 2754 return $version_data;
63855db1
AD
2755 }
2756 }
f67d9754 2757 }
b72c3ef8 2758 }
63855db1 2759 return false;
b72c3ef8 2760 }
472782e8 2761
9968d46f
AD
2762 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2763
2764 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
ed41f171 2765 if (count($ids) == 0) return;
472782e8
AD
2766
2767 $tmp_ids = array();
2768
2769 foreach ($ids as $id) {
2770 array_push($tmp_ids, "ref_id = '$id'");
2771 }
2772
2773 $ids_qpart = join(" OR ", $tmp_ids);
2774
2775 if ($cmode == 0) {
8d505d78 2776 db_query($link, "UPDATE ttrss_user_entries SET
472782e8 2777 unread = false,last_read = NOW()
9968d46f 2778 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 2779 } else if ($cmode == 1) {
8d505d78 2780 db_query($link, "UPDATE ttrss_user_entries SET
472782e8 2781 unread = true
9968d46f 2782 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 2783 } else {
8d505d78 2784 db_query($link, "UPDATE ttrss_user_entries SET
472782e8 2785 unread = NOT unread,last_read = NOW()
9968d46f 2786 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
472782e8 2787 }
0737b95a
AD
2788
2789 /* update ccache */
2790
2791 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2792 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2793
2794 while ($line = db_fetch_assoc($result)) {
2795 ccache_update($link, $line["feed_id"], $owner_uid);
2796 }
472782e8
AD
2797 }
2798
ca5133cb 2799 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
0b126ac2 2800
3972bf59 2801 $a_id = db_escape_string($link, $id);
0b126ac2 2802
bc976a8c
AD
2803 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2804
8d505d78 2805 $query = "SELECT DISTINCT tag_name,
0c3d1c68 2806 owner_uid as owner FROM
0b126ac2 2807 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
bd3f2ade 2808 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
0b126ac2 2809
bd3f2ade 2810 $obj_id = md5("TAGS:$owner_uid:$id");
8d505d78 2811 $tags = array();
bd3f2ade 2812
0e4a7d7a 2813 /* check cache first */
490c366d 2814
0e4a7d7a
AD
2815 if ($tag_cache === false) {
2816 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2817 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
490c366d 2818
0e4a7d7a
AD
2819 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2820 }
bd3f2ade 2821
0e4a7d7a
AD
2822 if ($tag_cache) {
2823 $tags = explode(",", $tag_cache);
2824 } else {
490c366d 2825
0e4a7d7a 2826 /* do it the hard way */
490c366d 2827
0e4a7d7a 2828 $tmp_result = db_query($link, $query);
490c366d 2829
0e4a7d7a
AD
2830 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2831 array_push($tags, $tmp_line["tag_name"]);
2832 }
490c366d 2833
0e4a7d7a 2834 /* update the cache */
490c366d 2835
3972bf59 2836 $tags_str = db_escape_string($link, join(",", $tags));
bd3f2ade 2837
0e4a7d7a
AD
2838 db_query($link, "UPDATE ttrss_user_entries
2839 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2840 AND owner_uid = $owner_uid");
0b126ac2
AD
2841 }
2842
2843 return $tags;
2844 }
2845
d62a3b63
AD
2846 function trim_array($array) {
2847 $tmp = $array;
3415b075 2848 array_walk($tmp, 'trim');
d62a3b63
AD
2849 return $tmp;
2850 }
2851
be832a1a 2852 function tag_is_valid($tag) {
ef063748
AD
2853 if ($tag == '') return false;
2854 if (preg_match("/^[0-9]*$/", $tag)) return false;
41f7498a 2855 if (mb_strlen($tag) > 250) return false;
ef063748 2856
31365729
AD
2857 if (function_exists('iconv')) {
2858 $tag = iconv("utf-8", "utf-8", $tag);
2859 }
2860
ef063748
AD
2861 if (!$tag) return false;
2862
2863 return true;
be832a1a
AD
2864 }
2865
d98e76d9
AD
2866 function render_login_form($link) {
2867 require_once "login_form.php";
97acbaf1 2868 exit;
01a87dff
AD
2869 }
2870
dc56b3b7
AD
2871 // from http://developer.apple.com/internet/safari/faq.html
2872 function no_cache_incantation() {
2873 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2874 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2875 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2876 header("Cache-Control: post-check=0, pre-check=0", false);
2877 header("Pragma: no-cache"); // HTTP/1.0
2878 }
2879
42395d28 2880 function format_warning($msg, $id = "") {
883fee8d 2881 global $link;
8d505d78 2882 return "<div class=\"warning\" id=\"$id\">
fcef9eea 2883 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
0d32b41e
AD
2884 }
2885
08ac193a 2886 function format_notice($msg, $id = "") {
883fee8d 2887 global $link;
8d505d78 2888 return "<div class=\"notice\" id=\"$id\">
fcef9eea 2889 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
0d32b41e
AD
2890 }
2891
08ac193a 2892 function format_error($msg, $id = "") {
883fee8d 2893 global $link;
8d505d78 2894 return "<div class=\"error\" id=\"$id\">
fcef9eea 2895 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
68d2f95e
AD
2896 }
2897
4dccf1ed
AD
2898 function print_notice($msg) {
2899 return print format_notice($msg);
2900 }
2901
2902 function print_warning($msg) {
2903 return print format_warning($msg);
2904 }
2905
68d2f95e
AD
2906 function print_error($msg) {
2907 return print format_error($msg);
2908 }
2909
2910
4dccf1ed
AD
2911 function T_sprintf() {
2912 $args = func_get_args();
2913 return vsprintf(__(array_shift($args)), $args);
2914 }
2915
51682b23
AD
2916 function format_inline_player($link, $url, $ctype) {
2917
2918 $entry = "";
2919
44cd77b6
AD
2920 $url = htmlspecialchars($url);
2921
8d505d78 2922 if (strpos($ctype, "audio/") === 0) {
c3edc667
AD
2923
2924 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
8d505d78 2925 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
c3edc667
AD
2926 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2927
2928 $id = 'AUDIO-' . uniqid();
2929
cb081096 2930 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
ca3bca99 2931 <source type=\"$ctype\" src=\"$url\"></source>
8d505d78 2932 </audio>";
c3edc667 2933
8d505d78 2934 $entry .= "<span onclick=\"player(this)\"
c3edc667
AD
2935 title=\"".__("Click to play")."\" status=\"0\"
2936 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2937
2938 } else {
8d505d78
AD
2939
2940 $entry .= "<object type=\"application/x-shockwave-flash\"
ad95edc2 2941 data=\"lib/button/musicplayer.swf?song_url=$url\"
8d505d78
AD
2942 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2943 <param name=\"movie\"
ad95edc2 2944 value=\"lib/button/musicplayer.swf?song_url=$url\" />
8d505d78 2945 </object>";
c3edc667 2946 }
ca3bca99 2947
44cd77b6
AD
2948 if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
2949 href=\"$url\">" . basename($url) . "</a>";
ca3bca99
AD
2950
2951 return $entry;
2952
51682b23
AD
2953 }
2954
ca3bca99
AD
2955 return "";
2956
2957/* $filename = substr($url, strrpos($url, "/")+1);
c3edc667
AD
2958
2959 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
ca3bca99 2960 $filename . " (" . $ctype . ")" . "</a>"; */
c3edc667 2961
51682b23
AD
2962 }
2963
64436e10 2964 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
64436e10 2965 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3de0261a 2966
009646d2
AD
2967 $rv = array();
2968
2969 $rv['id'] = $id;
2970
10eb9da8 2971 /* we can figure out feed_id from article id anyway, why do we
e04c18a2 2972 * pass feed_id here? let's ignore the argument :( */
10eb9da8
AD
2973
2974 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2975 WHERE ref_id = '$id'");
2976
e04c18a2 2977 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
10eb9da8 2978
009646d2
AD
2979 $rv['feed_id'] = $feed_id;
2980
2981 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3de0261a 2982
3de0261a 2983 if ($mark_as_read) {
8d505d78
AD
2984 $result = db_query($link, "UPDATE ttrss_user_entries
2985 SET unread = false,last_read = NOW()
64436e10 2986 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
8a4c759e 2987
64436e10 2988 ccache_update($link, $feed_id, $owner_uid);
3de0261a
AD
2989 }
2990
7252abe3 2991 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
fc2b26a6 2992 ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
8cc3c778 2993 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
33de3d37 2994 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3de0261a 2995 num_comments,
9c506873 2996 tag_cache,
c7e51de1 2997 author,
ef83538d 2998 orig_feed_id,
87764a50
AD
2999 note,
3000 cached_content
3de0261a 3001 FROM ttrss_entries,ttrss_user_entries
64436e10 3002 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3de0261a
AD
3003
3004 if ($result) {
3005
3de0261a
AD
3006 $line = db_fetch_assoc($result);
3007
84d952f1
AD
3008 $tag_cache = $line["tag_cache"];
3009
3010 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3011 unset($line["tag_cache"]);
3012
3013 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3014
3015 global $pluginhost;
3016
3017 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE) as $p) {
3018 $line = $p->hook_render_article($line);
3019 }
8cc3c778 3020
3de0261a
AD
3021 $num_comments = $line["num_comments"];
3022 $entry_comments = "";
3023
3024 if ($num_comments > 0) {
3025 if ($line["comments"]) {
6e577ba1 3026 $comments_url = htmlspecialchars($line["comments"]);
3de0261a 3027 } else {
6e577ba1 3028 $comments_url = htmlspecialchars($line["link"]);
3de0261a 3029 }
7514749d 3030 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3de0261a
AD
3031 } else {
3032 if ($line["comments"] && $line["link"] != $line["comments"]) {
6e577ba1 3033 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
8d505d78 3034 }
3de0261a
AD
3035 }
3036
eedfb635
AD
3037 if ($zoom_mode) {
3038 header("Content-Type: text/html");
009646d2 3039 $rv['content'] .= "<html><head>
5bb0cc8e 3040 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
eedfb635
AD
3041 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3042 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
b87a625c 3043 </head><body id=\"ttrssZoom\">";
eedfb635
AD
3044 }
3045
009646d2 3046 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
bc372fe3 3047
126e639a 3048 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3de0261a
AD
3049
3050 $entry_author = $line["author"];
3051
3052 if ($entry_author) {
60164936 3053 $entry_author = __(" - ") . $entry_author;
3de0261a
AD
3054 }
3055
8d505d78 3056 $parsed_updated = make_local_datetime($link, $line["updated"], true,
64436e10 3057 $owner_uid, true);
324944f3 3058
5321e775 3059 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3de0261a
AD
3060
3061 if ($line["link"]) {
c6c010d9 3062 $rv['content'] .= "<div class='postTitle'><a target='_blank'
bf1dc420 3063 title=\"".htmlspecialchars($line['title'])."\"
8d505d78 3064 href=\"" .
5c568973 3065 htmlspecialchars($line["link"]) . "\">" .
11bd95b4
AD
3066 $line["title"] . "</a>" .
3067 "<span class='author'>$entry_author</span></div>";
3de0261a 3068 } else {
c6c010d9 3069 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3de0261a
AD
3070 }
3071
84d952f1
AD
3072 $tags_str = format_tags_string($line["tags"], $id);
3073 $tags_str_full = join(", ", $line["tags"]);
0780f4f4
AD
3074
3075 if (!$tags_str_full) $tags_str_full = __("no tags");
e7544143 3076
3de0261a
AD
3077 if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
3078
f0755b7c 3079 $rv['content'] .= "<div class='postTags' style='float : right'>
2a3b6de0 3080 <img src='images/tag.png'
e9823609 3081 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
eedfb635
AD
3082
3083 if (!$zoom_mode) {
009646d2 3084 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
8d505d78 3085 <a title=\"".__('Edit tags for this article')."\"
31a53903 3086 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
4710e3dc 3087
0780f4f4
AD
3088 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3089 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3090 position=\"below\">$tags_str_full</div>";
3091
19c73507 3092 global $pluginhost;
f9ac31d6 3093
19c73507
AD
3094 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) {
3095 $rv['content'] .= $p->hook_article_button($line);
411fe209
AD
3096 }
3097
6f3976c9 3098
24ecbcae
AD
3099 } else {
3100 $tags_str = strip_tags($tags_str);
009646d2 3101 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
eedfb635 3102 }
009646d2
AD
3103 $rv['content'] .= "</div>";
3104 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3de0261a 3105
ef83538d
AD
3106 if ($line["orig_feed_id"]) {
3107
3108 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3109 WHERE id = ".$line["orig_feed_id"]);
3110
3111 if (db_num_rows($tmp_result) != 0) {
3112
009646d2
AD
3113 $rv['content'] .= "<div clear='both'>";
3114 $rv['content'] .= __("Originally from:");
ef83538d 3115
009646d2 3116 $rv['content'] .= "&nbsp;";
ef83538d
AD
3117
3118 $tmp_line = db_fetch_assoc($tmp_result);
3119
009646d2 3120 $rv['content'] .= "<a target='_blank'
ef83538d
AD
3121 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3122 $tmp_line['title'] . "</a>";
3123
009646d2 3124 $rv['content'] .= "&nbsp;";
ef83538d 3125
009646d2 3126 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
c2167866 3127 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
ef83538d 3128
009646d2 3129 $rv['content'] .= "</div>";
ef83538d
AD
3130 }
3131 }
3132
009646d2 3133 $rv['content'] .= "</div>";
3de0261a 3134
009646d2 3135 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
c7e51de1 3136 if ($line['note']) {
16cbc19a 3137 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
c7e51de1 3138 }
009646d2 3139 $rv['content'] .= "</div>";
c7e51de1 3140
009646d2 3141 $rv['content'] .= "<div class=\"postContent\">";
741b6090 3142
84d952f1 3143 $rv['content'] .= $line["content"];
db54143e 3144
009646d2 3145 $rv['content'] .= format_article_enclosures($link, $id,
33de3d37 3146 $always_display_enclosures, $line["content"], $line["hide_images"]);
ce53e200 3147
009646d2 3148 $rv['content'] .= "</div>";
dad14b51 3149
009646d2 3150 $rv['content'] .= "</div>";
3de0261a
AD
3151
3152 }
3153
009646d2
AD
3154 if ($zoom_mode) {
3155 $rv['content'] .= "
b87a625c 3156 <div class='footer'>
2ae69126
AD
3157 <button onclick=\"return window.close()\">".
3158 __("Close this window")."</button></div>";
009646d2 3159 $rv['content'] .= "</body></html>";
eedfb635 3160 }
3de0261a 3161
009646d2
AD
3162 return $rv;
3163
3de0261a
AD
3164 }
3165
79178062 3166 function print_checkpoint($n, $s) {
fa9e88c3 3167 $ts = microtime(true);
79178062
AD
3168 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3169 return $ts;
3170 }
3de0261a 3171
79178062
AD
3172 function sanitize_tag($tag) {
3173 $tag = trim($tag);
52d7e7da 3174
79178062 3175 $tag = mb_strtolower($tag, 'utf-8');
bd202c3f 3176
79178062 3177 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
46921916 3178
79178062
AD
3179// $tag = str_replace('"', "", $tag);
3180// $tag = str_replace("+", " ", $tag);
3181 $tag = str_replace("technorati tag: ", "", $tag);
961f4c73 3182
79178062
AD
3183 return $tag;
3184 }
3de0261a 3185
79178062 3186 function get_self_url_prefix() {
51cc3873
AD
3187 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
3188 return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
3189 } else {
3190 return SELF_URL_PATH;
3191 }
79178062 3192 }
a9bcfb8f 3193
45004d43
AD
3194 /**
3195 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3196 *
3197 * @return string The Mozilla Firefox feed adding URL.
3198 */
3199 function add_feed_url() {
ed102aa0
AD
3200 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3201
3202 $url_path = get_self_url_prefix() .
97acbaf1 3203 "/public.php?op=subscribe&feed_url=%s";
755a43ee 3204 return $url_path;
45004d43
AD
3205 } // function add_feed_url
3206
e90053fe
AD
3207 function encrypt_password($pass, $salt = '', $mode2 = false) {
3208 if ($salt && $mode2) {
3209 return "MODE2:" . hash('sha256', $salt . $pass);
3210 } else if ($salt) {
3211 return "SHA1X:" . sha1("$salt:$pass");
1a9f4d3c
AD
3212 } else {
3213 return "SHA1:" . sha1($pass);
3214 }
45004d43
AD
3215 } // function encrypt_password
3216
6aff7845 3217 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
fee840fb
AD
3218 $filters = array();
3219
5574b09e 3220 $cat_id = (int)getFeedCategory($link, $feed_id);
fee840fb 3221
6aff7845 3222 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
8e8c8934 3223 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
8d505d78 3224
67bd0b1f
AD
3225 $check_cats = join(",", array_merge(
3226 getParentCategories($link, $cat_id, $owner_uid),
3227 array($cat_id)));
3228
0e4a7d7a 3229 while ($line = db_fetch_assoc($result)) {
6aff7845
AD
3230 $filter_id = $line["id"];
3231
3232 $result2 = db_query($link, "SELECT
a3a896a1 3233 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
6aff7845
AD
3234 FROM ttrss_filters2_rules AS r,
3235 ttrss_filter_types AS t
3236 WHERE
67bd0b1f 3237 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
6aff7845
AD
3238 (feed_id IS NULL OR feed_id = '$feed_id') AND
3239 filter_type = t.id AND filter_id = '$filter_id'");
3240
3241 $rules = array();
3242 $actions = array();
ba975b2e 3243
6aff7845
AD
3244 while ($rule_line = db_fetch_assoc($result2)) {
3245# print_r($rule_line);
8d505d78 3246
6aff7845
AD
3247 $rule = array();
3248 $rule["reg_exp"] = $rule_line["reg_exp"];
3249 $rule["type"] = $rule_line["type_name"];
a3a896a1 3250 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
6aff7845
AD
3251
3252 array_push($rules, $rule);
3253 }
3254
3255 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3256 FROM ttrss_filters2_actions AS a,
3257 ttrss_filter_actions AS t
3258 WHERE
3259 action_id = t.id AND filter_id = '$filter_id'");
3260
3261 while ($action_line = db_fetch_assoc($result2)) {
3262# print_r($action_line);
3263
3264 $action = array();
3265 $action["type"] = $action_line["type_name"];
3266 $action["param"] = $action_line["action_param"];
3267
3268 array_push($actions, $action);
0e4a7d7a 3269 }
b8ffa322 3270
b8ffa322 3271
6aff7845
AD
3272 $filter = array();
3273 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
a3a896a1 3274 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
6aff7845
AD
3275 $filter["rules"] = $rules;
3276 $filter["actions"] = $actions;
3277
3278 if (count($rules) > 0 && count($actions) > 0) {
3279 array_push($filters, $filter);
3280 }
3281 }
3282
0e4a7d7a 3283 return $filters;
fee840fb 3284 }
1e36af0c
AD
3285
3286 function get_score_pic($score) {
8d505d78
AD
3287 if ($score > 100) {
3288 return "score_high.png";
3289 } else if ($score > 0) {
883fee8d 3290 return "score_half_high.png";
1cce3aca 3291 } else if ($score < -100) {
883fee8d 3292 return "score_low.png";
1cce3aca 3293 } else if ($score < 0) {
883fee8d 3294 return "score_half_low.png";
8d505d78 3295 } else {
883fee8d 3296 return "score_neutral.png";
1e36af0c
AD
3297 }
3298 }
ec92c9d1 3299
7defa089
AD
3300 function feed_has_icon($id) {
3301 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
3302 }
f29ba148
AD
3303
3304 function init_connection($link) {
8c0496f7
AD
3305 if ($link) {
3306
3307 if (DB_TYPE == "pgsql") {
3308 pg_query($link, "set client_encoding = 'UTF-8'");
3309 pg_set_client_encoding("UNICODE");
3310 pg_query($link, "set datestyle = 'ISO, european'");
3311 pg_query($link, "set TIME ZONE 0");
3312 } else {
3313 db_query($link, "SET time_zone = '+0:0'");
3314
3315 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
3316 db_query($link, "SET NAMES " . MYSQL_CHARSET);
3317 }
3318 }
19c73507
AD
3319
3320 global $pluginhost;
3321
8c0496f7 3322 $pluginhost = new PluginHost($link);
d2a421e3 3323 $pluginhost->load(PLUGINS, $pluginhost::KIND_ALL);
19c73507 3324
5f0a3741
AD
3325 return true;
3326 } else {
3327 print "Unable to connect to database:" . db_last_error();
3328 return false;
f29ba148
AD
3329 }
3330 }
5e96ca9d 3331
307d187c
AD
3332 function format_tags_string($tags, $id) {
3333
3334 $tags_str = "";
3335 $tags_nolinks_str = "";
3336
3337 $num_tags = 0;
3338
d9084cf2 3339 $tag_limit = 6;
307d187c
AD
3340
3341 $formatted_tags = array();
3342
3343 foreach ($tags as $tag) {
3344 $num_tags++;
3345 $tag_escaped = str_replace("'", "\\'", $tag);
3346
275a0af2
AD
3347 if (mb_strlen($tag) > 30) {
3348 $tag = truncate_string($tag, 30);
3349 }
3350
307d187c
AD
3351 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3352
3353 array_push($formatted_tags, $tag_str);
275a0af2
AD
3354
3355 $tmp_tags_str = implode(", ", $formatted_tags);
8d505d78 3356
275a0af2 3357 if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
307d187c
AD
3358 break;
3359 }
3360 }
3361
3362 $tags_str = implode(", ", $formatted_tags);
3363
3364 if ($num_tags < count($tags)) {
3365 $tags_str .= ", &hellip;";
3366 }
3367
3368 if ($num_tags == 0) {
3369 $tags_str = __("no tags");
3370 }
3371
3372 return $tags_str;
3373
3374 }
2eb9c95c
AD
3375
3376 function format_article_labels($labels, $id) {
3377
3378 $labels_str = "";
3379
3380 foreach ($labels as $l) {
8d505d78 3381 $labels_str .= sprintf("<span class='hlLabelRef'
2eb9c95c
AD
3382 style='color : %s; background-color : %s'>%s</span>",
3383 $l[2], $l[3], $l[1]);
3384 }
3385
3386 return $labels_str;
3387
3388 }
c7e51de1 3389
16cbc19a 3390 function format_article_note($id, $note, $allow_edit = true) {
c7e51de1 3391
fcfa9ef1
AD
3392 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3393 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
16cbc19a 3394 ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
c7e51de1
AD
3395
3396 return $str;
3397 }
7f969260 3398
7e329f13 3399
d2a317e3
AD
3400 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3401 if ($parent_cat_id) {
3402 $parent_qpart = "parent_cat = '$parent_cat_id'";
3403 $parent_insert = "'$parent_cat_id'";
3404 } else {
3405 $parent_qpart = "parent_cat IS NULL";
3406 $parent_insert = "NULL";
3407 }
3408
3409 $result = db_query($link,
3410 "SELECT id FROM ttrss_feed_categories
3411 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3412
3413 if (db_num_rows($result) == 0) {
3414 return false;
3415 } else {
3416 return db_fetch_result($result, 0, "id");
3417 }
3418 }
3419
3420 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
c00907f2
AD
3421
3422 if (!$feed_cat) return false;
3423
5c7c7da9
AD
3424 db_query($link, "BEGIN");
3425
d2a317e3
AD
3426 if ($parent_cat_id) {
3427 $parent_qpart = "parent_cat = '$parent_cat_id'";
3428 $parent_insert = "'$parent_cat_id'";
3429 } else {
3430 $parent_qpart = "parent_cat IS NULL";
3431 $parent_insert = "NULL";
3432 }
3433
129562e0
AD
3434 $feed_cat = mb_substr($feed_cat, 0, 250);
3435
5c7c7da9
AD
3436 $result = db_query($link,
3437 "SELECT id FROM ttrss_feed_categories
d2a317e3 3438 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
5c7c7da9
AD
3439
3440 if (db_num_rows($result) == 0) {
8d505d78 3441
5c7c7da9 3442 $result = db_query($link,
d2a317e3
AD
3443 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3444 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
5c7c7da9
AD
3445
3446 db_query($link, "COMMIT");
3447
3448 return true;
3449 }
3450
3451 return false;
8d505d78 3452 }
5c7c7da9 3453
ab197ae1 3454 function getArticleFeed($link, $id) {
8d505d78 3455 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
a545dc31 3456 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
ab197ae1
AD
3457
3458 if (db_num_rows($result) != 0) {
3459 return db_fetch_result($result, 0, "feed_id");
3460 } else {
3461 return 0;
3462 }
3463 }
a5819bb3 3464
f2c6c008
CW
3465 /**
3466 * Fixes incomplete URLs by prepending "http://".
f0266f51
CW
3467 * Also replaces feed:// with http://, and
3468 * prepends a trailing slash if the url is a domain name only.
f2c6c008
CW
3469 *
3470 * @param string $url Possibly incomplete URL
3471 *
3472 * @return string Fixed URL.
3473 */
3474 function fix_url($url) {
3475 if (strpos($url, '://') === false) {
3476 $url = 'http://' . $url;
f0266f51
CW
3477 } else if (substr($url, 0, 5) == 'feed:') {
3478 $url = 'http:' . substr($url, 5);
3479 }
3480
3481 //prepend slash if the URL has no slash in it
3482 // "http://www.example" -> "http://www.example/"
44453773 3483 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
f0266f51 3484 $url .= '/';
f2c6c008 3485 }
ec39a02c
AD
3486
3487 if ($url != "http:///")
3488 return $url;
3489 else
3490 return '';
f2c6c008
CW
3491 }
3492
a5819bb3
AD
3493 function validate_feed_url($url) {
3494 $parts = parse_url($url);
3495
3496 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
3497
3498 }
d9084cf2 3499
be35798b
AD
3500 function get_article_enclosures($link, $id) {
3501
8d505d78 3502 $query = "SELECT * FROM ttrss_enclosures
be35798b
AD
3503 WHERE post_id = '$id' AND content_url != ''";
3504
be35798b
AD
3505 $rv = array();
3506
0e4a7d7a 3507 $result = db_query($link, $query);
be35798b 3508
0e4a7d7a
AD
3509 if (db_num_rows($result) > 0) {
3510 while ($line = db_fetch_assoc($result)) {
3511 array_push($rv, $line);
be35798b
AD
3512 }
3513 }
3514
3515 return $rv;
3516 }
3517
31a53903
AD
3518 function save_email_address($link, $email) {
3519 // FIXME: implement persistent storage of emails
3520
8d505d78 3521 if (!$_SESSION['stored_emails'])
31a53903
AD
3522 $_SESSION['stored_emails'] = array();
3523
3524 if (!in_array($email, $_SESSION['stored_emails']))
3525 array_push($_SESSION['stored_emails'], $email);
3526 }
8801fb01 3527
8801fb01
AD
3528
3529 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3530
3531 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3532
3533 $sql_is_cat = bool_to_sql_bool($is_cat);
3534
8d505d78
AD
3535 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3536 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
8801fb01
AD
3537 AND owner_uid = " . $owner_uid);
3538
3539 if (db_num_rows($result) == 1) {
3540 return db_fetch_result($result, 0, "access_key");
3541 } else {
3972bf59 3542 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
8801fb01 3543
8d505d78 3544 $result = db_query($link, "INSERT INTO ttrss_access_keys
8801fb01
AD
3545 (access_key, feed_id, is_cat, owner_uid)
3546 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3547
3548 return $key;
3549 }
3550 return false;
3551 }
f0266f51 3552
759e5132 3553 function get_feeds_from_html($url, $content)
f0266f51
CW
3554 {
3555 $url = fix_url($url);
3556 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
3557
fb074239
AD
3558 libxml_use_internal_errors(true);
3559
f0266f51 3560 $doc = new DOMDocument();
8d505d78 3561 $doc->loadHTML($content);
f0266f51
CW
3562 $xpath = new DOMXPath($doc);
3563 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3564 $feedUrls = array();
3565 foreach ($entries as $entry) {
3566 if ($entry->hasAttribute('href')) {
3567 $title = $entry->getAttribute('title');
3568 if ($title == '') {
3569 $title = $entry->getAttribute('type');
3570 }
923818fc
CW
3571 $feedUrl = rewrite_relative_url(
3572 $baseUrl, $entry->getAttribute('href')
3573 );
f0266f51
CW
3574 $feedUrls[$feedUrl] = $title;
3575 }
3576 }
3577 return $feedUrls;
3578 }
3579
759e5132 3580 function is_html($content) {
32b86711 3581 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
759e5132 3582 }
f33479da 3583
759e5132
AD
3584 function url_is_html($url, $login = false, $pass = false) {
3585 return is_html(fetch_file_contents($url, false, $login, $pass));
f33479da 3586 }
24e2bb3a 3587
d90868d7 3588 function print_label_select($link, $name, $value, $attributes = "") {
24e2bb3a
AD
3589
3590 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3591 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3592
8d505d78 3593 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
d90868d7 3594 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
24e2bb3a
AD
3595
3596 while ($line = db_fetch_assoc($result)) {
3597
3598 $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
3599
d90868d7
AD
3600 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3601 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
24e2bb3a
AD
3602
3603 }
3604
d90868d7 3605# print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
24e2bb3a
AD
3606
3607 print "</select>";
3608
3609
3610 }
3611
009646d2 3612 function format_article_enclosures($link, $id, $always_display_enclosures,
33de3d37 3613 $article_content, $hide_images = false) {
dad14b51
AD
3614
3615 $result = get_article_enclosures($link, $id);
009646d2 3616 $rv = '';
8d505d78 3617
dad14b51 3618 if (count($result) > 0) {
8d505d78 3619
dad14b51
AD
3620 $entries_html = array();
3621 $entries = array();
ca3bca99 3622 $entries_inline = array();
8d505d78 3623
dad14b51 3624 foreach ($result as $line) {
8d505d78 3625
dad14b51
AD
3626 $url = $line["content_url"];
3627 $ctype = $line["content_type"];
8d505d78 3628
dad14b51 3629 if (!$ctype) $ctype = __("unknown type");
8d505d78 3630
749b56bd 3631 $filename = substr($url, strrpos($url, "/")+1);
8d505d78 3632
ca3bca99
AD
3633 $player = format_inline_player($link, $url, $ctype);
3634
3635 if ($player) array_push($entries_inline, $player);
8d505d78 3636
c3edc667
AD
3637# $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3638# $filename . " (" . $ctype . ")" . "</a>";
8d505d78 3639
749b56bd
AD
3640 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3641 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3642
dad14b51 3643 array_push($entries_html, $entry);
8d505d78 3644
dad14b51 3645 $entry = array();
8d505d78 3646
dad14b51
AD
3647 $entry["type"] = $ctype;
3648 $entry["filename"] = $filename;
3649 $entry["url"] = $url;
8d505d78 3650
dad14b51
AD
3651 array_push($entries, $entry);
3652 }
8d505d78 3653
ba79634c 3654 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
dad14b51
AD
3655 if ($always_display_enclosures ||
3656 !preg_match("/<img/i", $article_content)) {
8d505d78 3657
dad14b51 3658 foreach ($entries as $entry) {
8d505d78 3659
dad14b51
AD
3660 if (preg_match("/image/", $entry["type"]) ||
3661 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
8d505d78 3662
33de3d37
AD
3663 if (!$hide_images) {
3664 $rv .= "<p><img
3665 alt=\"".htmlspecialchars($entry["filename"])."\"
3666 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3667 } else {
3668 $rv .= "<p><a target=\"_blank\"
3669 href=\"".htmlspecialchars($entry["url"])."\"
3670 >" .htmlspecialchars($entry["url"]) . "</a></p>";
749b56bd 3671
33de3d37 3672 }
dad14b51
AD
3673 }
3674 }
3675 }
3676 }
8d505d78 3677
ca3bca99
AD
3678 if (count($entries_inline) > 0) {
3679 $rv .= "<hr clear='both'/>";
3680 foreach ($entries_inline as $entry) { $rv .= $entry; };
3681 $rv .= "<hr clear='both'/>";
3682 }
3683
bf6df236 3684 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
f9432f26 3685 "<option value=''>" . __('Attachments')."</option>";
8d505d78 3686
f9432f26
AD
3687 foreach ($entries as $entry) {
3688 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
8d505d78 3689
f9432f26
AD
3690 };
3691
3692 $rv .= "</select>";
dad14b51 3693 }
009646d2
AD
3694
3695 return $rv;
dad14b51
AD
3696 }
3697
f8fb4498
AD
3698 function getLastArticleId($link) {
3699 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3700 WHERE owner_uid = " . $_SESSION["uid"]);
3701
3702 if (db_num_rows($result) == 1) {
3703 return db_fetch_result($result, 0, "id");
3704 } else {
3705 return -1;
3706 }
3707 }
8cc3c778
AD
3708
3709 function build_url($parts) {
3710 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3711 }
3712
f679105c
CW
3713 /**
3714 * Converts a (possibly) relative URL to a absolute one.
3715 *
3716 * @param string $url Base URL (i.e. from where the document is)
3717 * @param string $rel_url Possibly relative URL in the document
3718 *
3719 * @return string Absolute URL
3720 */
8cc3c778 3721 function rewrite_relative_url($url, $rel_url) {
b4520bb8
AD
3722 if (strpos($rel_url, "magnet:") === 0) {
3723 return $rel_url;
3724 } else if (strpos($rel_url, "://") !== false) {
8cc3c778 3725 return $rel_url;
f9052d35 3726 } else if (strpos($rel_url, "//") === 0) {
3727 # protocol-relative URL (rare but they exist)
3728 return $rel_url;
8d505d78 3729 } else if (strpos($rel_url, "/") === 0)
8cc3c778
AD
3730 {
3731 $parts = parse_url($url);
3732 $parts['path'] = $rel_url;
3733
3734 return build_url($parts);
3735
3736 } else {
3737 $parts = parse_url($url);
f679105c
CW
3738 if (!isset($parts['path'])) {
3739 $parts['path'] = '/';
3740 }
3741 $dir = $parts['path'];
3742 if (substr($dir, -1) !== '/') {
3743 $dir = dirname($parts['path']);
3744 $dir !== '/' && $dir .= '/';
3745 }
3746 $parts['path'] = $dir . $rel_url;
8cc3c778
AD
3747
3748 return build_url($parts);
3749 }
3750 }
3751
e4f7f8df 3752 function sphinx_search($query, $offset = 0, $limit = 30) {
31303c6b
AD
3753 require_once 'lib/sphinxapi.php';
3754
e4f7f8df
AD
3755 $sphinxClient = new SphinxClient();
3756
3757 $sphinxClient->SetServer('localhost', 9312);
3758 $sphinxClient->SetConnectTimeout(1);
3759
8d505d78 3760 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
e4f7f8df
AD
3761 'feed_title' => 20));
3762
3763 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
3764 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
3765 $sphinxClient->SetLimits($offset, $limit, 1000);
3766 $sphinxClient->SetArrayResult(false);
3767 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
8d505d78 3768
e4f7f8df
AD
3769 $result = $sphinxClient->Query($query, SPHINX_INDEX);
3770
3771 $ids = array();
3772
3773 if (is_array($result['matches'])) {
3774 foreach (array_keys($result['matches']) as $int_id) {
3775 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3776 array_push($ids, $ref_id);
3777 }
3778 }
3779
3780 return $ids;
3781 }
3782
868650e4
AD
3783 function cleanup_tags($link, $days = 14, $limit = 1000) {
3784
3785 if (DB_TYPE == "pgsql") {
3786 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3787 } else if (DB_TYPE == "mysql") {
3788 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3789 }
3790
b5ec13fa 3791 $tags_deleted = 0;
868650e4 3792
b5ec13fa
AD
3793 while ($limit > 0) {
3794 $limit_part = 500;
3795
8d505d78
AD
3796 $query = "SELECT ttrss_tags.id AS id
3797 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
b5ec13fa
AD
3798 WHERE post_int_id = int_id AND $interval_query AND
3799 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
8d505d78 3800
b5ec13fa
AD
3801 $result = db_query($link, $query);
3802
3803 $ids = array();
3804
3805 while ($line = db_fetch_assoc($result)) {
3806 array_push($ids, $line['id']);
3807 }
3808
3809 if (count($ids) > 0) {
3810 $ids = join(",", $ids);
b5ec13fa
AD
3811
3812 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3813 $tags_deleted += db_affected_rows($link, $tmp_result);
3814 } else {
3815 break;
3816 }
3817
3818 $limit -= $limit_part;
3819 }
3820
b5ec13fa 3821 return $tags_deleted;
868650e4
AD
3822 }
3823
88e4e597
AD
3824 function print_user_stylesheet($link) {
3825 $value = get_pref($link, 'USER_STYLESHEET');
3826
3827 if ($value) {
3828 print "<style type=\"text/css\">";
5823f9fb 3829 print str_replace("<br/>", "\n", $value);
88e4e597
AD
3830 print "</style>";
3831 }
3832
3833 }
3834
73c32678
AD
3835 function rewrite_urls($html) {
3836 libxml_use_internal_errors(true);
3837
3838 $charset_hack = '<head>
3839 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3840 </head>';
3841
3842 $doc = new DOMDocument();
3843 $doc->loadHTML($charset_hack . $html);
3844 $xpath = new DOMXPath($doc);
3845
3846 $entries = $xpath->query('//*/text()');
3847
3848 foreach ($entries as $entry) {
3849 if (strstr($entry->wholeText, "://") !== false) {
3850 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3851 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText);
3852
3853 if ($text != $entry->wholeText) {
3854 $cdoc = new DOMDocument();
3855 $cdoc->loadHTML($charset_hack . $text);
3856
3857
3858 foreach ($cdoc->childNodes as $cnode) {
3859 $cnode = $doc->importNode($cnode, true);
3860
3861 if ($cnode) {
3862 $entry->parentNode->insertBefore($cnode);
3863 }
3864 }
3865
3866 $entry->parentNode->removeChild($entry);
3867
3868 }
3869 }
3870 }
3871
3872 $node = $doc->getElementsByTagName('body')->item(0);
3873
376897af
AD
3874 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3875 if ($node)
cc38c8e5 3876 return $doc->saveXML($node);
376897af
AD
3877 else
3878 return $html;
533c0ea6
AD
3879 }
3880
2680295b 3881 function filter_to_sql($link, $filter, $owner_uid) {
4e02f582 3882 $query = array();
36184020 3883
4e02f582
AD
3884 if (DB_TYPE == "pgsql")
3885 $reg_qpart = "~";
3886 else
3887 $reg_qpart = "REGEXP";
36184020 3888
4e02f582
AD
3889 foreach ($filter["rules"] AS $rule) {
3890 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3891 $rule['reg_exp']) !== FALSE;
36184020 3892
4e02f582 3893 if ($regexp_valid) {
36184020 3894
3972bf59 3895 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
36184020 3896
a3a896a1 3897 switch ($rule["type"]) {
4e02f582
AD
3898 case "title":
3899 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3900 $rule['reg_exp'] . "')";
3901 break;
3902 case "content":
3903 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3904 $rule['reg_exp'] . "')";
3905 break;
3906 case "both":
3907 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3908 $rule['reg_exp'] . "') OR LOWER(" .
3909 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3910 break;
3911 case "tag":
3912 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3913 $rule['reg_exp'] . "')";
3914 break;
3915 case "link":
3916 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3917 $rule['reg_exp'] . "')";
3918 break;
3919 case "author":
3920 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3921 $rule['reg_exp'] . "')";
3922 break;
3923 }
36184020 3924
ec1f8a3d
AD
3925 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3926
6b218731 3927 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3972bf59 3928 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
4e02f582 3929 }
6b8b3af8 3930
4e02f582 3931 if (isset($rule["cat_id"])) {
2680295b
AD
3932
3933 if ($rule["cat_id"] > 0) {
3934 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3935 array_push($children, $rule["cat_id"]);
3936
3937 $children = join(",", $children);
3938
3939 $cat_qpart = "cat_id IN ($children)";
3940 } else {
3941 $cat_qpart = "cat_id IS NULL";
3942 }
3943
3944 $qpart .= " AND $cat_qpart";
56fbb82c 3945 }
4e02f582
AD
3946
3947 array_push($query, "($qpart)");
3948
56fbb82c 3949 }
4e02f582 3950 }
56fbb82c 3951
4e02f582 3952 if (count($query) > 0) {
a3a896a1 3953 $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
56fbb82c 3954 } else {
a3a896a1 3955 $fullquery = "(false)";
56fbb82c 3956 }
a3a896a1
AD
3957
3958 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3959
3960 return $fullquery;
36184020 3961 }
ae5f7bb1 3962
3382bce1
AD
3963 if (!function_exists('gzdecode')) {
3964 function gzdecode($string) { // no support for 2nd argument
3965 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3966 base64_encode($string));
3967 }
3968 }
3969
8db5d8ea
AD
3970 function get_random_bytes($length) {
3971 if (function_exists('openssl_random_pseudo_bytes')) {
3972 return openssl_random_pseudo_bytes($length);
3973 } else {
3974 $output = "";
3975
3976 for ($i = 0; $i < $length; $i++)
3977 $output .= chr(mt_rand(0, 255));
3978
3979 return $output;
3980 }
3981 }
871f0a7a
AD
3982
3983 function read_stdin() {
3984 $fp = fopen("php://stdin", "r");
3985
3986 if ($fp) {
3987 $line = trim(fgets($fp));
3988 fclose($fp);
3989 return $line;
3990 }
3991
3992 return null;
3993 }
e3449aa1
AD
3994
3995 function tmpdirname($path, $prefix) {
3996 // Use PHP's tmpfile function to create a temporary
3997 // directory name. Delete the file and keep the name.
3998 $tempname = tempnam($path,$prefix);
3999 if (!$tempname)
4000 return false;
4001
4002 if (!unlink($tempname))
4003 return false;
4004
4005 return $tempname;
4006 }
4007
6aff7845
AD
4008 function getFeedCategory($link, $feed) {
4009 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
4010 WHERE id = '$feed'");
4011
4012 if (db_num_rows($result) > 0) {
4013 return db_fetch_result($result, 0, "cat_id");
4014 } else {
4015 return false;
4016 }
4017
4018 }
4019
8dcb2b47
AD
4020 function implements_interface($class, $interface) {
4021 return in_array($interface, class_implements($class));
4022 }
e88c1943 4023
e2b0054b
AD
4024 function geturl($url){
4025
4026 (function_exists('curl_init')) ? '' : die('cURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini');
4027
4028 $curl = curl_init();
4029 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4030 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4031 $header[] = "Cache-Control: max-age=0";
4032 $header[] = "Connection: keep-alive";
4033 $header[] = "Keep-Alive: 300";
4034 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4035 $header[] = "Accept-Language: en-us,en;q=0.5";
4036 $header[] = "Pragma: ";
4037
4038 curl_setopt($curl, CURLOPT_URL, $url);
4039 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4040 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
4041 curl_setopt($curl, CURLOPT_HEADER, true);
4042 curl_setopt($curl, CURLOPT_REFERER, $url);
4043 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
4044 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
4045 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
4046 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4047 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
4048
4049 $html = curl_exec($curl);
4050
4051 $status = curl_getinfo($curl);
4052 curl_close($curl);
4053
4054 if($status['http_code']!=200){
4055 if($status['http_code'] == 301 || $status['http_code'] == 302) {
4056 list($header) = explode("\r\n\r\n", $html, 2);
4057 $matches = array();
4058 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4059 $url = trim(str_replace($matches[1],"",$matches[0]));
4060 $url_parsed = parse_url($url);
4061 return (isset($url_parsed))? geturl($url, $referer):'';
4062 }
4063 $oline='';
4064 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4065 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
911845b5
AD
4066# $handle = @fopen('./curl.error.log', 'a');
4067# fwrite($handle, $line);
e2b0054b
AD
4068 return FALSE;
4069 }
4070 return $url;
4071 }
8dcb2b47 4072
c670a80d
AD
4073 function get_minified_js($files) {
4074 require_once 'lib/jshrink/Minifier.php';
4075
4076 $rv = '';
4077
4078 foreach ($files as $js) {
4079 if (!isset($_GET['debug'])) {
4080 $cached_file = CACHE_DIR . "/js/$js.js";
4081
4082 if (file_exists($cached_file) &&
4083 is_readable($cached_file) &&
4084 filemtime($cached_file) >= filemtime("js/$js.js")) {
4085
4086 $rv .= file_get_contents($cached_file);
4087
4088 } else {
4089 $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
4090 file_put_contents($cached_file, $minified);
4091 $rv .= $minified;
4092 }
4093 } else {
4094 $rv .= file_get_contents("js/$js.js");
4095 }
4096 }
4097
4098 return $rv;
4099 }
4100
b5d4716a
AD
4101 function stylesheet_tag($filename) {
4102 $timestamp = filemtime($filename);
4103
4104 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4105 }
4106
4107 function javascript_tag($filename) {
4108 $query = "";
4109
4110 if (!(strpos($filename, "?") === FALSE)) {
4111 $query = substr($filename, strpos($filename, "?")+1);
4112 $filename = substr($filename, 0, strpos($filename, "?"));
4113 }
4114
4115 $timestamp = filemtime($filename);
4116
4117 if ($query) $timestamp .= "&$query";
4118
4119 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4120 }
4121
16314dda
AD
4122 function calculate_dep_timestamp() {
4123 $files = array_merge(glob("js/*.js"), glob("*.css"));
4124
4125 $max_ts = -1;
4126
4127 foreach ($files as $file) {
4128 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4129 }
4130
4131 return $max_ts;
4132 }
4133
bcbb2ec7
AD
4134 function T_js_decl($s1, $s2) {
4135 if ($s1 && $s2) {
4136 $s1 = preg_replace("/\n/", "", $s1);
4137 $s2 = preg_replace("/\n/", "", $s2);
4138
4139 $s1 = preg_replace("/\"/", "\\\"", $s1);
4140 $s2 = preg_replace("/\"/", "\\\"", $s2);
4141
4142 return "T_messages[\"$s1\"] = \"$s2\";\n";
4143 }
4144 }
4145
4146 function init_js_translations() {
4147
4148 print 'var T_messages = new Object();
4149
4150 function __(msg) {
4151 if (T_messages[msg]) {
4152 return T_messages[msg];
4153 } else {
4154 return msg;
4155 }
4156 }
4157
4158 function ngettext(msg1, msg2, n) {
4159 return (parseInt(n) > 1) ? msg2 : msg1;
4160 }';
4161
4162 $l10n = _get_reader();
4163
4164 for ($i = 0; $i < $l10n->total; $i++) {
4165 $orig = $l10n->get_original_string($i);
4166 $translation = __($orig);
4167
4168 print T_js_decl($orig, $translation);
4169 }
4170 }
4171
f822a8e5
AD
4172 function label_to_feed_id($label) {
4173 return LABEL_BASE_INDEX - 1 - abs($label);
4174 }
4175
4176 function feed_to_label_id($feed) {
4177 return LABEL_BASE_INDEX - 1 + abs($feed);
4178 }
4179
8c0496f7 4180?>