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