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