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