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