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