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