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