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