]> git.wh0rd.org - tt-rss.git/blob - include/functions.php
1c1b73a376480b52bcc3669ded3238ac9dd19cf4
[tt-rss.git] / include / functions.php
1 <?php
2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 114);
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, view_settings, 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["view_settings"])
1153 $view_settings = @json_decode($line["view_settings"]);
1154 else
1155 $view_settings = null; */
1156
1157 $view_settings = null;
1158
1159 if ($line["num_children"] > 0) {
1160 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1161 } else {
1162 $child_counter = 0;
1163 }
1164
1165 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1166 "vs" => $view_settings,
1167 "counter" => $line["unread"] + $child_counter);
1168
1169 array_push($ret_arr, $cv);
1170 }
1171
1172 /* Special case: NULL category doesn't actually exist in the DB */
1173
1174 $cv = array("id" => 0, "kind" => "cat",
1175 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1176
1177 array_push($ret_arr, $cv);
1178
1179 return $ret_arr;
1180 }
1181
1182 // only accepts real cats (>= 0)
1183 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1184 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1185
1186 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1187 AND owner_uid = $owner_uid");
1188
1189 $unread = 0;
1190
1191 while ($line = db_fetch_assoc($result)) {
1192 $unread += getCategoryUnread($link, $line["id"], $owner_uid);
1193 $unread += getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1194 }
1195
1196 return $unread;
1197 }
1198
1199 function getCategoryUnread($link, $cat, $owner_uid = false) {
1200
1201 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1202
1203 if ($cat >= 0) {
1204
1205 if ($cat != 0) {
1206 $cat_query = "cat_id = '$cat'";
1207 } else {
1208 $cat_query = "cat_id IS NULL";
1209 }
1210
1211 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1212 AND owner_uid = " . $owner_uid);
1213
1214 $cat_feeds = array();
1215 while ($line = db_fetch_assoc($result)) {
1216 array_push($cat_feeds, "feed_id = " . $line["id"]);
1217 }
1218
1219 if (count($cat_feeds) == 0) return 0;
1220
1221 $match_part = implode(" OR ", $cat_feeds);
1222
1223 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1224 FROM ttrss_user_entries
1225 WHERE unread = true AND ($match_part)
1226 AND owner_uid = " . $owner_uid);
1227
1228 $unread = 0;
1229
1230 # this needs to be rewritten
1231 while ($line = db_fetch_assoc($result)) {
1232 $unread += $line["unread"];
1233 }
1234
1235 return $unread;
1236 } else if ($cat == -1) {
1237 return getFeedUnread($link, -1) + getFeedUnread($link, -2) + getFeedUnread($link, -3) + getFeedUnread($link, 0);
1238 } else if ($cat == -2) {
1239
1240 $result = db_query($link, "
1241 SELECT COUNT(unread) AS unread FROM
1242 ttrss_user_entries, ttrss_user_labels2
1243 WHERE article_id = ref_id AND unread = true
1244 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1245
1246 $unread = db_fetch_result($result, 0, "unread");
1247
1248 return $unread;
1249
1250 }
1251 }
1252
1253 function getFeedUnread($link, $feed, $is_cat = false) {
1254 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1255 }
1256
1257 function getLabelUnread($link, $label_id, $owner_uid = false) {
1258 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1259
1260 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1261 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1262
1263 if (db_num_rows($result) != 0) {
1264 return db_fetch_result($result, 0, "unread");
1265 } else {
1266 return 0;
1267 }
1268 }
1269
1270 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1271 $owner_uid = false) {
1272
1273 $n_feed = (int) $feed;
1274 $need_entries = false;
1275
1276 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1277
1278 if ($unread_only) {
1279 $unread_qpart = "unread = true";
1280 } else {
1281 $unread_qpart = "true";
1282 }
1283
1284 if ($is_cat) {
1285 return getCategoryUnread($link, $n_feed, $owner_uid);
1286 } else if ($n_feed == -6) {
1287 return 0;
1288 } else if ($feed != "0" && $n_feed == 0) {
1289
1290 $feed = db_escape_string($link, $feed);
1291
1292 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1293 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1294 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1295 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1296 return db_fetch_result($result, 0, "count");
1297
1298 } else if ($n_feed == -1) {
1299 $match_part = "marked = true";
1300 } else if ($n_feed == -2) {
1301 $match_part = "published = true";
1302 } else if ($n_feed == -3) {
1303 $match_part = "unread = true AND score >= 0";
1304
1305 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1306
1307 if (DB_TYPE == "pgsql") {
1308 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1309 } else {
1310 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1311 }
1312
1313 $need_entries = true;
1314
1315 } else if ($n_feed == -4) {
1316 $match_part = "true";
1317 } else if ($n_feed >= 0) {
1318
1319 if ($n_feed != 0) {
1320 $match_part = "feed_id = '$n_feed'";
1321 } else {
1322 $match_part = "feed_id IS NULL";
1323 }
1324
1325 } else if ($feed < LABEL_BASE_INDEX) {
1326
1327 $label_id = feed_to_label_id($feed);
1328
1329 return getLabelUnread($link, $label_id, $owner_uid);
1330
1331 }
1332
1333 if ($match_part) {
1334
1335 if ($need_entries) {
1336 $from_qpart = "ttrss_user_entries,ttrss_entries";
1337 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1338 } else {
1339 $from_qpart = "ttrss_user_entries";
1340 }
1341
1342 $query = "SELECT count(int_id) AS unread
1343 FROM $from_qpart WHERE
1344 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1345
1346 //echo "[$feed/$query]\n";
1347
1348 $result = db_query($link, $query);
1349
1350 } else {
1351
1352 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1353 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1354 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1355 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1356 }
1357
1358 $unread = db_fetch_result($result, 0, "unread");
1359
1360 return $unread;
1361 }
1362
1363 function getGlobalUnread($link, $user_id = false) {
1364
1365 if (!$user_id) {
1366 $user_id = $_SESSION["uid"];
1367 }
1368
1369 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1370 WHERE owner_uid = '$user_id' AND feed_id > 0");
1371
1372 $c_id = db_fetch_result($result, 0, "c_id");
1373
1374 return $c_id;
1375 }
1376
1377 function getGlobalCounters($link, $global_unread = -1) {
1378 $ret_arr = array();
1379
1380 if ($global_unread == -1) {
1381 $global_unread = getGlobalUnread($link);
1382 }
1383
1384 $cv = array("id" => "global-unread",
1385 "counter" => (int) $global_unread);
1386
1387 array_push($ret_arr, $cv);
1388
1389 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1390 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1391
1392 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1393
1394 $cv = array("id" => "subscribed-feeds",
1395 "counter" => (int) $subscribed_feeds);
1396
1397 array_push($ret_arr, $cv);
1398
1399 return $ret_arr;
1400 }
1401
1402 function getVirtCounters($link) {
1403
1404 $ret_arr = array();
1405
1406 for ($i = 0; $i >= -4; $i--) {
1407
1408 $count = getFeedUnread($link, $i);
1409
1410 $cv = array("id" => $i,
1411 "counter" => (int) $count);
1412
1413 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1414 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1415
1416 array_push($ret_arr, $cv);
1417 }
1418
1419 global $pluginhost;
1420
1421 if ($pluginhost) {
1422 $feeds = $pluginhost->get_feeds(-1);
1423
1424 if (is_array($feeds)) {
1425 foreach ($feeds as $feed) {
1426 $cv = array("id" => PluginHost::pfeed_to_feed_id($feed['id']),
1427 "counter" => $feed['sender']->get_unread($feed['id']));
1428
1429 array_push($ret_arr, $cv);
1430 }
1431 }
1432 }
1433
1434 return $ret_arr;
1435 }
1436
1437 function getLabelCounters($link, $descriptions = false) {
1438
1439 $ret_arr = array();
1440
1441 $owner_uid = $_SESSION["uid"];
1442
1443 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1444 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1445 (ttrss_labels2.id = label_id)
1446 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1447 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1448 ttrss_labels2.caption");
1449
1450 while ($line = db_fetch_assoc($result)) {
1451
1452 $id = label_to_feed_id($line["id"]);
1453
1454 $label_name = $line["caption"];
1455 $count = $line["unread"];
1456
1457 $cv = array("id" => $id,
1458 "counter" => (int) $count);
1459
1460 if ($descriptions)
1461 $cv["description"] = $label_name;
1462
1463 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1464 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1465
1466 array_push($ret_arr, $cv);
1467 }
1468
1469 return $ret_arr;
1470 }
1471
1472 function getFeedCounters($link, $active_feed = false) {
1473
1474 $ret_arr = array();
1475
1476 $query = "SELECT ttrss_feeds.id, view_settings,
1477 ttrss_feeds.title,
1478 ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated,
1479 last_error, value AS count
1480 FROM ttrss_feeds, ttrss_counters_cache
1481 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1482 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1483 AND ttrss_counters_cache.feed_id = id";
1484
1485 $result = db_query($link, $query);
1486 $fctrs_modified = false;
1487
1488 while ($line = db_fetch_assoc($result)) {
1489
1490 $id = $line["id"];
1491 $count = $line["count"];
1492 $last_error = htmlspecialchars($line["last_error"]);
1493
1494 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1495
1496 $has_img = feed_has_icon($id);
1497
1498 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1499 $last_updated = '';
1500
1501 /* if ($line["view_settings"])
1502 $view_settings = @json_decode($line["view_settings"]);
1503 else
1504 $view_settings = null; */
1505
1506 $view_settings = null;
1507
1508 $cv = array("id" => $id,
1509 "updated" => $last_updated,
1510 "vs" => $view_settings,
1511 "counter" => (int) $count,
1512 "has_img" => (int) $has_img);
1513
1514 if ($last_error)
1515 $cv["error"] = $last_error;
1516
1517 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1518 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1519
1520 if ($active_feed && $id == $active_feed)
1521 $cv["title"] = truncate_string($line["title"], 30);
1522
1523 array_push($ret_arr, $cv);
1524
1525 }
1526
1527 return $ret_arr;
1528 }
1529
1530 function get_pgsql_version($link) {
1531 $result = db_query($link, "SELECT version() AS version");
1532 $version = explode(" ", db_fetch_result($result, 0, "version"));
1533 return $version[1];
1534 }
1535
1536 /**
1537 * @return array (code => Status code, message => error message if available)
1538 *
1539 * 0 - OK, Feed already exists
1540 * 1 - OK, Feed added
1541 * 2 - Invalid URL
1542 * 3 - URL content is HTML, no feeds available
1543 * 4 - URL content is HTML which contains multiple feeds.
1544 * Here you should call extractfeedurls in rpc-backend
1545 * to get all possible feeds.
1546 * 5 - Couldn't download the URL content.
1547 */
1548 function subscribe_to_feed($link, $url, $cat_id = 0,
1549 $auth_login = '', $auth_pass = '') {
1550
1551 global $fetch_last_error;
1552
1553 require_once "include/rssfuncs.php";
1554
1555 $url = fix_url($url);
1556
1557 if (!$url || !validate_feed_url($url)) return array("code" => 2);
1558
1559 $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass);
1560
1561 if (!$contents) {
1562 return array("code" => 5, "message" => $fetch_last_error);
1563 }
1564
1565 if (is_html($contents)) {
1566 $feedUrls = get_feeds_from_html($url, $contents);
1567
1568 if (count($feedUrls) == 0) {
1569 return array("code" => 3);
1570 } else if (count($feedUrls) > 1) {
1571 return array("code" => 4, "feeds" => $feedUrls);
1572 }
1573 //use feed url as new URL
1574 $url = key($feedUrls);
1575 }
1576
1577 if ($cat_id == "0" || !$cat_id) {
1578 $cat_qpart = "NULL";
1579 } else {
1580 $cat_qpart = "'$cat_id'";
1581 }
1582
1583 $result = db_query($link,
1584 "SELECT id FROM ttrss_feeds
1585 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1586
1587 if (db_num_rows($result) == 0) {
1588 $result = db_query($link,
1589 "INSERT INTO ttrss_feeds
1590 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1591 VALUES ('".$_SESSION["uid"]."', '$url',
1592 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1593
1594 $result = db_query($link,
1595 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1596 AND owner_uid = " . $_SESSION["uid"]);
1597
1598 $feed_id = db_fetch_result($result, 0, "id");
1599
1600 if ($feed_id) {
1601 update_rss_feed($link, $feed_id, true);
1602 }
1603
1604 return array("code" => 1);
1605 } else {
1606 return array("code" => 0);
1607 }
1608 }
1609
1610 function print_feed_select($link, $id, $default_id = "",
1611 $attributes = "", $include_all_feeds = true,
1612 $root_id = false, $nest_level = 0) {
1613
1614 if (!$root_id) {
1615 print "<select id=\"$id\" name=\"$id\" $attributes>";
1616 if ($include_all_feeds) {
1617 $is_selected = ("0" == $default_id) ? "selected=\"1\"" : "";
1618 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1619 }
1620 }
1621
1622 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1623
1624 if ($root_id)
1625 $parent_qpart = "parent_cat = '$root_id'";
1626 else
1627 $parent_qpart = "parent_cat IS NULL";
1628
1629 $result = db_query($link, "SELECT id,title,
1630 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1631 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1632 FROM ttrss_feed_categories
1633 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1634
1635 while ($line = db_fetch_assoc($result)) {
1636
1637 for ($i = 0; $i < $nest_level; $i++)
1638 $line["title"] = " - " . $line["title"];
1639
1640 $is_selected = ("CAT:".$line["id"] == $default_id) ? "selected=\"1\"" : "";
1641
1642 printf("<option $is_selected value='CAT:%d'>%s</option>",
1643 $line["id"], htmlspecialchars($line["title"]));
1644
1645 if ($line["num_children"] > 0)
1646 print_feed_select($link, $id, $default_id, $attributes,
1647 $include_all_feeds, $line["id"], $nest_level+1);
1648
1649 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1650 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1651
1652 while ($fline = db_fetch_assoc($feed_result)) {
1653 $is_selected = ($fline["id"] == $default_id) ? "selected=\"1\"" : "";
1654
1655 $fline["title"] = " + " . $fline["title"];
1656
1657 for ($i = 0; $i < $nest_level; $i++)
1658 $fline["title"] = " - " . $fline["title"];
1659
1660 printf("<option $is_selected value='%d'>%s</option>",
1661 $fline["id"], htmlspecialchars($fline["title"]));
1662 }
1663 }
1664
1665 if (!$root_id) {
1666 $is_selected = ($default_id == "CAT:0") ? "selected=\"1\"" : "";
1667
1668 printf("<option $is_selected value='CAT:0'>%s</option>",
1669 __("Uncategorized"));
1670
1671 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1672 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1673
1674 while ($fline = db_fetch_assoc($feed_result)) {
1675 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ? "selected=\"1\"" : "";
1676
1677 $fline["title"] = " + " . $fline["title"];
1678
1679 for ($i = 0; $i < $nest_level; $i++)
1680 $fline["title"] = " - " . $fline["title"];
1681
1682 printf("<option $is_selected value='%d'>%s</option>",
1683 $fline["id"], htmlspecialchars($fline["title"]));
1684 }
1685 }
1686
1687 } else {
1688 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1689 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1690
1691 while ($line = db_fetch_assoc($result)) {
1692
1693 $is_selected = ($line["id"] == $default_id) ? "selected=\"1\"" : "";
1694
1695 printf("<option $is_selected value='%d'>%s</option>",
1696 $line["id"], htmlspecialchars($line["title"]));
1697 }
1698 }
1699
1700 if (!$root_id) {
1701 print "</select>";
1702 }
1703 }
1704
1705 function print_feed_cat_select($link, $id, $default_id,
1706 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1707
1708 if (!$root_id) {
1709 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1710 }
1711
1712 if ($root_id)
1713 $parent_qpart = "parent_cat = '$root_id'";
1714 else
1715 $parent_qpart = "parent_cat IS NULL";
1716
1717 $result = db_query($link, "SELECT id,title,
1718 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1719 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1720 FROM ttrss_feed_categories
1721 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1722
1723 while ($line = db_fetch_assoc($result)) {
1724 if ($line["id"] == $default_id) {
1725 $is_selected = "selected=\"1\"";
1726 } else {
1727 $is_selected = "";
1728 }
1729
1730 for ($i = 0; $i < $nest_level; $i++)
1731 $line["title"] = " - " . $line["title"];
1732
1733 if ($line["title"])
1734 printf("<option $is_selected value='%d'>%s</option>",
1735 $line["id"], htmlspecialchars($line["title"]));
1736
1737 if ($line["num_children"] > 0)
1738 print_feed_cat_select($link, $id, $default_id, $attributes,
1739 $include_all_cats, $line["id"], $nest_level+1);
1740 }
1741
1742 if (!$root_id) {
1743 if ($include_all_cats) {
1744 if (db_num_rows($result) > 0) {
1745 print "<option disabled=\"1\">--------</option>";
1746 }
1747
1748 if ($default_id == 0) {
1749 $is_selected = "selected=\"1\"";
1750 } else {
1751 $is_selected = "";
1752 }
1753
1754 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1755 }
1756 print "</select>";
1757 }
1758 }
1759
1760 function checkbox_to_sql_bool($val) {
1761 return ($val == "on") ? "true" : "false";
1762 }
1763
1764 function getFeedCatTitle($link, $id) {
1765 if ($id == -1) {
1766 return __("Special");
1767 } else if ($id < LABEL_BASE_INDEX) {
1768 return __("Labels");
1769 } else if ($id > 0) {
1770 $result = db_query($link, "SELECT ttrss_feed_categories.title
1771 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1772 cat_id = ttrss_feed_categories.id");
1773 if (db_num_rows($result) == 1) {
1774 return db_fetch_result($result, 0, "title");
1775 } else {
1776 return __("Uncategorized");
1777 }
1778 } else {
1779 return "getFeedCatTitle($id) failed";
1780 }
1781
1782 }
1783
1784 function getFeedIcon($id) {
1785 switch ($id) {
1786 case 0:
1787 return "images/archive.png";
1788 break;
1789 case -1:
1790 return "images/mark_set.svg";
1791 break;
1792 case -2:
1793 return "images/pub_set.svg";
1794 break;
1795 case -3:
1796 return "images/fresh.png";
1797 break;
1798 case -4:
1799 return "images/tag.png";
1800 break;
1801 case -6:
1802 return "images/recently_read.png";
1803 break;
1804 default:
1805 if ($id < LABEL_BASE_INDEX) {
1806 return "images/label.png";
1807 } else {
1808 if (file_exists(ICONS_DIR . "/$id.ico"))
1809 return ICONS_URL . "/$id.ico";
1810 }
1811 break;
1812 }
1813 }
1814
1815 function getFeedTitle($link, $id, $cat = false) {
1816 if ($cat) {
1817 return getCategoryTitle($link, $id);
1818 } else if ($id == -1) {
1819 return __("Starred articles");
1820 } else if ($id == -2) {
1821 return __("Published articles");
1822 } else if ($id == -3) {
1823 return __("Fresh articles");
1824 } else if ($id == -4) {
1825 return __("All articles");
1826 } else if ($id === 0 || $id === "0") {
1827 return __("Archived articles");
1828 } else if ($id == -6) {
1829 return __("Recently read");
1830 } else if ($id < LABEL_BASE_INDEX) {
1831 $label_id = feed_to_label_id($id);
1832 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1833 if (db_num_rows($result) == 1) {
1834 return db_fetch_result($result, 0, "caption");
1835 } else {
1836 return "Unknown label ($label_id)";
1837 }
1838
1839 } else if (is_numeric($id) && $id > 0) {
1840 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1841 if (db_num_rows($result) == 1) {
1842 return db_fetch_result($result, 0, "title");
1843 } else {
1844 return "Unknown feed ($id)";
1845 }
1846 } else {
1847 return $id;
1848 }
1849 }
1850
1851 function make_init_params($link) {
1852 $params = array();
1853
1854 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1855 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1856 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1857 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1858
1859 $params[strtolower($param)] = (int) get_pref($link, $param);
1860 }
1861
1862 $params["icons_url"] = ICONS_URL;
1863 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
1864 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1865 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1866 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1867 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1868 $params["label_base_index"] = (int) LABEL_BASE_INDEX;
1869
1870 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1871 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1872
1873 $max_feed_id = db_fetch_result($result, 0, "mid");
1874 $num_feeds = db_fetch_result($result, 0, "nf");
1875
1876 $params["max_feed_id"] = (int) $max_feed_id;
1877 $params["num_feeds"] = (int) $num_feeds;
1878
1879 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1880 $params["hotkeys"] = get_hotkeys_map($link);
1881
1882 $params["csrf_token"] = $_SESSION["csrf_token"];
1883 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1884
1885 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
1886
1887 return $params;
1888 }
1889
1890 function get_hotkeys_info($link) {
1891 $hotkeys = array(
1892 __("Navigation") => array(
1893 "next_feed" => __("Open next feed"),
1894 "prev_feed" => __("Open previous feed"),
1895 "next_article" => __("Open next article"),
1896 "prev_article" => __("Open previous article"),
1897 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1898 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1899 "search_dialog" => __("Show search dialog")),
1900 __("Article") => array(
1901 "toggle_mark" => __("Toggle starred"),
1902 "toggle_publ" => __("Toggle published"),
1903 "toggle_unread" => __("Toggle unread"),
1904 "edit_tags" => __("Edit tags"),
1905 "dismiss_selected" => __("Dismiss selected"),
1906 "dismiss_read" => __("Dismiss read"),
1907 "open_in_new_window" => __("Open in new window"),
1908 "catchup_below" => __("Mark below as read"),
1909 "catchup_above" => __("Mark above as read"),
1910 "article_scroll_down" => __("Scroll down"),
1911 "article_scroll_up" => __("Scroll up"),
1912 "select_article_cursor" => __("Select article under cursor"),
1913 "email_article" => __("Email article"),
1914 "close_article" => __("Close/collapse article"),
1915 "toggle_widescreen" => __("Toggle widescreen mode"),
1916 "toggle_embed_original" => __("Toggle embed original")),
1917 __("Article selection") => array(
1918 "select_all" => __("Select all articles"),
1919 "select_unread" => __("Select unread"),
1920 "select_marked" => __("Select starred"),
1921 "select_published" => __("Select published"),
1922 "select_invert" => __("Invert selection"),
1923 "select_none" => __("Deselect everything")),
1924 __("Feed") => array(
1925 "feed_refresh" => __("Refresh current feed"),
1926 "feed_unhide_read" => __("Un/hide read feeds"),
1927 "feed_subscribe" => __("Subscribe to feed"),
1928 "feed_edit" => __("Edit feed"),
1929 "feed_catchup" => __("Mark as read"),
1930 "feed_reverse" => __("Reverse headlines"),
1931 "feed_debug_update" => __("Debug feed update"),
1932 "catchup_all" => __("Mark all feeds as read"),
1933 "cat_toggle_collapse" => __("Un/collapse current category"),
1934 "toggle_combined_mode" => __("Toggle combined mode"),
1935 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1936 __("Go to") => array(
1937 "goto_all" => __("All articles"),
1938 "goto_fresh" => __("Fresh"),
1939 "goto_marked" => __("Starred"),
1940 "goto_published" => __("Published"),
1941 "goto_tagcloud" => __("Tag cloud"),
1942 "goto_prefs" => __("Preferences")),
1943 __("Other") => array(
1944 "create_label" => __("Create label"),
1945 "create_filter" => __("Create filter"),
1946 "collapse_sidebar" => __("Un/collapse sidebar"),
1947 "help_dialog" => __("Show help dialog"))
1948 );
1949
1950 return $hotkeys;
1951 }
1952
1953 function get_hotkeys_map($link) {
1954 $hotkeys = array(
1955 // "navigation" => array(
1956 "k" => "next_feed",
1957 "j" => "prev_feed",
1958 "n" => "next_article",
1959 "p" => "prev_article",
1960 "(38)|up" => "prev_article",
1961 "(40)|down" => "next_article",
1962 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1963 // "^(40)|Ctrl-down" => "next_article_noscroll",
1964 "(191)|/" => "search_dialog",
1965 // "article" => array(
1966 "s" => "toggle_mark",
1967 "*s" => "toggle_publ",
1968 "u" => "toggle_unread",
1969 "*t" => "edit_tags",
1970 "*d" => "dismiss_selected",
1971 "*x" => "dismiss_read",
1972 "o" => "open_in_new_window",
1973 "c p" => "catchup_below",
1974 "c n" => "catchup_above",
1975 "*n" => "article_scroll_down",
1976 "*p" => "article_scroll_up",
1977 "*(38)|Shift+up" => "article_scroll_up",
1978 "*(40)|Shift+down" => "article_scroll_down",
1979 "a *w" => "toggle_widescreen",
1980 "a e" => "toggle_embed_original",
1981 "e" => "email_article",
1982 "a q" => "close_article",
1983 // "article_selection" => array(
1984 "a a" => "select_all",
1985 "a u" => "select_unread",
1986 "a *u" => "select_marked",
1987 "a p" => "select_published",
1988 "a i" => "select_invert",
1989 "a n" => "select_none",
1990 // "feed" => array(
1991 "f r" => "feed_refresh",
1992 "f a" => "feed_unhide_read",
1993 "f s" => "feed_subscribe",
1994 "f e" => "feed_edit",
1995 "f q" => "feed_catchup",
1996 "f x" => "feed_reverse",
1997 "f *d" => "feed_debug_update",
1998 "f *c" => "toggle_combined_mode",
1999 "f c" => "toggle_cdm_expanded",
2000 "*q" => "catchup_all",
2001 "x" => "cat_toggle_collapse",
2002 // "goto" => array(
2003 "g a" => "goto_all",
2004 "g f" => "goto_fresh",
2005 "g s" => "goto_marked",
2006 "g p" => "goto_published",
2007 "g t" => "goto_tagcloud",
2008 "g *p" => "goto_prefs",
2009 // "other" => array(
2010 "(9)|Tab" => "select_article_cursor", // tab
2011 "c l" => "create_label",
2012 "c f" => "create_filter",
2013 "c s" => "collapse_sidebar",
2014 "^(191)|Ctrl+/" => "help_dialog",
2015 );
2016
2017 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2018 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2019 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2020 }
2021
2022 global $pluginhost;
2023 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP) as $plugin) {
2024 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2025 }
2026
2027 $prefixes = array();
2028
2029 foreach (array_keys($hotkeys) as $hotkey) {
2030 $pair = explode(" ", $hotkey, 2);
2031
2032 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2033 array_push($prefixes, $pair[0]);
2034 }
2035 }
2036
2037 return array($prefixes, $hotkeys);
2038 }
2039
2040 function make_runtime_info($link) {
2041 $data = array();
2042
2043 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2044 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2045
2046 $max_feed_id = db_fetch_result($result, 0, "mid");
2047 $num_feeds = db_fetch_result($result, 0, "nf");
2048
2049 $data["max_feed_id"] = (int) $max_feed_id;
2050 $data["num_feeds"] = (int) $num_feeds;
2051
2052 $data['last_article_id'] = getLastArticleId($link);
2053 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2054
2055 $data['dep_ts'] = calculate_dep_timestamp();
2056 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2057
2058 if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
2059
2060 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2061
2062 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2063
2064 $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
2065
2066 if ($stamp) {
2067 $stamp_delta = time() - $stamp;
2068
2069 if ($stamp_delta > 1800) {
2070 $stamp_check = 0;
2071 } else {
2072 $stamp_check = 1;
2073 $_SESSION["daemon_stamp_check"] = time();
2074 }
2075
2076 $data['daemon_stamp_ok'] = $stamp_check;
2077
2078 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2079
2080 $data['daemon_stamp'] = $stamp_fmt;
2081 }
2082 }
2083 }
2084
2085 if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
2086 $new_version_details = @check_for_update($link);
2087
2088 $data['new_version_available'] = (int) ($new_version_details != false);
2089
2090 $_SESSION["last_version_check"] = time();
2091 $_SESSION["version_data"] = $new_version_details;
2092 }
2093
2094 return $data;
2095 }
2096
2097 function search_to_sql($link, $search) {
2098
2099 $search_query_part = "";
2100
2101 $keywords = explode(" ", $search);
2102 $query_keywords = array();
2103
2104 foreach ($keywords as $k) {
2105 if (strpos($k, "-") === 0) {
2106 $k = substr($k, 1);
2107 $not = "NOT";
2108 } else {
2109 $not = "";
2110 }
2111
2112 $commandpair = explode(":", mb_strtolower($k), 2);
2113
2114 if ($commandpair[0] == "note" && $commandpair[1]) {
2115
2116 if ($commandpair[1] == "true")
2117 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2118 else
2119 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2120
2121 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2122
2123 if ($commandpair[1] == "true")
2124 array_push($query_keywords, "($not (marked = true))");
2125 else
2126 array_push($query_keywords, "($not (marked = false))");
2127
2128 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2129
2130 if ($commandpair[1] == "true")
2131 array_push($query_keywords, "($not (published = true))");
2132 else
2133 array_push($query_keywords, "($not (published = false))");
2134
2135 } else if (strpos($k, "@") === 0) {
2136
2137 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2138 $orig_ts = strtotime(substr($k, 1));
2139 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2140
2141 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2142
2143 array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
2144 } else {
2145 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2146 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2147 }
2148 }
2149
2150 $search_query_part = implode("AND", $query_keywords);
2151
2152 return $search_query_part;
2153 }
2154
2155 function getParentCategories($link, $cat, $owner_uid) {
2156 $rv = array();
2157
2158 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2159 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2160
2161 while ($line = db_fetch_assoc($result)) {
2162 array_push($rv, $line["parent_cat"]);
2163 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2164 }
2165
2166 return $rv;
2167 }
2168
2169 function getChildCategories($link, $cat, $owner_uid) {
2170 $rv = array();
2171
2172 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2173 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2174
2175 while ($line = db_fetch_assoc($result)) {
2176 array_push($rv, $line["id"]);
2177 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2178 }
2179
2180 return $rv;
2181 }
2182
2183 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) {
2184
2185 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2186
2187 $ext_tables_part = "";
2188
2189 if ($search) {
2190
2191 if (SPHINX_ENABLED) {
2192 $ids = join(",", @sphinx_search($search, 0, 500));
2193
2194 if ($ids)
2195 $search_query_part = "ref_id IN ($ids) AND ";
2196 else
2197 $search_query_part = "ref_id = -1 AND ";
2198
2199 } else {
2200 $search_query_part = search_to_sql($link, $search);
2201 $search_query_part .= " AND ";
2202 }
2203
2204 } else {
2205 $search_query_part = "";
2206 }
2207
2208 if ($filter) {
2209
2210 if (DB_TYPE == "pgsql") {
2211 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2212 } else {
2213 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2214 }
2215
2216 $override_order = "updated DESC";
2217
2218 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2219
2220 // Try to check if SQL regexp implementation chokes on a valid regexp
2221 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2222 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2223 WHERE $filter_query_part LIMIT 1", false);
2224
2225 if ($result) {
2226 $test = db_fetch_result($result, 0, "true_val");
2227
2228 if (!$test) {
2229 $filter_query_part = "false AND";
2230 } else {
2231 $filter_query_part .= " AND";
2232 }
2233 } else {
2234 $filter_query_part = "false AND";
2235 }
2236
2237 } else {
2238 $filter_query_part = "";
2239 }
2240
2241 if ($since_id) {
2242 $since_id_part = "ttrss_entries.id > $since_id AND ";
2243 } else {
2244 $since_id_part = "";
2245 }
2246
2247 $view_query_part = "";
2248
2249 if ($view_mode == "adaptive") {
2250 if ($search) {
2251 $view_query_part = " ";
2252 } else if ($feed != -1) {
2253
2254 $unread = getFeedUnread($link, $feed, $cat_view);
2255
2256 if ($cat_view && $feed > 0 && $include_children)
2257 $unread += getCategoryChildrenUnread($link, $feed);
2258
2259 if ($unread > 0)
2260 $view_query_part = " unread = true AND ";
2261
2262 }
2263 }
2264
2265 if ($view_mode == "marked") {
2266 $view_query_part = " marked = true AND ";
2267 }
2268
2269 if ($view_mode == "has_note") {
2270 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2271 }
2272
2273 if ($view_mode == "published") {
2274 $view_query_part = " published = true AND ";
2275 }
2276
2277 if ($view_mode == "unread" && $feed != -6) {
2278 $view_query_part = " unread = true AND ";
2279 }
2280
2281 if ($limit > 0) {
2282 $limit_query_part = "LIMIT " . $limit;
2283 }
2284
2285 $allow_archived = false;
2286
2287 $vfeed_query_part = "";
2288
2289 // override query strategy and enable feed display when searching globally
2290 if ($search && $search_mode == "all_feeds") {
2291 $query_strategy_part = "true";
2292 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2293 /* tags */
2294 } else if (!is_numeric($feed)) {
2295 $query_strategy_part = "true";
2296 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2297 id = feed_id) as feed_title,";
2298 } else if ($search && $search_mode == "this_cat") {
2299 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2300
2301 if ($feed > 0) {
2302 if ($include_children) {
2303 $subcats = getChildCategories($link, $feed, $owner_uid);
2304 array_push($subcats, $feed);
2305 $cats_qpart = join(",", $subcats);
2306 } else {
2307 $cats_qpart = $feed;
2308 }
2309
2310 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2311
2312 } else {
2313 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2314 }
2315
2316 } else if ($feed > 0) {
2317
2318 if ($cat_view) {
2319
2320 if ($feed > 0) {
2321 if ($include_children) {
2322 # sub-cats
2323 $subcats = getChildCategories($link, $feed, $owner_uid);
2324
2325 array_push($subcats, $feed);
2326 $query_strategy_part = "cat_id IN (".
2327 implode(",", $subcats).")";
2328
2329 } else {
2330 $query_strategy_part = "cat_id = '$feed'";
2331 }
2332
2333 } else {
2334 $query_strategy_part = "cat_id IS NULL";
2335 }
2336
2337 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2338
2339 } else {
2340 $query_strategy_part = "feed_id = '$feed'";
2341 }
2342 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2343 $query_strategy_part = "feed_id IS NULL";
2344 $allow_archived = true;
2345 } else if ($feed == 0 && $cat_view) { // uncategorized
2346 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2347 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2348 } else if ($feed == -1) { // starred virtual feed
2349 $query_strategy_part = "marked = true";
2350 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2351 $allow_archived = true;
2352
2353 if (!$override_order) {
2354 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2355 }
2356
2357 } else if ($feed == -2) { // published virtual feed OR labels category
2358
2359 if (!$cat_view) {
2360 $query_strategy_part = "published = true";
2361 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2362 $allow_archived = true;
2363
2364 if (!$override_order) {
2365 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2366 }
2367
2368 } else {
2369 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2370
2371 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2372
2373 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2374 ttrss_user_labels2.article_id = ref_id";
2375
2376 }
2377 } else if ($feed == -6) { // recently read
2378 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2379 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2380 $allow_archived = true;
2381
2382 if (!$override_order) $override_order = "last_read DESC";
2383 } else if ($feed == -3) { // fresh virtual feed
2384 $query_strategy_part = "unread = true AND score >= 0";
2385
2386 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2387
2388 if (DB_TYPE == "pgsql") {
2389 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2390 } else {
2391 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2392 }
2393
2394 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2395 } else if ($feed == -4) { // all articles virtual feed
2396 $query_strategy_part = "true";
2397 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2398 } else if ($feed <= LABEL_BASE_INDEX) { // labels
2399 $label_id = feed_to_label_id($feed);
2400
2401 $query_strategy_part = "label_id = '$label_id' AND
2402 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2403 ttrss_user_labels2.article_id = ref_id";
2404
2405 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2406 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2407 $allow_archived = true;
2408
2409 } else {
2410 $query_strategy_part = "true";
2411 }
2412
2413 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2414 $date_sort_field = "updated";
2415 } else {
2416 $date_sort_field = "date_entered";
2417 }
2418
2419 $order_by = "$date_sort_field DESC, updated DESC";
2420
2421 if ($view_mode == "unread_first") {
2422 $order_by = "unread DESC, $order_by";
2423 }
2424
2425 if ($override_order) {
2426 $order_by = $override_order;
2427 }
2428
2429 $feed_title = "";
2430
2431 if ($search) {
2432 $feed_title = T_sprintf("Search results: %s", $search);
2433 } else {
2434 if ($cat_view) {
2435 $feed_title = getCategoryTitle($link, $feed);
2436 } else {
2437 if (is_numeric($feed) && $feed > 0) {
2438 $result = db_query($link, "SELECT title,site_url,last_error
2439 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2440
2441 $feed_title = db_fetch_result($result, 0, "title");
2442 $feed_site_url = db_fetch_result($result, 0, "site_url");
2443 $last_error = db_fetch_result($result, 0, "last_error");
2444 } else {
2445 $feed_title = getFeedTitle($link, $feed);
2446 }
2447 }
2448 }
2449
2450 $content_query_part = "content as content_preview, cached_content, ";
2451
2452 if (is_numeric($feed)) {
2453
2454 if ($feed >= 0) {
2455 $feed_kind = "Feeds";
2456 } else {
2457 $feed_kind = "Labels";
2458 }
2459
2460 if ($limit_query_part) {
2461 $offset_query_part = "OFFSET $offset";
2462 }
2463
2464 // proper override_order applied above
2465 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2466 if (!$override_order) {
2467 $order_by = "ttrss_feeds.title, $order_by";
2468 } else {
2469 $order_by = "ttrss_feeds.title, $override_order";
2470 }
2471 }
2472
2473 if (!$allow_archived) {
2474 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2475 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2476
2477 } else {
2478 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2479 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2480 }
2481
2482 $query = "SELECT DISTINCT
2483 date_entered,
2484 guid,
2485 ttrss_entries.id,ttrss_entries.title,
2486 updated,
2487 label_cache,
2488 tag_cache,
2489 always_display_enclosures,
2490 site_url,
2491 note,
2492 num_comments,
2493 comments,
2494 int_id,
2495 hide_images,
2496 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2497 last_marked, last_published,
2498 $vfeed_query_part
2499 $content_query_part
2500 author,score
2501 FROM
2502 $from_qpart
2503 WHERE
2504 $feed_check_qpart
2505 ttrss_user_entries.ref_id = ttrss_entries.id AND
2506 ttrss_user_entries.owner_uid = '$owner_uid' AND
2507 $search_query_part
2508 $filter_query_part
2509 $view_query_part
2510 $since_id_part
2511 $query_strategy_part ORDER BY $order_by
2512 $limit_query_part $offset_query_part";
2513
2514 if ($_REQUEST["debug"]) print $query;
2515
2516 $result = db_query($link, $query);
2517
2518 } else {
2519 // browsing by tag
2520
2521 $select_qpart = "SELECT DISTINCT " .
2522 "date_entered," .
2523 "guid," .
2524 "note," .
2525 "ttrss_entries.id as id," .
2526 "title," .
2527 "updated," .
2528 "unread," .
2529 "feed_id," .
2530 "orig_feed_id," .
2531 "marked," .
2532 "num_comments, " .
2533 "comments, " .
2534 "tag_cache," .
2535 "label_cache," .
2536 "link," .
2537 "last_read," .
2538 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2539 "last_marked, last_published, " .
2540 $since_id_part .
2541 $vfeed_query_part .
2542 $content_query_part .
2543 "score ";
2544
2545 $feed_kind = "Tags";
2546 $all_tags = explode(",", $feed);
2547 if ($search_mode == 'any') {
2548 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2549 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2550 $where_qpart = " WHERE " .
2551 "ref_id = ttrss_entries.id AND " .
2552 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2553 "post_int_id = int_id AND $tag_sql AND " .
2554 $view_query_part .
2555 $search_query_part .
2556 $query_strategy_part . " ORDER BY $order_by " .
2557 $limit_query_part;
2558
2559 } else {
2560 $i = 1;
2561 $sub_selects = array();
2562 $sub_ands = array();
2563 foreach ($all_tags as $term) {
2564 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");
2565 $i++;
2566 }
2567 if ($i > 2) {
2568 $x = 1;
2569 $y = 2;
2570 do {
2571 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2572 $x++;
2573 $y++;
2574 } while ($y < $i);
2575 }
2576 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2577 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2578 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2579 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2580 }
2581 // error_log("TAG SQL: " . $tag_sql);
2582 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2583
2584 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2585 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2586 }
2587
2588 return array($result, $feed_title, $feed_site_url, $last_error);
2589
2590 }
2591
2592 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2593 if (!$owner) $owner = $_SESSION["uid"];
2594
2595 $res = trim($str); if (!$res) return '';
2596
2597 if (strpos($res, "href=") === false)
2598 $res = rewrite_urls($res);
2599
2600 $charset_hack = '<head>
2601 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2602 </head>';
2603
2604 $res = trim($res); if (!$res) return '';
2605
2606 libxml_use_internal_errors(true);
2607
2608 $doc = new DOMDocument();
2609 $doc->loadHTML($charset_hack . $res);
2610 $xpath = new DOMXPath($doc);
2611
2612 $entries = $xpath->query('(//a[@href]|//img[@src])');
2613
2614 foreach ($entries as $entry) {
2615
2616 if ($site_url) {
2617
2618 if ($entry->hasAttribute('href'))
2619 $entry->setAttribute('href',
2620 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2621
2622 if ($entry->hasAttribute('src')) {
2623 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2624
2625 $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
2626
2627 if (file_exists($cached_filename)) {
2628 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
2629 }
2630
2631 $entry->setAttribute('src', $src);
2632 }
2633
2634 if ($entry->nodeName == 'img') {
2635 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2636 $force_remove_images || $_SESSION["bw_limit"]) {
2637
2638 $p = $doc->createElement('p');
2639
2640 $a = $doc->createElement('a');
2641 $a->setAttribute('href', $entry->getAttribute('src'));
2642
2643 $a->appendChild(new DOMText($entry->getAttribute('src')));
2644 $a->setAttribute('target', '_blank');
2645
2646 $p->appendChild($a);
2647
2648 $entry->parentNode->replaceChild($p, $entry);
2649 }
2650 }
2651 }
2652
2653 if (strtolower($entry->nodeName) == "a") {
2654 $entry->setAttribute("target", "_blank");
2655 }
2656 }
2657
2658 $entries = $xpath->query('//iframe');
2659 foreach ($entries as $entry) {
2660 $entry->setAttribute('sandbox', 'allow-scripts');
2661
2662 }
2663
2664 $allowed_elements = array('a', 'address', 'audio', 'article',
2665 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2666 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2667 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2668 'header', 'html', 'i', 'img', 'ins', 'kbd',
2669 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2670 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2671 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2672 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2673
2674 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2675
2676 $disallowed_attributes = array('id', 'style', 'class');
2677
2678 global $pluginhost;
2679
2680 if (isset($pluginhost)) {
2681 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE) as $plugin) {
2682 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2683 if (is_array($retval)) {
2684 $doc = $retval[0];
2685 $allowed_elements = $retval[1];
2686 $disallowed_attributes = $retval[2];
2687 } else {
2688 $doc = $retval;
2689 }
2690 }
2691 }
2692
2693 $doc->removeChild($doc->firstChild); //remove doctype
2694 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2695 $res = $doc->saveHTML();
2696 return $res;
2697 }
2698
2699 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2700 $entries = $doc->getElementsByTagName("*");
2701
2702 foreach ($entries as $entry) {
2703 if (!in_array($entry->nodeName, $allowed_elements)) {
2704 $entry->parentNode->removeChild($entry);
2705 }
2706
2707 if ($entry->hasAttributes()) {
2708 $attrs_to_remove = array();
2709
2710 foreach ($entry->attributes as $attr) {
2711
2712 if (strpos($attr->nodeName, 'on') === 0) {
2713 array_push($attrs_to_remove, $attr);
2714 }
2715
2716 if (in_array($attr->nodeName, $disallowed_attributes)) {
2717 array_push($attrs_to_remove, $attr);
2718 }
2719 }
2720
2721 foreach ($attrs_to_remove as $attr) {
2722 $entry->removeAttributeNode($attr);
2723 }
2724 }
2725 }
2726
2727 return $doc;
2728 }
2729
2730 function check_for_update($link) {
2731 if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
2732 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
2733 "&iid=" . sha1(SELF_URL_PATH);
2734
2735 $version_data = @fetch_file_contents($version_url);
2736
2737 if ($version_data) {
2738 $version_data = json_decode($version_data, true);
2739 if ($version_data && $version_data['version']) {
2740
2741 if (version_compare(VERSION, $version_data['version']) == -1) {
2742 return $version_data;
2743 }
2744 }
2745 }
2746 }
2747 return false;
2748 }
2749
2750 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2751
2752 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2753 if (count($ids) == 0) return;
2754
2755 $tmp_ids = array();
2756
2757 foreach ($ids as $id) {
2758 array_push($tmp_ids, "ref_id = '$id'");
2759 }
2760
2761 $ids_qpart = join(" OR ", $tmp_ids);
2762
2763 if ($cmode == 0) {
2764 db_query($link, "UPDATE ttrss_user_entries SET
2765 unread = false,last_read = NOW()
2766 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2767 } else if ($cmode == 1) {
2768 db_query($link, "UPDATE ttrss_user_entries SET
2769 unread = true
2770 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2771 } else {
2772 db_query($link, "UPDATE ttrss_user_entries SET
2773 unread = NOT unread,last_read = NOW()
2774 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2775 }
2776
2777 /* update ccache */
2778
2779 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2780 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2781
2782 while ($line = db_fetch_assoc($result)) {
2783 ccache_update($link, $line["feed_id"], $owner_uid);
2784 }
2785 }
2786
2787 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2788
2789 $a_id = db_escape_string($link, $id);
2790
2791 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2792
2793 $query = "SELECT DISTINCT tag_name,
2794 owner_uid as owner FROM
2795 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2796 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2797
2798 $obj_id = md5("TAGS:$owner_uid:$id");
2799 $tags = array();
2800
2801 /* check cache first */
2802
2803 if ($tag_cache === false) {
2804 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2805 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2806
2807 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2808 }
2809
2810 if ($tag_cache) {
2811 $tags = explode(",", $tag_cache);
2812 } else {
2813
2814 /* do it the hard way */
2815
2816 $tmp_result = db_query($link, $query);
2817
2818 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2819 array_push($tags, $tmp_line["tag_name"]);
2820 }
2821
2822 /* update the cache */
2823
2824 $tags_str = db_escape_string($link, join(",", $tags));
2825
2826 db_query($link, "UPDATE ttrss_user_entries
2827 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2828 AND owner_uid = $owner_uid");
2829 }
2830
2831 return $tags;
2832 }
2833
2834 function trim_array($array) {
2835 $tmp = $array;
2836 array_walk($tmp, 'trim');
2837 return $tmp;
2838 }
2839
2840 function tag_is_valid($tag) {
2841 if ($tag == '') return false;
2842 if (preg_match("/^[0-9]*$/", $tag)) return false;
2843 if (mb_strlen($tag) > 250) return false;
2844
2845 if (function_exists('iconv')) {
2846 $tag = iconv("utf-8", "utf-8", $tag);
2847 }
2848
2849 if (!$tag) return false;
2850
2851 return true;
2852 }
2853
2854 function render_login_form($link) {
2855 require_once "login_form.php";
2856 exit;
2857 }
2858
2859 // from http://developer.apple.com/internet/safari/faq.html
2860 function no_cache_incantation() {
2861 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2862 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2863 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2864 header("Cache-Control: post-check=0, pre-check=0", false);
2865 header("Pragma: no-cache"); // HTTP/1.0
2866 }
2867
2868 function format_warning($msg, $id = "") {
2869 global $link;
2870 return "<div class=\"warning\" id=\"$id\">
2871 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2872 }
2873
2874 function format_notice($msg, $id = "") {
2875 global $link;
2876 return "<div class=\"notice\" id=\"$id\">
2877 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2878 }
2879
2880 function format_error($msg, $id = "") {
2881 global $link;
2882 return "<div class=\"error\" id=\"$id\">
2883 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2884 }
2885
2886 function print_notice($msg) {
2887 return print format_notice($msg);
2888 }
2889
2890 function print_warning($msg) {
2891 return print format_warning($msg);
2892 }
2893
2894 function print_error($msg) {
2895 return print format_error($msg);
2896 }
2897
2898
2899 function T_sprintf() {
2900 $args = func_get_args();
2901 return vsprintf(__(array_shift($args)), $args);
2902 }
2903
2904 function format_inline_player($link, $url, $ctype) {
2905
2906 $entry = "";
2907
2908 $url = htmlspecialchars($url);
2909
2910 if (strpos($ctype, "audio/") === 0) {
2911
2912 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2913 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2914 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2915
2916 $id = 'AUDIO-' . uniqid();
2917
2918 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2919 <source type=\"$ctype\" src=\"$url\"></source>
2920 </audio>";
2921
2922 $entry .= "<span onclick=\"player(this)\"
2923 title=\"".__("Click to play")."\" status=\"0\"
2924 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2925
2926 } else {
2927
2928 $entry .= "<object type=\"application/x-shockwave-flash\"
2929 data=\"lib/button/musicplayer.swf?song_url=$url\"
2930 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2931 <param name=\"movie\"
2932 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2933 </object>";
2934 }
2935
2936 if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
2937 href=\"$url\">" . basename($url) . "</a>";
2938
2939 return $entry;
2940
2941 }
2942
2943 return "";
2944
2945 /* $filename = substr($url, strrpos($url, "/")+1);
2946
2947 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2948 $filename . " (" . $ctype . ")" . "</a>"; */
2949
2950 }
2951
2952 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2953 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2954
2955 $rv = array();
2956
2957 $rv['id'] = $id;
2958
2959 /* we can figure out feed_id from article id anyway, why do we
2960 * pass feed_id here? let's ignore the argument :( */
2961
2962 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2963 WHERE ref_id = '$id'");
2964
2965 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2966
2967 $rv['feed_id'] = $feed_id;
2968
2969 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2970
2971 if ($mark_as_read) {
2972 $result = db_query($link, "UPDATE ttrss_user_entries
2973 SET unread = false,last_read = NOW()
2974 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2975
2976 ccache_update($link, $feed_id, $owner_uid);
2977 }
2978
2979 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2980 ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
2981 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2982 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2983 num_comments,
2984 tag_cache,
2985 author,
2986 orig_feed_id,
2987 note,
2988 cached_content
2989 FROM ttrss_entries,ttrss_user_entries
2990 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2991
2992 if ($result) {
2993
2994 $line = db_fetch_assoc($result);
2995
2996 $tag_cache = $line["tag_cache"];
2997
2998 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2999 unset($line["tag_cache"]);
3000
3001 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3002
3003 global $pluginhost;
3004
3005 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE) as $p) {
3006 $line = $p->hook_render_article($line);
3007 }
3008
3009 $num_comments = $line["num_comments"];
3010 $entry_comments = "";
3011
3012 if ($num_comments > 0) {
3013 if ($line["comments"]) {
3014 $comments_url = htmlspecialchars($line["comments"]);
3015 } else {
3016 $comments_url = htmlspecialchars($line["link"]);
3017 }
3018 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3019 } else {
3020 if ($line["comments"] && $line["link"] != $line["comments"]) {
3021 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3022 }
3023 }
3024
3025 if ($zoom_mode) {
3026 header("Content-Type: text/html");
3027 $rv['content'] .= "<html><head>
3028 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3029 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3030 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3031 </head><body id=\"ttrssZoom\">";
3032 }
3033
3034 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3035
3036 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3037
3038 $entry_author = $line["author"];
3039
3040 if ($entry_author) {
3041 $entry_author = __(" - ") . $entry_author;
3042 }
3043
3044 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3045 $owner_uid, true);
3046
3047 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3048
3049 if ($line["link"]) {
3050 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3051 title=\"".htmlspecialchars($line['title'])."\"
3052 href=\"" .
3053 htmlspecialchars($line["link"]) . "\">" .
3054 $line["title"] . "</a>" .
3055 "<span class='author'>$entry_author</span></div>";
3056 } else {
3057 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3058 }
3059
3060 $tags_str = format_tags_string($line["tags"], $id);
3061 $tags_str_full = join(", ", $line["tags"]);
3062
3063 if (!$tags_str_full) $tags_str_full = __("no tags");
3064
3065 if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
3066
3067 $rv['content'] .= "<div class='postTags' style='float : right'>
3068 <img src='images/tag.png'
3069 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
3070
3071 if (!$zoom_mode) {
3072 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3073 <a title=\"".__('Edit tags for this article')."\"
3074 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3075
3076 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3077 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3078 position=\"below\">$tags_str_full</div>";
3079
3080 global $pluginhost;
3081
3082 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) {
3083 $rv['content'] .= $p->hook_article_button($line);
3084 }
3085
3086
3087 } else {
3088 $tags_str = strip_tags($tags_str);
3089 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3090 }
3091 $rv['content'] .= "</div>";
3092 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3093
3094 if ($line["orig_feed_id"]) {
3095
3096 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3097 WHERE id = ".$line["orig_feed_id"]);
3098
3099 if (db_num_rows($tmp_result) != 0) {
3100
3101 $rv['content'] .= "<div clear='both'>";
3102 $rv['content'] .= __("Originally from:");
3103
3104 $rv['content'] .= "&nbsp;";
3105
3106 $tmp_line = db_fetch_assoc($tmp_result);
3107
3108 $rv['content'] .= "<a target='_blank'
3109 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3110 $tmp_line['title'] . "</a>";
3111
3112 $rv['content'] .= "&nbsp;";
3113
3114 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3115 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3116
3117 $rv['content'] .= "</div>";
3118 }
3119 }
3120
3121 $rv['content'] .= "</div>";
3122
3123 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3124 if ($line['note']) {
3125 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3126 }
3127 $rv['content'] .= "</div>";
3128
3129 $rv['content'] .= "<div class=\"postContent\">";
3130
3131 $rv['content'] .= $line["content"];
3132
3133 $rv['content'] .= format_article_enclosures($link, $id,
3134 $always_display_enclosures, $line["content"], $line["hide_images"]);
3135
3136 $rv['content'] .= "</div>";
3137
3138 $rv['content'] .= "</div>";
3139
3140 }
3141
3142 if ($zoom_mode) {
3143 $rv['content'] .= "
3144 <div class='footer'>
3145 <button onclick=\"return window.close()\">".
3146 __("Close this window")."</button></div>";
3147 $rv['content'] .= "</body></html>";
3148 }
3149
3150 return $rv;
3151
3152 }
3153
3154 function print_checkpoint($n, $s) {
3155 $ts = microtime(true);
3156 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3157 return $ts;
3158 }
3159
3160 function sanitize_tag($tag) {
3161 $tag = trim($tag);
3162
3163 $tag = mb_strtolower($tag, 'utf-8');
3164
3165 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3166
3167 // $tag = str_replace('"', "", $tag);
3168 // $tag = str_replace("+", " ", $tag);
3169 $tag = str_replace("technorati tag: ", "", $tag);
3170
3171 return $tag;
3172 }
3173
3174 function get_self_url_prefix() {
3175 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
3176 return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
3177 } else {
3178 return SELF_URL_PATH;
3179 }
3180 }
3181
3182 /**
3183 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3184 *
3185 * @return string The Mozilla Firefox feed adding URL.
3186 */
3187 function add_feed_url() {
3188 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3189
3190 $url_path = get_self_url_prefix() .
3191 "/public.php?op=subscribe&feed_url=%s";
3192 return $url_path;
3193 } // function add_feed_url
3194
3195 function encrypt_password($pass, $salt = '', $mode2 = false) {
3196 if ($salt && $mode2) {
3197 return "MODE2:" . hash('sha256', $salt . $pass);
3198 } else if ($salt) {
3199 return "SHA1X:" . sha1("$salt:$pass");
3200 } else {
3201 return "SHA1:" . sha1($pass);
3202 }
3203 } // function encrypt_password
3204
3205 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3206 $filters = array();
3207
3208 $cat_id = (int)getFeedCategory($link, $feed_id);
3209
3210 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3211 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3212
3213 $check_cats = join(",", array_merge(
3214 getParentCategories($link, $cat_id, $owner_uid),
3215 array($cat_id)));
3216
3217 while ($line = db_fetch_assoc($result)) {
3218 $filter_id = $line["id"];
3219
3220 $result2 = db_query($link, "SELECT
3221 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3222 FROM ttrss_filters2_rules AS r,
3223 ttrss_filter_types AS t
3224 WHERE
3225 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3226 (feed_id IS NULL OR feed_id = '$feed_id') AND
3227 filter_type = t.id AND filter_id = '$filter_id'");
3228
3229 $rules = array();
3230 $actions = array();
3231
3232 while ($rule_line = db_fetch_assoc($result2)) {
3233 # print_r($rule_line);
3234
3235 $rule = array();
3236 $rule["reg_exp"] = $rule_line["reg_exp"];
3237 $rule["type"] = $rule_line["type_name"];
3238 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3239
3240 array_push($rules, $rule);
3241 }
3242
3243 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3244 FROM ttrss_filters2_actions AS a,
3245 ttrss_filter_actions AS t
3246 WHERE
3247 action_id = t.id AND filter_id = '$filter_id'");
3248
3249 while ($action_line = db_fetch_assoc($result2)) {
3250 # print_r($action_line);
3251
3252 $action = array();
3253 $action["type"] = $action_line["type_name"];
3254 $action["param"] = $action_line["action_param"];
3255
3256 array_push($actions, $action);
3257 }
3258
3259
3260 $filter = array();
3261 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3262 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3263 $filter["rules"] = $rules;
3264 $filter["actions"] = $actions;
3265
3266 if (count($rules) > 0 && count($actions) > 0) {
3267 array_push($filters, $filter);
3268 }
3269 }
3270
3271 return $filters;
3272 }
3273
3274 function get_score_pic($score) {
3275 if ($score > 100) {
3276 return "score_high.png";
3277 } else if ($score > 0) {
3278 return "score_half_high.png";
3279 } else if ($score < -100) {
3280 return "score_low.png";
3281 } else if ($score < 0) {
3282 return "score_half_low.png";
3283 } else {
3284 return "score_neutral.png";
3285 }
3286 }
3287
3288 function feed_has_icon($id) {
3289 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
3290 }
3291
3292 function init_connection($link) {
3293 if ($link) {
3294
3295 if (DB_TYPE == "pgsql") {
3296 pg_query($link, "set client_encoding = 'UTF-8'");
3297 pg_set_client_encoding("UNICODE");
3298 pg_query($link, "set datestyle = 'ISO, european'");
3299 pg_query($link, "set TIME ZONE 0");
3300 } else {
3301 db_query($link, "SET time_zone = '+0:0'");
3302
3303 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
3304 db_query($link, "SET NAMES " . MYSQL_CHARSET);
3305 }
3306 }
3307
3308 global $pluginhost;
3309
3310 $pluginhost = new PluginHost($link);
3311 $pluginhost->load(PLUGINS, $pluginhost::KIND_ALL);
3312
3313 return true;
3314 } else {
3315 print "Unable to connect to database:" . db_last_error();
3316 return false;
3317 }
3318 }
3319
3320 function format_tags_string($tags, $id) {
3321
3322 $tags_str = "";
3323 $tags_nolinks_str = "";
3324
3325 $num_tags = 0;
3326
3327 $tag_limit = 6;
3328
3329 $formatted_tags = array();
3330
3331 foreach ($tags as $tag) {
3332 $num_tags++;
3333 $tag_escaped = str_replace("'", "\\'", $tag);
3334
3335 if (mb_strlen($tag) > 30) {
3336 $tag = truncate_string($tag, 30);
3337 }
3338
3339 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3340
3341 array_push($formatted_tags, $tag_str);
3342
3343 $tmp_tags_str = implode(", ", $formatted_tags);
3344
3345 if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
3346 break;
3347 }
3348 }
3349
3350 $tags_str = implode(", ", $formatted_tags);
3351
3352 if ($num_tags < count($tags)) {
3353 $tags_str .= ", &hellip;";
3354 }
3355
3356 if ($num_tags == 0) {
3357 $tags_str = __("no tags");
3358 }
3359
3360 return $tags_str;
3361
3362 }
3363
3364 function format_article_labels($labels, $id) {
3365
3366 $labels_str = "";
3367
3368 foreach ($labels as $l) {
3369 $labels_str .= sprintf("<span class='hlLabelRef'
3370 style='color : %s; background-color : %s'>%s</span>",
3371 $l[2], $l[3], $l[1]);
3372 }
3373
3374 return $labels_str;
3375
3376 }
3377
3378 function format_article_note($id, $note, $allow_edit = true) {
3379
3380 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3381 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3382 ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
3383
3384 return $str;
3385 }
3386
3387
3388 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3389 if ($parent_cat_id) {
3390 $parent_qpart = "parent_cat = '$parent_cat_id'";
3391 $parent_insert = "'$parent_cat_id'";
3392 } else {
3393 $parent_qpart = "parent_cat IS NULL";
3394 $parent_insert = "NULL";
3395 }
3396
3397 $result = db_query($link,
3398 "SELECT id FROM ttrss_feed_categories
3399 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3400
3401 if (db_num_rows($result) == 0) {
3402 return false;
3403 } else {
3404 return db_fetch_result($result, 0, "id");
3405 }
3406 }
3407
3408 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3409
3410 if (!$feed_cat) return false;
3411
3412 db_query($link, "BEGIN");
3413
3414 if ($parent_cat_id) {
3415 $parent_qpart = "parent_cat = '$parent_cat_id'";
3416 $parent_insert = "'$parent_cat_id'";
3417 } else {
3418 $parent_qpart = "parent_cat IS NULL";
3419 $parent_insert = "NULL";
3420 }
3421
3422 $result = db_query($link,
3423 "SELECT id FROM ttrss_feed_categories
3424 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3425
3426 if (db_num_rows($result) == 0) {
3427
3428 $result = db_query($link,
3429 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3430 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3431
3432 db_query($link, "COMMIT");
3433
3434 return true;
3435 }
3436
3437 return false;
3438 }
3439
3440 function getArticleFeed($link, $id) {
3441 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3442 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3443
3444 if (db_num_rows($result) != 0) {
3445 return db_fetch_result($result, 0, "feed_id");
3446 } else {
3447 return 0;
3448 }
3449 }
3450
3451 /**
3452 * Fixes incomplete URLs by prepending "http://".
3453 * Also replaces feed:// with http://, and
3454 * prepends a trailing slash if the url is a domain name only.
3455 *
3456 * @param string $url Possibly incomplete URL
3457 *
3458 * @return string Fixed URL.
3459 */
3460 function fix_url($url) {
3461 if (strpos($url, '://') === false) {
3462 $url = 'http://' . $url;
3463 } else if (substr($url, 0, 5) == 'feed:') {
3464 $url = 'http:' . substr($url, 5);
3465 }
3466
3467 //prepend slash if the URL has no slash in it
3468 // "http://www.example" -> "http://www.example/"
3469 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
3470 $url .= '/';
3471 }
3472
3473 if ($url != "http:///")
3474 return $url;
3475 else
3476 return '';
3477 }
3478
3479 function validate_feed_url($url) {
3480 $parts = parse_url($url);
3481
3482 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
3483
3484 }
3485
3486 function get_article_enclosures($link, $id) {
3487
3488 $query = "SELECT * FROM ttrss_enclosures
3489 WHERE post_id = '$id' AND content_url != ''";
3490
3491 $rv = array();
3492
3493 $result = db_query($link, $query);
3494
3495 if (db_num_rows($result) > 0) {
3496 while ($line = db_fetch_assoc($result)) {
3497 array_push($rv, $line);
3498 }
3499 }
3500
3501 return $rv;
3502 }
3503
3504 function save_email_address($link, $email) {
3505 // FIXME: implement persistent storage of emails
3506
3507 if (!$_SESSION['stored_emails'])
3508 $_SESSION['stored_emails'] = array();
3509
3510 if (!in_array($email, $_SESSION['stored_emails']))
3511 array_push($_SESSION['stored_emails'], $email);
3512 }
3513
3514
3515 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3516
3517 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3518
3519 $sql_is_cat = bool_to_sql_bool($is_cat);
3520
3521 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3522 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3523 AND owner_uid = " . $owner_uid);
3524
3525 if (db_num_rows($result) == 1) {
3526 return db_fetch_result($result, 0, "access_key");
3527 } else {
3528 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3529
3530 $result = db_query($link, "INSERT INTO ttrss_access_keys
3531 (access_key, feed_id, is_cat, owner_uid)
3532 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3533
3534 return $key;
3535 }
3536 return false;
3537 }
3538
3539 function get_feeds_from_html($url, $content)
3540 {
3541 $url = fix_url($url);
3542 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
3543
3544 libxml_use_internal_errors(true);
3545
3546 $doc = new DOMDocument();
3547 $doc->loadHTML($content);
3548 $xpath = new DOMXPath($doc);
3549 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3550 $feedUrls = array();
3551 foreach ($entries as $entry) {
3552 if ($entry->hasAttribute('href')) {
3553 $title = $entry->getAttribute('title');
3554 if ($title == '') {
3555 $title = $entry->getAttribute('type');
3556 }
3557 $feedUrl = rewrite_relative_url(
3558 $baseUrl, $entry->getAttribute('href')
3559 );
3560 $feedUrls[$feedUrl] = $title;
3561 }
3562 }
3563 return $feedUrls;
3564 }
3565
3566 function is_html($content) {
3567 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3568 }
3569
3570 function url_is_html($url, $login = false, $pass = false) {
3571 return is_html(fetch_file_contents($url, false, $login, $pass));
3572 }
3573
3574 function print_label_select($link, $name, $value, $attributes = "") {
3575
3576 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3577 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3578
3579 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3580 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3581
3582 while ($line = db_fetch_assoc($result)) {
3583
3584 $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
3585
3586 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3587 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3588
3589 }
3590
3591 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3592
3593 print "</select>";
3594
3595
3596 }
3597
3598 function format_article_enclosures($link, $id, $always_display_enclosures,
3599 $article_content, $hide_images = false) {
3600
3601 $result = get_article_enclosures($link, $id);
3602 $rv = '';
3603
3604 if (count($result) > 0) {
3605
3606 $entries_html = array();
3607 $entries = array();
3608 $entries_inline = array();
3609
3610 foreach ($result as $line) {
3611
3612 $url = $line["content_url"];
3613 $ctype = $line["content_type"];
3614
3615 if (!$ctype) $ctype = __("unknown type");
3616
3617 $filename = substr($url, strrpos($url, "/")+1);
3618
3619 $player = format_inline_player($link, $url, $ctype);
3620
3621 if ($player) array_push($entries_inline, $player);
3622
3623 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3624 # $filename . " (" . $ctype . ")" . "</a>";
3625
3626 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3627 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3628
3629 array_push($entries_html, $entry);
3630
3631 $entry = array();
3632
3633 $entry["type"] = $ctype;
3634 $entry["filename"] = $filename;
3635 $entry["url"] = $url;
3636
3637 array_push($entries, $entry);
3638 }
3639
3640 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3641 if ($always_display_enclosures ||
3642 !preg_match("/<img/i", $article_content)) {
3643
3644 foreach ($entries as $entry) {
3645
3646 if (preg_match("/image/", $entry["type"]) ||
3647 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3648
3649 if (!$hide_images) {
3650 $rv .= "<p><img
3651 alt=\"".htmlspecialchars($entry["filename"])."\"
3652 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3653 } else {
3654 $rv .= "<p><a target=\"_blank\"
3655 href=\"".htmlspecialchars($entry["url"])."\"
3656 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3657
3658 }
3659 }
3660 }
3661 }
3662 }
3663
3664 if (count($entries_inline) > 0) {
3665 $rv .= "<hr clear='both'/>";
3666 foreach ($entries_inline as $entry) { $rv .= $entry; };
3667 $rv .= "<hr clear='both'/>";
3668 }
3669
3670 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3671 "<option value=''>" . __('Attachments')."</option>";
3672
3673 foreach ($entries as $entry) {
3674 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3675
3676 };
3677
3678 $rv .= "</select>";
3679 }
3680
3681 return $rv;
3682 }
3683
3684 function getLastArticleId($link) {
3685 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3686 WHERE owner_uid = " . $_SESSION["uid"]);
3687
3688 if (db_num_rows($result) == 1) {
3689 return db_fetch_result($result, 0, "id");
3690 } else {
3691 return -1;
3692 }
3693 }
3694
3695 function build_url($parts) {
3696 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3697 }
3698
3699 /**
3700 * Converts a (possibly) relative URL to a absolute one.
3701 *
3702 * @param string $url Base URL (i.e. from where the document is)
3703 * @param string $rel_url Possibly relative URL in the document
3704 *
3705 * @return string Absolute URL
3706 */
3707 function rewrite_relative_url($url, $rel_url) {
3708 if (strpos($rel_url, "magnet:") === 0) {
3709 return $rel_url;
3710 } else if (strpos($rel_url, "://") !== false) {
3711 return $rel_url;
3712 } else if (strpos($rel_url, "//") === 0) {
3713 # protocol-relative URL (rare but they exist)
3714 return $rel_url;
3715 } else if (strpos($rel_url, "/") === 0)
3716 {
3717 $parts = parse_url($url);
3718 $parts['path'] = $rel_url;
3719
3720 return build_url($parts);
3721
3722 } else {
3723 $parts = parse_url($url);
3724 if (!isset($parts['path'])) {
3725 $parts['path'] = '/';
3726 }
3727 $dir = $parts['path'];
3728 if (substr($dir, -1) !== '/') {
3729 $dir = dirname($parts['path']);
3730 $dir !== '/' && $dir .= '/';
3731 }
3732 $parts['path'] = $dir . $rel_url;
3733
3734 return build_url($parts);
3735 }
3736 }
3737
3738 function sphinx_search($query, $offset = 0, $limit = 30) {
3739 require_once 'lib/sphinxapi.php';
3740
3741 $sphinxClient = new SphinxClient();
3742
3743 $sphinxClient->SetServer('localhost', 9312);
3744 $sphinxClient->SetConnectTimeout(1);
3745
3746 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3747 'feed_title' => 20));
3748
3749 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
3750 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
3751 $sphinxClient->SetLimits($offset, $limit, 1000);
3752 $sphinxClient->SetArrayResult(false);
3753 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3754
3755 $result = $sphinxClient->Query($query, SPHINX_INDEX);
3756
3757 $ids = array();
3758
3759 if (is_array($result['matches'])) {
3760 foreach (array_keys($result['matches']) as $int_id) {
3761 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3762 array_push($ids, $ref_id);
3763 }
3764 }
3765
3766 return $ids;
3767 }
3768
3769 function cleanup_tags($link, $days = 14, $limit = 1000) {
3770
3771 if (DB_TYPE == "pgsql") {
3772 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3773 } else if (DB_TYPE == "mysql") {
3774 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3775 }
3776
3777 $tags_deleted = 0;
3778
3779 while ($limit > 0) {
3780 $limit_part = 500;
3781
3782 $query = "SELECT ttrss_tags.id AS id
3783 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3784 WHERE post_int_id = int_id AND $interval_query AND
3785 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3786
3787 $result = db_query($link, $query);
3788
3789 $ids = array();
3790
3791 while ($line = db_fetch_assoc($result)) {
3792 array_push($ids, $line['id']);
3793 }
3794
3795 if (count($ids) > 0) {
3796 $ids = join(",", $ids);
3797
3798 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3799 $tags_deleted += db_affected_rows($link, $tmp_result);
3800 } else {
3801 break;
3802 }
3803
3804 $limit -= $limit_part;
3805 }
3806
3807 return $tags_deleted;
3808 }
3809
3810 function print_user_stylesheet($link) {
3811 $value = get_pref($link, 'USER_STYLESHEET');
3812
3813 if ($value) {
3814 print "<style type=\"text/css\">";
3815 print str_replace("<br/>", "\n", $value);
3816 print "</style>";
3817 }
3818
3819 }
3820
3821 function rewrite_urls($html) {
3822 libxml_use_internal_errors(true);
3823
3824 $charset_hack = '<head>
3825 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3826 </head>';
3827
3828 $doc = new DOMDocument();
3829 $doc->loadHTML($charset_hack . $html);
3830 $xpath = new DOMXPath($doc);
3831
3832 $entries = $xpath->query('//*/text()');
3833
3834 foreach ($entries as $entry) {
3835 if (strstr($entry->wholeText, "://") !== false) {
3836 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3837 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText);
3838
3839 if ($text != $entry->wholeText) {
3840 $cdoc = new DOMDocument();
3841 $cdoc->loadHTML($charset_hack . $text);
3842
3843
3844 foreach ($cdoc->childNodes as $cnode) {
3845 $cnode = $doc->importNode($cnode, true);
3846
3847 if ($cnode) {
3848 $entry->parentNode->insertBefore($cnode);
3849 }
3850 }
3851
3852 $entry->parentNode->removeChild($entry);
3853
3854 }
3855 }
3856 }
3857
3858 $node = $doc->getElementsByTagName('body')->item(0);
3859
3860 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3861 if ($node)
3862 return $doc->saveXML($node);
3863 else
3864 return $html;
3865 }
3866
3867 function filter_to_sql($link, $filter, $owner_uid) {
3868 $query = array();
3869
3870 if (DB_TYPE == "pgsql")
3871 $reg_qpart = "~";
3872 else
3873 $reg_qpart = "REGEXP";
3874
3875 foreach ($filter["rules"] AS $rule) {
3876 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3877 $rule['reg_exp']) !== FALSE;
3878
3879 if ($regexp_valid) {
3880
3881 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3882
3883 switch ($rule["type"]) {
3884 case "title":
3885 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3886 $rule['reg_exp'] . "')";
3887 break;
3888 case "content":
3889 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3890 $rule['reg_exp'] . "')";
3891 break;
3892 case "both":
3893 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3894 $rule['reg_exp'] . "') OR LOWER(" .
3895 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3896 break;
3897 case "tag":
3898 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3899 $rule['reg_exp'] . "')";
3900 break;
3901 case "link":
3902 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3903 $rule['reg_exp'] . "')";
3904 break;
3905 case "author":
3906 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3907 $rule['reg_exp'] . "')";
3908 break;
3909 }
3910
3911 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3912
3913 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3914 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3915 }
3916
3917 if (isset($rule["cat_id"])) {
3918
3919 if ($rule["cat_id"] > 0) {
3920 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3921 array_push($children, $rule["cat_id"]);
3922
3923 $children = join(",", $children);
3924
3925 $cat_qpart = "cat_id IN ($children)";
3926 } else {
3927 $cat_qpart = "cat_id IS NULL";
3928 }
3929
3930 $qpart .= " AND $cat_qpart";
3931 }
3932
3933 array_push($query, "($qpart)");
3934
3935 }
3936 }
3937
3938 if (count($query) > 0) {
3939 $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
3940 } else {
3941 $fullquery = "(false)";
3942 }
3943
3944 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3945
3946 return $fullquery;
3947 }
3948
3949 if (!function_exists('gzdecode')) {
3950 function gzdecode($string) { // no support for 2nd argument
3951 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3952 base64_encode($string));
3953 }
3954 }
3955
3956 function get_random_bytes($length) {
3957 if (function_exists('openssl_random_pseudo_bytes')) {
3958 return openssl_random_pseudo_bytes($length);
3959 } else {
3960 $output = "";
3961
3962 for ($i = 0; $i < $length; $i++)
3963 $output .= chr(mt_rand(0, 255));
3964
3965 return $output;
3966 }
3967 }
3968
3969 function read_stdin() {
3970 $fp = fopen("php://stdin", "r");
3971
3972 if ($fp) {
3973 $line = trim(fgets($fp));
3974 fclose($fp);
3975 return $line;
3976 }
3977
3978 return null;
3979 }
3980
3981 function tmpdirname($path, $prefix) {
3982 // Use PHP's tmpfile function to create a temporary
3983 // directory name. Delete the file and keep the name.
3984 $tempname = tempnam($path,$prefix);
3985 if (!$tempname)
3986 return false;
3987
3988 if (!unlink($tempname))
3989 return false;
3990
3991 return $tempname;
3992 }
3993
3994 function getFeedCategory($link, $feed) {
3995 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3996 WHERE id = '$feed'");
3997
3998 if (db_num_rows($result) > 0) {
3999 return db_fetch_result($result, 0, "cat_id");
4000 } else {
4001 return false;
4002 }
4003
4004 }
4005
4006 function implements_interface($class, $interface) {
4007 return in_array($interface, class_implements($class));
4008 }
4009
4010 function geturl($url){
4011
4012 (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');
4013
4014 $curl = curl_init();
4015 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4016 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4017 $header[] = "Cache-Control: max-age=0";
4018 $header[] = "Connection: keep-alive";
4019 $header[] = "Keep-Alive: 300";
4020 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4021 $header[] = "Accept-Language: en-us,en;q=0.5";
4022 $header[] = "Pragma: ";
4023
4024 curl_setopt($curl, CURLOPT_URL, $url);
4025 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4026 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
4027 curl_setopt($curl, CURLOPT_HEADER, true);
4028 curl_setopt($curl, CURLOPT_REFERER, $url);
4029 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
4030 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
4031 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
4032 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4033 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
4034
4035 $html = curl_exec($curl);
4036
4037 $status = curl_getinfo($curl);
4038 curl_close($curl);
4039
4040 if($status['http_code']!=200){
4041 if($status['http_code'] == 301 || $status['http_code'] == 302) {
4042 list($header) = explode("\r\n\r\n", $html, 2);
4043 $matches = array();
4044 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4045 $url = trim(str_replace($matches[1],"",$matches[0]));
4046 $url_parsed = parse_url($url);
4047 return (isset($url_parsed))? geturl($url, $referer):'';
4048 }
4049 $oline='';
4050 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4051 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4052 # $handle = @fopen('./curl.error.log', 'a');
4053 # fwrite($handle, $line);
4054 return FALSE;
4055 }
4056 return $url;
4057 }
4058
4059 function get_minified_js($files) {
4060 require_once 'lib/jshrink/Minifier.php';
4061
4062 $rv = '';
4063
4064 foreach ($files as $js) {
4065 if (!isset($_GET['debug'])) {
4066 $cached_file = CACHE_DIR . "/js/$js.js";
4067
4068 if (file_exists($cached_file) &&
4069 is_readable($cached_file) &&
4070 filemtime($cached_file) >= filemtime("js/$js.js")) {
4071
4072 $rv .= file_get_contents($cached_file);
4073
4074 } else {
4075 $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
4076 file_put_contents($cached_file, $minified);
4077 $rv .= $minified;
4078 }
4079 } else {
4080 $rv .= file_get_contents("js/$js.js");
4081 }
4082 }
4083
4084 return $rv;
4085 }
4086
4087 function stylesheet_tag($filename) {
4088 $timestamp = filemtime($filename);
4089
4090 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4091 }
4092
4093 function javascript_tag($filename) {
4094 $query = "";
4095
4096 if (!(strpos($filename, "?") === FALSE)) {
4097 $query = substr($filename, strpos($filename, "?")+1);
4098 $filename = substr($filename, 0, strpos($filename, "?"));
4099 }
4100
4101 $timestamp = filemtime($filename);
4102
4103 if ($query) $timestamp .= "&$query";
4104
4105 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4106 }
4107
4108 function calculate_dep_timestamp() {
4109 $files = array_merge(glob("js/*.js"), glob("*.css"));
4110
4111 $max_ts = -1;
4112
4113 foreach ($files as $file) {
4114 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4115 }
4116
4117 return $max_ts;
4118 }
4119
4120 function T_js_decl($s1, $s2) {
4121 if ($s1 && $s2) {
4122 $s1 = preg_replace("/\n/", "", $s1);
4123 $s2 = preg_replace("/\n/", "", $s2);
4124
4125 $s1 = preg_replace("/\"/", "\\\"", $s1);
4126 $s2 = preg_replace("/\"/", "\\\"", $s2);
4127
4128 return "T_messages[\"$s1\"] = \"$s2\";\n";
4129 }
4130 }
4131
4132 function init_js_translations() {
4133
4134 print 'var T_messages = new Object();
4135
4136 function __(msg) {
4137 if (T_messages[msg]) {
4138 return T_messages[msg];
4139 } else {
4140 return msg;
4141 }
4142 }
4143
4144 function ngettext(msg1, msg2, n) {
4145 return (parseInt(n) > 1) ? msg2 : msg1;
4146 }';
4147
4148 $l10n = _get_reader();
4149
4150 for ($i = 0; $i < $l10n->total; $i++) {
4151 $orig = $l10n->get_original_string($i);
4152 $translation = __($orig);
4153
4154 print T_js_decl($orig, $translation);
4155 }
4156 }
4157
4158 function label_to_feed_id($label) {
4159 return LABEL_BASE_INDEX - 1 - abs($label);
4160 }
4161
4162 function feed_to_label_id($feed) {
4163 return LABEL_BASE_INDEX - 1 + abs($feed);
4164 }
4165
4166 ?>