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