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