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