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