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