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