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