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