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