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