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