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