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