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