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