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