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