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