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