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