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