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