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