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