]> git.wh0rd.org - tt-rss.git/blob - include/functions.php
Merge branch 'master' of github.com:gothfox/Tiny-Tiny-RSS
[tt-rss.git] / include / functions.php
1 <?php
2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 115);
4
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
7
8 $fetch_last_error = false;
9 $fetch_last_error_code = false;
10 $pluginhost = false;
11
12 function __autoload($class) {
13 $class_file = str_replace("_", "/", strtolower(basename($class)));
14
15 $file = dirname(__FILE__)."/../classes/$class_file.php";
16
17 if (file_exists($file)) {
18 require $file;
19 }
20
21 }
22
23 mb_internal_encoding("UTF-8");
24 date_default_timezone_set('UTC');
25 if (defined('E_DEPRECATED')) {
26 error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
27 } else {
28 error_reporting(E_ALL & ~E_NOTICE);
29 }
30
31 require_once 'config.php';
32
33 /**
34 * Define a constant if not already defined
35 *
36 * @param string $name The constant name.
37 * @param mixed $value The constant value.
38 * @access public
39 * @return boolean True if defined successfully or not.
40 */
41 function define_default($name, $value) {
42 defined($name) or define($name, $value);
43 }
44
45 ///// Some defaults that you can override in config.php //////
46
47 define_default('FEED_FETCH_TIMEOUT', 45);
48 // How may seconds to wait for response when requesting feed from a site
49 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
50 // How may seconds to wait for response when requesting feed from a
51 // site when that feed wasn't cached before
52 define_default('FILE_FETCH_TIMEOUT', 45);
53 // Default timeout when fetching files from remote sites
54 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
55 // How many seconds to wait for initial response from website when
56 // fetching files from remote sites
57
58 if (DB_TYPE == "pgsql") {
59 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
60 } else {
61 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
62 }
63
64 /**
65 * Return available translations names.
66 *
67 * @access public
68 * @return array A array of available translations.
69 */
70 function get_translations() {
71 $tr = array(
72 "auto" => "Detect automatically",
73 "ca_CA" => "Català",
74 "cs_CZ" => "Česky",
75 "en_US" => "English",
76 "es_ES" => "Español",
77 "de_DE" => "Deutsch",
78 "fr_FR" => "Français",
79 "hu_HU" => "Magyar (Hungarian)",
80 "it_IT" => "Italiano",
81 "ja_JP" => "日本語 (Japanese)",
82 "lv_LV" => "Latviešu",
83 "nb_NO" => "Norwegian bokmål",
84 "nl_NL" => "Dutch",
85 "pl_PL" => "Polski",
86 "ru_RU" => "Русский",
87 "pt_BR" => "Portuguese/Brazil",
88 "zh_CN" => "Simplified Chinese",
89 "sv_SE" => "Svenska",
90 "fi_FI" => "Suomi");
91
92 return $tr;
93 }
94
95 require_once "lib/accept-to-gettext.php";
96 require_once "lib/gettext/gettext.inc";
97
98
99 function startup_gettext() {
100
101 # Get locale from Accept-Language header
102 $lang = al2gt(array_keys(get_translations()), "text/html");
103
104 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
105 $lang = _TRANSLATION_OVERRIDE_DEFAULT;
106 }
107
108 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
109 $lang = $_SESSION["language"];
110 }
111
112 if ($lang) {
113 if (defined('LC_MESSAGES')) {
114 _setlocale(LC_MESSAGES, $lang);
115 } else if (defined('LC_ALL')) {
116 _setlocale(LC_ALL, $lang);
117 }
118
119 _bindtextdomain("messages", "locale");
120
121 _textdomain("messages");
122 _bind_textdomain_codeset("messages", "UTF-8");
123 }
124 }
125
126 startup_gettext();
127
128 require_once 'db-prefs.php';
129 require_once 'version.php';
130 require_once 'ccache.php';
131 require_once 'labels.php';
132
133 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
134 ini_set('user_agent', SELF_USER_AGENT);
135
136 require_once 'lib/pubsubhubbub/publisher.php';
137
138 $tz_offset = -1;
139 $utc_tz = new DateTimeZone('UTC');
140 $schema_version = false;
141
142 /**
143 * Print a timestamped debug message.
144 *
145 * @param string $msg The debug message.
146 * @return void
147 */
148 function _debug($msg) {
149 $ts = strftime("%H:%M:%S", time());
150 if (function_exists('posix_getpid')) {
151 $ts = "$ts/" . posix_getpid();
152 }
153
154 if (!(defined('QUIET') && QUIET)) {
155 print "[$ts] $msg\n";
156 }
157
158 if (defined('LOGFILE')) {
159 $fp = fopen(LOGFILE, 'a+');
160
161 if ($fp) {
162 fputs($fp, "[$ts] $msg\n");
163 fclose($fp);
164 }
165 }
166
167 } // function _debug
168
169 /**
170 * Purge a feed old posts.
171 *
172 * @param mixed $link A database connection.
173 * @param mixed $feed_id The id of the purged feed.
174 * @param mixed $purge_interval Olderness of purged posts.
175 * @param boolean $debug Set to True to enable the debug. False by default.
176 * @access public
177 * @return void
178 */
179 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
180
181 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
182
183 $rows = -1;
184
185 $result = db_query($link,
186 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
187
188 $owner_uid = false;
189
190 if (db_num_rows($result) == 1) {
191 $owner_uid = db_fetch_result($result, 0, "owner_uid");
192 }
193
194 if ($purge_interval == -1 || !$purge_interval) {
195 if ($owner_uid) {
196 ccache_update($link, $feed_id, $owner_uid);
197 }
198 return;
199 }
200
201 if (!$owner_uid) return;
202
203 if (FORCE_ARTICLE_PURGE == 0) {
204 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
205 $owner_uid, false);
206 } else {
207 $purge_unread = true;
208 $purge_interval = FORCE_ARTICLE_PURGE;
209 }
210
211 if (!$purge_unread) $query_limit = " unread = false AND ";
212
213 if (DB_TYPE == "pgsql") {
214 $pg_version = get_pgsql_version($link);
215
216 if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) {
217
218 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
219 ttrss_entries.id = ref_id AND
220 marked = false AND
221 feed_id = '$feed_id' AND
222 $query_limit
223 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
224
225 } else {
226
227 $result = db_query($link, "DELETE FROM ttrss_user_entries
228 USING ttrss_entries
229 WHERE ttrss_entries.id = ref_id AND
230 marked = false AND
231 feed_id = '$feed_id' AND
232 $query_limit
233 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
234 }
235
236 $rows = pg_affected_rows($result);
237
238 } else {
239
240 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
241 marked = false AND feed_id = '$feed_id' AND
242 (SELECT date_updated FROM ttrss_entries WHERE
243 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
244
245 $result = db_query($link, "DELETE FROM ttrss_user_entries
246 USING ttrss_user_entries, ttrss_entries
247 WHERE ttrss_entries.id = ref_id AND
248 marked = false AND
249 feed_id = '$feed_id' AND
250 $query_limit
251 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
252
253 $rows = mysql_affected_rows($link);
254
255 }
256
257 ccache_update($link, $feed_id, $owner_uid);
258
259 if ($debug) {
260 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
261 }
262
263 return $rows;
264 } // function purge_feed
265
266 function feed_purge_interval($link, $feed_id) {
267
268 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
269 WHERE id = '$feed_id'");
270
271 if (db_num_rows($result) == 1) {
272 $purge_interval = db_fetch_result($result, 0, "purge_interval");
273 $owner_uid = db_fetch_result($result, 0, "owner_uid");
274
275 if ($purge_interval == 0) $purge_interval = get_pref($link,
276 'PURGE_OLD_DAYS', $owner_uid);
277
278 return $purge_interval;
279
280 } else {
281 return -1;
282 }
283 }
284
285 function purge_orphans($link, $do_output = false) {
286
287 // purge orphaned posts in main content table
288 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
289 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
290
291 if ($do_output) {
292 $rows = db_affected_rows($link, $result);
293 _debug("Purged $rows orphaned posts.");
294 }
295 }
296
297 function get_feed_update_interval($link, $feed_id) {
298 $result = db_query($link, "SELECT owner_uid, update_interval FROM
299 ttrss_feeds WHERE id = '$feed_id'");
300
301 if (db_num_rows($result) == 1) {
302 $update_interval = db_fetch_result($result, 0, "update_interval");
303 $owner_uid = db_fetch_result($result, 0, "owner_uid");
304
305 if ($update_interval != 0) {
306 return $update_interval;
307 } else {
308 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
309 }
310
311 } else {
312 return -1;
313 }
314 }
315
316 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
317
318 global $fetch_last_error;
319 global $fetch_last_error_code;
320
321 if (!defined('NO_CURL') && function_exists('curl_init') && !ini_get("open_basedir")) {
322
323 if (ini_get("safe_mode")) {
324 $ch = curl_init(geturl($url));
325 } else {
326 $ch = curl_init($url);
327 }
328
329 if ($timestamp) {
330 curl_setopt($ch, CURLOPT_HTTPHEADER,
331 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
332 }
333
334 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout ? $timeout : FILE_FETCH_CONNECT_TIMEOUT);
335 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout ? $timeout : FILE_FETCH_TIMEOUT);
336 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, !ini_get("safe_mode"));
337 curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
338 curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
339 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
340 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
341 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
342 curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
343 curl_setopt($ch, CURLOPT_ENCODING , "gzip");
344 curl_setopt($ch, CURLOPT_REFERER, $url);
345
346 if ($post_query) {
347 curl_setopt($ch, CURLOPT_POST, true);
348 curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
349 }
350
351 if ($login && $pass)
352 curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
353
354 $contents = @curl_exec($ch);
355
356 if (curl_errno($ch) === 23 || curl_errno($ch) === 61) {
357 curl_setopt($ch, CURLOPT_ENCODING, 'none');
358 $contents = @curl_exec($ch);
359 }
360
361 if ($contents === false) {
362 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
363 curl_close($ch);
364 return false;
365 }
366
367 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
368 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
369
370 $fetch_last_error_code = $http_code;
371
372 if ($http_code != 200 || $type && strpos($content_type, "$type") === false) {
373 if (curl_errno($ch) != 0) {
374 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
375 } else {
376 $fetch_last_error = "HTTP Code: $http_code";
377 }
378 curl_close($ch);
379 return false;
380 }
381
382 curl_close($ch);
383
384 return $contents;
385 } else {
386 if ($login && $pass){
387 $url_parts = array();
388
389 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
390
391 $pass = urlencode($pass);
392
393 if ($url_parts[1] && $url_parts[2]) {
394 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
395 }
396 }
397
398 $data = @file_get_contents($url);
399
400 if (!$data && function_exists('error_get_last')) {
401 $error = error_get_last();
402 $fetch_last_error = $error["message"];
403 }
404 return $data;
405 }
406
407 }
408
409 /**
410 * Try to determine the favicon URL for a feed.
411 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
412 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
413 *
414 * @param string $url A feed or page URL
415 * @access public
416 * @return mixed The favicon URL, or false if none was found.
417 */
418 function get_favicon_url($url) {
419
420 $favicon_url = false;
421
422 if ($html = @fetch_file_contents($url)) {
423
424 libxml_use_internal_errors(true);
425
426 $doc = new DOMDocument();
427 $doc->loadHTML($html);
428 $xpath = new DOMXPath($doc);
429
430 $base = $xpath->query('/html/head/base');
431 foreach ($base as $b) {
432 $url = $b->getAttribute("href");
433 break;
434 }
435
436 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
437 if (count($entries) > 0) {
438 foreach ($entries as $entry) {
439 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
440 break;
441 }
442 }
443 }
444
445 if (!$favicon_url)
446 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
447
448 return $favicon_url;
449 } // function get_favicon_url
450
451 function check_feed_favicon($site_url, $feed, $link) {
452 # print "FAVICON [$site_url]: $favicon_url\n";
453
454 $icon_file = ICONS_DIR . "/$feed.ico";
455
456 if (!file_exists($icon_file)) {
457 $favicon_url = get_favicon_url($site_url);
458
459 if ($favicon_url) {
460 // Limiting to "image" type misses those served with text/plain
461 $contents = fetch_file_contents($favicon_url); // , "image");
462
463 if ($contents) {
464 // Crude image type matching.
465 // Patterns gleaned from the file(1) source code.
466 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
467 // 0 string \000\000\001\000 MS Windows icon resource
468 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
469 }
470 elseif (preg_match('/^GIF8/', $contents)) {
471 // 0 string GIF8 GIF image data
472 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
473 }
474 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
475 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
476 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
477 }
478 elseif (preg_match('/^\xff\xd8/', $contents)) {
479 // 0 beshort 0xffd8 JPEG image data
480 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
481 }
482 else {
483 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
484 $contents = "";
485 }
486 }
487
488 if ($contents) {
489 $fp = @fopen($icon_file, "w");
490
491 if ($fp) {
492 fwrite($fp, $contents);
493 fclose($fp);
494 chmod($icon_file, 0644);
495 }
496 }
497 }
498 }
499 }
500
501 function print_select($id, $default, $values, $attributes = "") {
502 print "<select name=\"$id\" id=\"$id\" $attributes>";
503 foreach ($values as $v) {
504 if ($v == $default)
505 $sel = "selected=\"1\"";
506 else
507 $sel = "";
508
509 $v = trim($v);
510
511 print "<option value=\"$v\" $sel>$v</option>";
512 }
513 print "</select>";
514 }
515
516 function print_select_hash($id, $default, $values, $attributes = "") {
517 print "<select name=\"$id\" id='$id' $attributes>";
518 foreach (array_keys($values) as $v) {
519 if ($v == $default)
520 $sel = 'selected="selected"';
521 else
522 $sel = "";
523
524 $v = trim($v);
525
526 print "<option $sel value=\"$v\">".$values[$v]."</option>";
527 }
528
529 print "</select>";
530 }
531
532 function print_radio($id, $default, $true_is, $values, $attributes = "") {
533 foreach ($values as $v) {
534
535 if ($v == $default)
536 $sel = "checked";
537 else
538 $sel = "";
539
540 if ($v == $true_is) {
541 $sel .= " value=\"1\"";
542 } else {
543 $sel .= " value=\"0\"";
544 }
545
546 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
547 type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
548
549 }
550 }
551
552 function initialize_user_prefs($link, $uid, $profile = false) {
553
554 $uid = db_escape_string($link, $uid);
555
556 if (!$profile) {
557 $profile = "NULL";
558 $profile_qpart = "AND profile IS NULL";
559 } else {
560 $profile_qpart = "AND profile = '$profile'";
561 }
562
563 if (get_schema_version($link) < 63) $profile_qpart = "";
564
565 db_query($link, "BEGIN");
566
567 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
568
569 $u_result = db_query($link, "SELECT pref_name
570 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
571
572 $active_prefs = array();
573
574 while ($line = db_fetch_assoc($u_result)) {
575 array_push($active_prefs, $line["pref_name"]);
576 }
577
578 while ($line = db_fetch_assoc($result)) {
579 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
580 // print "adding " . $line["pref_name"] . "<br>";
581
582 $line["def_value"] = db_escape_string($link, $line["def_value"]);
583 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
584
585 if (get_schema_version($link) < 63) {
586 db_query($link, "INSERT INTO ttrss_user_prefs
587 (owner_uid,pref_name,value) VALUES
588 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
589
590 } else {
591 db_query($link, "INSERT INTO ttrss_user_prefs
592 (owner_uid,pref_name,value, profile) VALUES
593 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
594 }
595
596 }
597 }
598
599 db_query($link, "COMMIT");
600
601 }
602
603 function get_ssl_certificate_id() {
604 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
605 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
606 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
607 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
608 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
609 }
610 return "";
611 }
612
613 function authenticate_user($link, $login, $password, $check_only = false) {
614
615 if (!SINGLE_USER_MODE) {
616 $user_id = false;
617
618 global $pluginhost;
619 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER) as $plugin) {
620
621 $user_id = (int) $plugin->authenticate($login, $password);
622
623 if ($user_id) {
624 $_SESSION["auth_module"] = strtolower(get_class($plugin));
625 break;
626 }
627 }
628
629 if ($user_id && !$check_only) {
630 @session_start();
631
632 $_SESSION["uid"] = $user_id;
633 $_SESSION["version"] = VERSION;
634
635 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
636 WHERE id = '$user_id'");
637
638 $_SESSION["name"] = db_fetch_result($result, 0, "login");
639 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
640 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
641
642 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
643 $_SESSION["uid"]);
644
645 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
646 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
647 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
648
649 $_SESSION["last_version_check"] = time();
650
651 initialize_user_prefs($link, $_SESSION["uid"]);
652
653 return true;
654 }
655
656 return false;
657
658 } else {
659
660 $_SESSION["uid"] = 1;
661 $_SESSION["name"] = "admin";
662 $_SESSION["access_level"] = 10;
663
664 $_SESSION["hide_hello"] = true;
665 $_SESSION["hide_logout"] = true;
666
667 $_SESSION["auth_module"] = false;
668
669 if (!$_SESSION["csrf_token"]) {
670 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
671 }
672
673 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
674
675 initialize_user_prefs($link, $_SESSION["uid"]);
676
677 return true;
678 }
679 }
680
681 function make_password($length = 8) {
682
683 $password = "";
684 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
685
686 $i = 0;
687
688 while ($i < $length) {
689 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
690
691 if (!strstr($password, $char)) {
692 $password .= $char;
693 $i++;
694 }
695 }
696 return $password;
697 }
698
699 // this is called after user is created to initialize default feeds, labels
700 // or whatever else
701
702 // user preferences are checked on every login, not here
703
704 function initialize_user($link, $uid) {
705
706 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
707 values ('$uid', 'Tiny Tiny RSS: New Releases',
708 'http://tt-rss.org/releases.rss')");
709
710 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
711 values ('$uid', 'Tiny Tiny RSS: Forum',
712 'http://tt-rss.org/forum/rss.php')");
713 }
714
715 function logout_user() {
716 session_destroy();
717 if (isset($_COOKIE[session_name()])) {
718 setcookie(session_name(), '', time()-42000, '/');
719 }
720 }
721
722 function validate_csrf($csrf_token) {
723 return $csrf_token == $_SESSION['csrf_token'];
724 }
725
726 function load_user_plugins($link, $owner_uid) {
727 if ($owner_uid) {
728 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
729
730 global $pluginhost;
731 $pluginhost->load($plugins, $pluginhost::KIND_USER, $owner_uid);
732
733 if (get_schema_version($link) > 100) {
734 $pluginhost->load_data();
735 }
736 }
737 }
738
739 function login_sequence($link) {
740 $_SESSION["prefs_cache"] = false;
741
742 if (SINGLE_USER_MODE) {
743 @session_start();
744 authenticate_user($link, "admin", null);
745 cache_prefs($link);
746 load_user_plugins($link, $_SESSION["uid"]);
747 } else {
748 if (!validate_session($link)) $_SESSION["uid"] = false;
749
750 if (!$_SESSION["uid"]) {
751
752 if (AUTH_AUTO_LOGIN && authenticate_user($link, null, null)) {
753 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
754 } else {
755 authenticate_user($link, null, null, true);
756 }
757
758 if (!$_SESSION["uid"]) {
759 render_login_form($link);
760 @session_destroy();
761 setcookie(session_name(), '', time()-42000, '/');
762 exit;
763 }
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 = "date_entered > NOW() - INTERVAL '$intl hour' ";
1085 } else {
1086 $match_part = "date_entered > 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 $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 $allow_archived = true;
2417 $query_strategy_part = "true";
2418 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2419 } else if ($feed <= LABEL_BASE_INDEX) { // labels
2420 $label_id = feed_to_label_id($feed);
2421
2422 $query_strategy_part = "label_id = '$label_id' AND
2423 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2424 ttrss_user_labels2.article_id = ref_id";
2425
2426 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2427 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2428 $allow_archived = true;
2429
2430 } else {
2431 $query_strategy_part = "true";
2432 }
2433
2434 $order_by = "score DESC, date_entered DESC, updated DESC";
2435
2436 if ($view_mode == "unread_first") {
2437 $order_by = "unread DESC, $order_by";
2438 }
2439
2440 if ($override_order) {
2441 $order_by = $override_order;
2442 }
2443
2444 $feed_title = "";
2445
2446 if ($search) {
2447 $feed_title = T_sprintf("Search results: %s", $search);
2448 } else {
2449 if ($cat_view) {
2450 $feed_title = getCategoryTitle($link, $feed);
2451 } else {
2452 if (is_numeric($feed) && $feed > 0) {
2453 $result = db_query($link, "SELECT title,site_url,last_error
2454 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2455
2456 $feed_title = db_fetch_result($result, 0, "title");
2457 $feed_site_url = db_fetch_result($result, 0, "site_url");
2458 $last_error = db_fetch_result($result, 0, "last_error");
2459 } else {
2460 $feed_title = getFeedTitle($link, $feed);
2461 }
2462 }
2463 }
2464
2465 $content_query_part = "content as content_preview, cached_content, ";
2466
2467 if (is_numeric($feed)) {
2468
2469 if ($feed >= 0) {
2470 $feed_kind = "Feeds";
2471 } else {
2472 $feed_kind = "Labels";
2473 }
2474
2475 if ($limit_query_part) {
2476 $offset_query_part = "OFFSET $offset";
2477 }
2478
2479 // proper override_order applied above
2480 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2481 if (!$override_order) {
2482 $order_by = "ttrss_feeds.title, $order_by";
2483 } else {
2484 $order_by = "ttrss_feeds.title, $override_order";
2485 }
2486 }
2487
2488 if (!$allow_archived) {
2489 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2490 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2491
2492 } else {
2493 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2494 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2495 }
2496
2497 $query = "SELECT DISTINCT
2498 date_entered,
2499 guid,
2500 ttrss_entries.id,ttrss_entries.title,
2501 updated,
2502 label_cache,
2503 tag_cache,
2504 always_display_enclosures,
2505 site_url,
2506 note,
2507 num_comments,
2508 comments,
2509 int_id,
2510 hide_images,
2511 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2512 last_marked, last_published,
2513 $vfeed_query_part
2514 $content_query_part
2515 author,score
2516 FROM
2517 $from_qpart
2518 WHERE
2519 $feed_check_qpart
2520 ttrss_user_entries.ref_id = ttrss_entries.id AND
2521 ttrss_user_entries.owner_uid = '$owner_uid' AND
2522 $search_query_part
2523 $filter_query_part
2524 $view_query_part
2525 $since_id_part
2526 $query_strategy_part ORDER BY $order_by
2527 $limit_query_part $offset_query_part";
2528
2529 if ($_REQUEST["debug"]) print $query;
2530
2531 $result = db_query($link, $query);
2532
2533 } else {
2534 // browsing by tag
2535
2536 $select_qpart = "SELECT DISTINCT " .
2537 "date_entered," .
2538 "guid," .
2539 "note," .
2540 "ttrss_entries.id as id," .
2541 "title," .
2542 "updated," .
2543 "unread," .
2544 "feed_id," .
2545 "orig_feed_id," .
2546 "marked," .
2547 "num_comments, " .
2548 "comments, " .
2549 "tag_cache," .
2550 "label_cache," .
2551 "link," .
2552 "last_read," .
2553 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2554 "last_marked, last_published, " .
2555 $since_id_part .
2556 $vfeed_query_part .
2557 $content_query_part .
2558 "score ";
2559
2560 $feed_kind = "Tags";
2561 $all_tags = explode(",", $feed);
2562 if ($search_mode == 'any') {
2563 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2564 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2565 $where_qpart = " WHERE " .
2566 "ref_id = ttrss_entries.id AND " .
2567 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2568 "post_int_id = int_id AND $tag_sql AND " .
2569 $view_query_part .
2570 $search_query_part .
2571 $query_strategy_part . " ORDER BY $order_by " .
2572 $limit_query_part;
2573
2574 } else {
2575 $i = 1;
2576 $sub_selects = array();
2577 $sub_ands = array();
2578 foreach ($all_tags as $term) {
2579 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");
2580 $i++;
2581 }
2582 if ($i > 2) {
2583 $x = 1;
2584 $y = 2;
2585 do {
2586 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2587 $x++;
2588 $y++;
2589 } while ($y < $i);
2590 }
2591 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2592 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2593 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2594 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2595 }
2596 // error_log("TAG SQL: " . $tag_sql);
2597 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2598
2599 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2600 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2601 }
2602
2603 return array($result, $feed_title, $feed_site_url, $last_error);
2604
2605 }
2606
2607 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2608 if (!$owner) $owner = $_SESSION["uid"];
2609
2610 $res = trim($str); if (!$res) return '';
2611
2612 if (strpos($res, "href=") === false)
2613 $res = rewrite_urls($res);
2614
2615 $charset_hack = '<head>
2616 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2617 </head>';
2618
2619 $res = trim($res); if (!$res) return '';
2620
2621 libxml_use_internal_errors(true);
2622
2623 $doc = new DOMDocument();
2624 $doc->loadHTML($charset_hack . $res);
2625 $xpath = new DOMXPath($doc);
2626
2627 $entries = $xpath->query('(//a[@href]|//img[@src])');
2628
2629 foreach ($entries as $entry) {
2630
2631 if ($site_url) {
2632
2633 if ($entry->hasAttribute('href'))
2634 $entry->setAttribute('href',
2635 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2636
2637 if ($entry->hasAttribute('src')) {
2638 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2639
2640 $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
2641
2642 if (file_exists($cached_filename)) {
2643 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
2644 }
2645
2646 $entry->setAttribute('src', $src);
2647 }
2648
2649 if ($entry->nodeName == 'img') {
2650 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2651 $force_remove_images || $_SESSION["bw_limit"]) {
2652
2653 $p = $doc->createElement('p');
2654
2655 $a = $doc->createElement('a');
2656 $a->setAttribute('href', $entry->getAttribute('src'));
2657
2658 $a->appendChild(new DOMText($entry->getAttribute('src')));
2659 $a->setAttribute('target', '_blank');
2660
2661 $p->appendChild($a);
2662
2663 $entry->parentNode->replaceChild($p, $entry);
2664 }
2665 }
2666 }
2667
2668 if (strtolower($entry->nodeName) == "a") {
2669 $entry->setAttribute("target", "_blank");
2670 }
2671 }
2672
2673 $entries = $xpath->query('//iframe');
2674 foreach ($entries as $entry) {
2675 $entry->setAttribute('sandbox', 'allow-scripts');
2676
2677 }
2678
2679 $allowed_elements = array('a', 'address', 'audio', 'article',
2680 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2681 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2682 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2683 'header', 'html', 'i', 'img', 'ins', 'kbd',
2684 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2685 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2686 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2687 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2688
2689 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2690
2691 $disallowed_attributes = array('id', 'style', 'class');
2692
2693 global $pluginhost;
2694
2695 if (isset($pluginhost)) {
2696 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE) as $plugin) {
2697 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2698 if (is_array($retval)) {
2699 $doc = $retval[0];
2700 $allowed_elements = $retval[1];
2701 $disallowed_attributes = $retval[2];
2702 } else {
2703 $doc = $retval;
2704 }
2705 }
2706 }
2707
2708 $doc->removeChild($doc->firstChild); //remove doctype
2709 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2710 $res = $doc->saveHTML();
2711 return $res;
2712 }
2713
2714 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2715 $entries = $doc->getElementsByTagName("*");
2716
2717 foreach ($entries as $entry) {
2718 if (!in_array($entry->nodeName, $allowed_elements)) {
2719 $entry->parentNode->removeChild($entry);
2720 }
2721
2722 if ($entry->hasAttributes()) {
2723 $attrs_to_remove = array();
2724
2725 foreach ($entry->attributes as $attr) {
2726
2727 if (strpos($attr->nodeName, 'on') === 0) {
2728 array_push($attrs_to_remove, $attr);
2729 }
2730
2731 if (in_array($attr->nodeName, $disallowed_attributes)) {
2732 array_push($attrs_to_remove, $attr);
2733 }
2734 }
2735
2736 foreach ($attrs_to_remove as $attr) {
2737 $entry->removeAttributeNode($attr);
2738 }
2739 }
2740 }
2741
2742 return $doc;
2743 }
2744
2745 function check_for_update($link) {
2746 if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
2747 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
2748 "&iid=" . sha1(SELF_URL_PATH);
2749
2750 $version_data = @fetch_file_contents($version_url);
2751
2752 if ($version_data) {
2753 $version_data = json_decode($version_data, true);
2754 if ($version_data && $version_data['version']) {
2755
2756 if (version_compare(VERSION, $version_data['version']) == -1) {
2757 return $version_data;
2758 }
2759 }
2760 }
2761 }
2762 return false;
2763 }
2764
2765 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2766
2767 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2768 if (count($ids) == 0) return;
2769
2770 $tmp_ids = array();
2771
2772 foreach ($ids as $id) {
2773 array_push($tmp_ids, "ref_id = '$id'");
2774 }
2775
2776 $ids_qpart = join(" OR ", $tmp_ids);
2777
2778 if ($cmode == 0) {
2779 db_query($link, "UPDATE ttrss_user_entries SET
2780 unread = false,last_read = NOW()
2781 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2782 } else if ($cmode == 1) {
2783 db_query($link, "UPDATE ttrss_user_entries SET
2784 unread = true
2785 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2786 } else {
2787 db_query($link, "UPDATE ttrss_user_entries SET
2788 unread = NOT unread,last_read = NOW()
2789 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2790 }
2791
2792 /* update ccache */
2793
2794 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2795 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2796
2797 while ($line = db_fetch_assoc($result)) {
2798 ccache_update($link, $line["feed_id"], $owner_uid);
2799 }
2800 }
2801
2802 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2803
2804 $a_id = db_escape_string($link, $id);
2805
2806 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2807
2808 $query = "SELECT DISTINCT tag_name,
2809 owner_uid as owner FROM
2810 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2811 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2812
2813 $obj_id = md5("TAGS:$owner_uid:$id");
2814 $tags = array();
2815
2816 /* check cache first */
2817
2818 if ($tag_cache === false) {
2819 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2820 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2821
2822 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2823 }
2824
2825 if ($tag_cache) {
2826 $tags = explode(",", $tag_cache);
2827 } else {
2828
2829 /* do it the hard way */
2830
2831 $tmp_result = db_query($link, $query);
2832
2833 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2834 array_push($tags, $tmp_line["tag_name"]);
2835 }
2836
2837 /* update the cache */
2838
2839 $tags_str = db_escape_string($link, join(",", $tags));
2840
2841 db_query($link, "UPDATE ttrss_user_entries
2842 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2843 AND owner_uid = $owner_uid");
2844 }
2845
2846 return $tags;
2847 }
2848
2849 function trim_array($array) {
2850 $tmp = $array;
2851 array_walk($tmp, 'trim');
2852 return $tmp;
2853 }
2854
2855 function tag_is_valid($tag) {
2856 if ($tag == '') return false;
2857 if (preg_match("/^[0-9]*$/", $tag)) return false;
2858 if (mb_strlen($tag) > 250) return false;
2859
2860 if (function_exists('iconv')) {
2861 $tag = iconv("utf-8", "utf-8", $tag);
2862 }
2863
2864 if (!$tag) return false;
2865
2866 return true;
2867 }
2868
2869 function render_login_form($link) {
2870 require_once "login_form.php";
2871 exit;
2872 }
2873
2874 // from http://developer.apple.com/internet/safari/faq.html
2875 function no_cache_incantation() {
2876 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2877 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2878 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2879 header("Cache-Control: post-check=0, pre-check=0", false);
2880 header("Pragma: no-cache"); // HTTP/1.0
2881 }
2882
2883 function format_warning($msg, $id = "") {
2884 global $link;
2885 return "<div class=\"warning\" id=\"$id\">
2886 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2887 }
2888
2889 function format_notice($msg, $id = "") {
2890 global $link;
2891 return "<div class=\"notice\" id=\"$id\">
2892 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2893 }
2894
2895 function format_error($msg, $id = "") {
2896 global $link;
2897 return "<div class=\"error\" id=\"$id\">
2898 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2899 }
2900
2901 function print_notice($msg) {
2902 return print format_notice($msg);
2903 }
2904
2905 function print_warning($msg) {
2906 return print format_warning($msg);
2907 }
2908
2909 function print_error($msg) {
2910 return print format_error($msg);
2911 }
2912
2913
2914 function T_sprintf() {
2915 $args = func_get_args();
2916 return vsprintf(__(array_shift($args)), $args);
2917 }
2918
2919 function format_inline_player($link, $url, $ctype) {
2920
2921 $entry = "";
2922
2923 $url = htmlspecialchars($url);
2924
2925 if (strpos($ctype, "audio/") === 0) {
2926
2927 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2928 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2929 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2930
2931 $id = 'AUDIO-' . uniqid();
2932
2933 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2934 <source type=\"$ctype\" src=\"$url\"></source>
2935 </audio>";
2936
2937 $entry .= "<span onclick=\"player(this)\"
2938 title=\"".__("Click to play")."\" status=\"0\"
2939 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2940
2941 } else {
2942
2943 $entry .= "<object type=\"application/x-shockwave-flash\"
2944 data=\"lib/button/musicplayer.swf?song_url=$url\"
2945 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2946 <param name=\"movie\"
2947 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2948 </object>";
2949 }
2950
2951 if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
2952 href=\"$url\">" . basename($url) . "</a>";
2953
2954 return $entry;
2955
2956 }
2957
2958 return "";
2959
2960 /* $filename = substr($url, strrpos($url, "/")+1);
2961
2962 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2963 $filename . " (" . $ctype . ")" . "</a>"; */
2964
2965 }
2966
2967 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2968 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2969
2970 $rv = array();
2971
2972 $rv['id'] = $id;
2973
2974 /* we can figure out feed_id from article id anyway, why do we
2975 * pass feed_id here? let's ignore the argument :( */
2976
2977 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2978 WHERE ref_id = '$id'");
2979
2980 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2981
2982 $rv['feed_id'] = $feed_id;
2983
2984 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2985
2986 if ($mark_as_read) {
2987 $result = db_query($link, "UPDATE ttrss_user_entries
2988 SET unread = false,last_read = NOW()
2989 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2990
2991 ccache_update($link, $feed_id, $owner_uid);
2992 }
2993
2994 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2995 ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
2996 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2997 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2998 num_comments,
2999 tag_cache,
3000 author,
3001 orig_feed_id,
3002 note,
3003 cached_content
3004 FROM ttrss_entries,ttrss_user_entries
3005 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3006
3007 if ($result) {
3008
3009 $line = db_fetch_assoc($result);
3010
3011 $tag_cache = $line["tag_cache"];
3012
3013 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3014 unset($line["tag_cache"]);
3015
3016 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3017
3018 global $pluginhost;
3019
3020 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE) as $p) {
3021 $line = $p->hook_render_article($line);
3022 }
3023
3024 $num_comments = $line["num_comments"];
3025 $entry_comments = "";
3026
3027 if ($num_comments > 0) {
3028 if ($line["comments"]) {
3029 $comments_url = htmlspecialchars($line["comments"]);
3030 } else {
3031 $comments_url = htmlspecialchars($line["link"]);
3032 }
3033 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3034 } else {
3035 if ($line["comments"] && $line["link"] != $line["comments"]) {
3036 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3037 }
3038 }
3039
3040 if ($zoom_mode) {
3041 header("Content-Type: text/html");
3042 $rv['content'] .= "<html><head>
3043 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3044 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3045 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3046 </head><body id=\"ttrssZoom\">";
3047 }
3048
3049 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3050
3051 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3052
3053 $entry_author = $line["author"];
3054
3055 if ($entry_author) {
3056 $entry_author = __(" - ") . $entry_author;
3057 }
3058
3059 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3060 $owner_uid, true);
3061
3062 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3063
3064 if ($line["link"]) {
3065 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3066 title=\"".htmlspecialchars($line['title'])."\"
3067 href=\"" .
3068 htmlspecialchars($line["link"]) . "\">" .
3069 $line["title"] . "</a>" .
3070 "<span class='author'>$entry_author</span></div>";
3071 } else {
3072 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3073 }
3074
3075 $tags_str = format_tags_string($line["tags"], $id);
3076 $tags_str_full = join(", ", $line["tags"]);
3077
3078 if (!$tags_str_full) $tags_str_full = __("no tags");
3079
3080 if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
3081
3082 $rv['content'] .= "<div class='postTags' style='float : right'>
3083 <img src='images/tag.png'
3084 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
3085
3086 if (!$zoom_mode) {
3087 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3088 <a title=\"".__('Edit tags for this article')."\"
3089 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3090
3091 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3092 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3093 position=\"below\">$tags_str_full</div>";
3094
3095 global $pluginhost;
3096
3097 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON) as $p) {
3098 $rv['content'] .= $p->hook_article_button($line);
3099 }
3100
3101
3102 } else {
3103 $tags_str = strip_tags($tags_str);
3104 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3105 }
3106 $rv['content'] .= "</div>";
3107 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3108
3109 if ($line["orig_feed_id"]) {
3110
3111 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3112 WHERE id = ".$line["orig_feed_id"]);
3113
3114 if (db_num_rows($tmp_result) != 0) {
3115
3116 $rv['content'] .= "<div clear='both'>";
3117 $rv['content'] .= __("Originally from:");
3118
3119 $rv['content'] .= "&nbsp;";
3120
3121 $tmp_line = db_fetch_assoc($tmp_result);
3122
3123 $rv['content'] .= "<a target='_blank'
3124 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3125 $tmp_line['title'] . "</a>";
3126
3127 $rv['content'] .= "&nbsp;";
3128
3129 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3130 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3131
3132 $rv['content'] .= "</div>";
3133 }
3134 }
3135
3136 $rv['content'] .= "</div>";
3137
3138 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3139 if ($line['note']) {
3140 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3141 }
3142 $rv['content'] .= "</div>";
3143
3144 $rv['content'] .= "<div class=\"postContent\">";
3145
3146 $rv['content'] .= $line["content"];
3147
3148 $rv['content'] .= format_article_enclosures($link, $id,
3149 $always_display_enclosures, $line["content"], $line["hide_images"]);
3150
3151 $rv['content'] .= "</div>";
3152
3153 $rv['content'] .= "</div>";
3154
3155 }
3156
3157 if ($zoom_mode) {
3158 $rv['content'] .= "
3159 <div class='footer'>
3160 <button onclick=\"return window.close()\">".
3161 __("Close this window")."</button></div>";
3162 $rv['content'] .= "</body></html>";
3163 }
3164
3165 return $rv;
3166
3167 }
3168
3169 function print_checkpoint($n, $s) {
3170 $ts = microtime(true);
3171 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3172 return $ts;
3173 }
3174
3175 function sanitize_tag($tag) {
3176 $tag = trim($tag);
3177
3178 $tag = mb_strtolower($tag, 'utf-8');
3179
3180 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3181
3182 // $tag = str_replace('"', "", $tag);
3183 // $tag = str_replace("+", " ", $tag);
3184 $tag = str_replace("technorati tag: ", "", $tag);
3185
3186 return $tag;
3187 }
3188
3189 function get_self_url_prefix() {
3190 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
3191 return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
3192 } else {
3193 return SELF_URL_PATH;
3194 }
3195 }
3196
3197 /**
3198 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3199 *
3200 * @return string The Mozilla Firefox feed adding URL.
3201 */
3202 function add_feed_url() {
3203 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3204
3205 $url_path = get_self_url_prefix() .
3206 "/public.php?op=subscribe&feed_url=%s";
3207 return $url_path;
3208 } // function add_feed_url
3209
3210 function encrypt_password($pass, $salt = '', $mode2 = false) {
3211 if ($salt && $mode2) {
3212 return "MODE2:" . hash('sha256', $salt . $pass);
3213 } else if ($salt) {
3214 return "SHA1X:" . sha1("$salt:$pass");
3215 } else {
3216 return "SHA1:" . sha1($pass);
3217 }
3218 } // function encrypt_password
3219
3220 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3221 $filters = array();
3222
3223 $cat_id = (int)getFeedCategory($link, $feed_id);
3224
3225 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3226 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3227
3228 $check_cats = join(",", array_merge(
3229 getParentCategories($link, $cat_id, $owner_uid),
3230 array($cat_id)));
3231
3232 while ($line = db_fetch_assoc($result)) {
3233 $filter_id = $line["id"];
3234
3235 $result2 = db_query($link, "SELECT
3236 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3237 FROM ttrss_filters2_rules AS r,
3238 ttrss_filter_types AS t
3239 WHERE
3240 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3241 (feed_id IS NULL OR feed_id = '$feed_id') AND
3242 filter_type = t.id AND filter_id = '$filter_id'");
3243
3244 $rules = array();
3245 $actions = array();
3246
3247 while ($rule_line = db_fetch_assoc($result2)) {
3248 # print_r($rule_line);
3249
3250 $rule = array();
3251 $rule["reg_exp"] = $rule_line["reg_exp"];
3252 $rule["type"] = $rule_line["type_name"];
3253 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3254
3255 array_push($rules, $rule);
3256 }
3257
3258 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3259 FROM ttrss_filters2_actions AS a,
3260 ttrss_filter_actions AS t
3261 WHERE
3262 action_id = t.id AND filter_id = '$filter_id'");
3263
3264 while ($action_line = db_fetch_assoc($result2)) {
3265 # print_r($action_line);
3266
3267 $action = array();
3268 $action["type"] = $action_line["type_name"];
3269 $action["param"] = $action_line["action_param"];
3270
3271 array_push($actions, $action);
3272 }
3273
3274
3275 $filter = array();
3276 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3277 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3278 $filter["rules"] = $rules;
3279 $filter["actions"] = $actions;
3280
3281 if (count($rules) > 0 && count($actions) > 0) {
3282 array_push($filters, $filter);
3283 }
3284 }
3285
3286 return $filters;
3287 }
3288
3289 function get_score_pic($score) {
3290 if ($score > 100) {
3291 return "score_high.png";
3292 } else if ($score > 0) {
3293 return "score_half_high.png";
3294 } else if ($score < -100) {
3295 return "score_low.png";
3296 } else if ($score < 0) {
3297 return "score_half_low.png";
3298 } else {
3299 return "score_neutral.png";
3300 }
3301 }
3302
3303 function feed_has_icon($id) {
3304 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
3305 }
3306
3307 function init_connection($link) {
3308 if ($link) {
3309
3310 if (DB_TYPE == "pgsql") {
3311 pg_query($link, "set client_encoding = 'UTF-8'");
3312 pg_set_client_encoding("UNICODE");
3313 pg_query($link, "set datestyle = 'ISO, european'");
3314 pg_query($link, "set TIME ZONE 0");
3315 } else {
3316 db_query($link, "SET time_zone = '+0:0'");
3317
3318 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
3319 db_query($link, "SET NAMES " . MYSQL_CHARSET);
3320 }
3321 }
3322
3323 global $pluginhost;
3324
3325 $pluginhost = new PluginHost($link);
3326 $pluginhost->load(PLUGINS, $pluginhost::KIND_ALL);
3327
3328 return true;
3329 } else {
3330 print "Unable to connect to database:" . db_last_error();
3331 return false;
3332 }
3333 }
3334
3335 function format_tags_string($tags, $id) {
3336
3337 $tags_str = "";
3338 $tags_nolinks_str = "";
3339
3340 $num_tags = 0;
3341
3342 $tag_limit = 6;
3343
3344 $formatted_tags = array();
3345
3346 foreach ($tags as $tag) {
3347 $num_tags++;
3348 $tag_escaped = str_replace("'", "\\'", $tag);
3349
3350 if (mb_strlen($tag) > 30) {
3351 $tag = truncate_string($tag, 30);
3352 }
3353
3354 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3355
3356 array_push($formatted_tags, $tag_str);
3357
3358 $tmp_tags_str = implode(", ", $formatted_tags);
3359
3360 if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
3361 break;
3362 }
3363 }
3364
3365 $tags_str = implode(", ", $formatted_tags);
3366
3367 if ($num_tags < count($tags)) {
3368 $tags_str .= ", &hellip;";
3369 }
3370
3371 if ($num_tags == 0) {
3372 $tags_str = __("no tags");
3373 }
3374
3375 return $tags_str;
3376
3377 }
3378
3379 function format_article_labels($labels, $id) {
3380
3381 $labels_str = "";
3382
3383 foreach ($labels as $l) {
3384 $labels_str .= sprintf("<span class='hlLabelRef'
3385 style='color : %s; background-color : %s'>%s</span>",
3386 $l[2], $l[3], $l[1]);
3387 }
3388
3389 return $labels_str;
3390
3391 }
3392
3393 function format_article_note($id, $note, $allow_edit = true) {
3394
3395 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3396 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3397 ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
3398
3399 return $str;
3400 }
3401
3402
3403 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3404 if ($parent_cat_id) {
3405 $parent_qpart = "parent_cat = '$parent_cat_id'";
3406 $parent_insert = "'$parent_cat_id'";
3407 } else {
3408 $parent_qpart = "parent_cat IS NULL";
3409 $parent_insert = "NULL";
3410 }
3411
3412 $result = db_query($link,
3413 "SELECT id FROM ttrss_feed_categories
3414 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3415
3416 if (db_num_rows($result) == 0) {
3417 return false;
3418 } else {
3419 return db_fetch_result($result, 0, "id");
3420 }
3421 }
3422
3423 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3424
3425 if (!$feed_cat) return false;
3426
3427 db_query($link, "BEGIN");
3428
3429 if ($parent_cat_id) {
3430 $parent_qpart = "parent_cat = '$parent_cat_id'";
3431 $parent_insert = "'$parent_cat_id'";
3432 } else {
3433 $parent_qpart = "parent_cat IS NULL";
3434 $parent_insert = "NULL";
3435 }
3436
3437 $feed_cat = mb_substr($feed_cat, 0, 250);
3438
3439 $result = db_query($link,
3440 "SELECT id FROM ttrss_feed_categories
3441 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3442
3443 if (db_num_rows($result) == 0) {
3444
3445 $result = db_query($link,
3446 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3447 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3448
3449 db_query($link, "COMMIT");
3450
3451 return true;
3452 }
3453
3454 return false;
3455 }
3456
3457 function getArticleFeed($link, $id) {
3458 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3459 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3460
3461 if (db_num_rows($result) != 0) {
3462 return db_fetch_result($result, 0, "feed_id");
3463 } else {
3464 return 0;
3465 }
3466 }
3467
3468 /**
3469 * Fixes incomplete URLs by prepending "http://".
3470 * Also replaces feed:// with http://, and
3471 * prepends a trailing slash if the url is a domain name only.
3472 *
3473 * @param string $url Possibly incomplete URL
3474 *
3475 * @return string Fixed URL.
3476 */
3477 function fix_url($url) {
3478 if (strpos($url, '://') === false) {
3479 $url = 'http://' . $url;
3480 } else if (substr($url, 0, 5) == 'feed:') {
3481 $url = 'http:' . substr($url, 5);
3482 }
3483
3484 //prepend slash if the URL has no slash in it
3485 // "http://www.example" -> "http://www.example/"
3486 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
3487 $url .= '/';
3488 }
3489
3490 if ($url != "http:///")
3491 return $url;
3492 else
3493 return '';
3494 }
3495
3496 function validate_feed_url($url) {
3497 $parts = parse_url($url);
3498
3499 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
3500
3501 }
3502
3503 function get_article_enclosures($link, $id) {
3504
3505 $query = "SELECT * FROM ttrss_enclosures
3506 WHERE post_id = '$id' AND content_url != ''";
3507
3508 $rv = array();
3509
3510 $result = db_query($link, $query);
3511
3512 if (db_num_rows($result) > 0) {
3513 while ($line = db_fetch_assoc($result)) {
3514 array_push($rv, $line);
3515 }
3516 }
3517
3518 return $rv;
3519 }
3520
3521 function save_email_address($link, $email) {
3522 // FIXME: implement persistent storage of emails
3523
3524 if (!$_SESSION['stored_emails'])
3525 $_SESSION['stored_emails'] = array();
3526
3527 if (!in_array($email, $_SESSION['stored_emails']))
3528 array_push($_SESSION['stored_emails'], $email);
3529 }
3530
3531
3532 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3533
3534 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3535
3536 $sql_is_cat = bool_to_sql_bool($is_cat);
3537
3538 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3539 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3540 AND owner_uid = " . $owner_uid);
3541
3542 if (db_num_rows($result) == 1) {
3543 return db_fetch_result($result, 0, "access_key");
3544 } else {
3545 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3546
3547 $result = db_query($link, "INSERT INTO ttrss_access_keys
3548 (access_key, feed_id, is_cat, owner_uid)
3549 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3550
3551 return $key;
3552 }
3553 return false;
3554 }
3555
3556 function get_feeds_from_html($url, $content)
3557 {
3558 $url = fix_url($url);
3559 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
3560
3561 libxml_use_internal_errors(true);
3562
3563 $doc = new DOMDocument();
3564 $doc->loadHTML($content);
3565 $xpath = new DOMXPath($doc);
3566 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3567 $feedUrls = array();
3568 foreach ($entries as $entry) {
3569 if ($entry->hasAttribute('href')) {
3570 $title = $entry->getAttribute('title');
3571 if ($title == '') {
3572 $title = $entry->getAttribute('type');
3573 }
3574 $feedUrl = rewrite_relative_url(
3575 $baseUrl, $entry->getAttribute('href')
3576 );
3577 $feedUrls[$feedUrl] = $title;
3578 }
3579 }
3580 return $feedUrls;
3581 }
3582
3583 function is_html($content) {
3584 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3585 }
3586
3587 function url_is_html($url, $login = false, $pass = false) {
3588 return is_html(fetch_file_contents($url, false, $login, $pass));
3589 }
3590
3591 function print_label_select($link, $name, $value, $attributes = "") {
3592
3593 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3594 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3595
3596 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3597 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3598
3599 while ($line = db_fetch_assoc($result)) {
3600
3601 $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
3602
3603 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3604 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3605
3606 }
3607
3608 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3609
3610 print "</select>";
3611
3612
3613 }
3614
3615 function format_article_enclosures($link, $id, $always_display_enclosures,
3616 $article_content, $hide_images = false) {
3617
3618 $result = get_article_enclosures($link, $id);
3619 $rv = '';
3620
3621 if (count($result) > 0) {
3622
3623 $entries_html = array();
3624 $entries = array();
3625 $entries_inline = array();
3626
3627 foreach ($result as $line) {
3628
3629 $url = $line["content_url"];
3630 $ctype = $line["content_type"];
3631
3632 if (!$ctype) $ctype = __("unknown type");
3633
3634 $filename = substr($url, strrpos($url, "/")+1);
3635
3636 $player = format_inline_player($link, $url, $ctype);
3637
3638 if ($player) array_push($entries_inline, $player);
3639
3640 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3641 # $filename . " (" . $ctype . ")" . "</a>";
3642
3643 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3644 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3645
3646 array_push($entries_html, $entry);
3647
3648 $entry = array();
3649
3650 $entry["type"] = $ctype;
3651 $entry["filename"] = $filename;
3652 $entry["url"] = $url;
3653
3654 array_push($entries, $entry);
3655 }
3656
3657 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3658 if ($always_display_enclosures ||
3659 !preg_match("/<img/i", $article_content)) {
3660
3661 foreach ($entries as $entry) {
3662
3663 if (preg_match("/image/", $entry["type"]) ||
3664 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3665
3666 if (!$hide_images) {
3667 $rv .= "<p><img
3668 alt=\"".htmlspecialchars($entry["filename"])."\"
3669 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3670 } else {
3671 $rv .= "<p><a target=\"_blank\"
3672 href=\"".htmlspecialchars($entry["url"])."\"
3673 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3674
3675 }
3676 }
3677 }
3678 }
3679 }
3680
3681 if (count($entries_inline) > 0) {
3682 $rv .= "<hr clear='both'/>";
3683 foreach ($entries_inline as $entry) { $rv .= $entry; };
3684 $rv .= "<hr clear='both'/>";
3685 }
3686
3687 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3688 "<option value=''>" . __('Attachments')."</option>";
3689
3690 foreach ($entries as $entry) {
3691 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3692
3693 };
3694
3695 $rv .= "</select>";
3696 }
3697
3698 return $rv;
3699 }
3700
3701 function getLastArticleId($link) {
3702 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3703 WHERE owner_uid = " . $_SESSION["uid"]);
3704
3705 if (db_num_rows($result) == 1) {
3706 return db_fetch_result($result, 0, "id");
3707 } else {
3708 return -1;
3709 }
3710 }
3711
3712 function build_url($parts) {
3713 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3714 }
3715
3716 /**
3717 * Converts a (possibly) relative URL to a absolute one.
3718 *
3719 * @param string $url Base URL (i.e. from where the document is)
3720 * @param string $rel_url Possibly relative URL in the document
3721 *
3722 * @return string Absolute URL
3723 */
3724 function rewrite_relative_url($url, $rel_url) {
3725 if (strpos($rel_url, "magnet:") === 0) {
3726 return $rel_url;
3727 } else if (strpos($rel_url, "://") !== false) {
3728 return $rel_url;
3729 } else if (strpos($rel_url, "//") === 0) {
3730 # protocol-relative URL (rare but they exist)
3731 return $rel_url;
3732 } else if (strpos($rel_url, "/") === 0)
3733 {
3734 $parts = parse_url($url);
3735 $parts['path'] = $rel_url;
3736
3737 return build_url($parts);
3738
3739 } else {
3740 $parts = parse_url($url);
3741 if (!isset($parts['path'])) {
3742 $parts['path'] = '/';
3743 }
3744 $dir = $parts['path'];
3745 if (substr($dir, -1) !== '/') {
3746 $dir = dirname($parts['path']);
3747 $dir !== '/' && $dir .= '/';
3748 }
3749 $parts['path'] = $dir . $rel_url;
3750
3751 return build_url($parts);
3752 }
3753 }
3754
3755 function sphinx_search($query, $offset = 0, $limit = 30) {
3756 require_once 'lib/sphinxapi.php';
3757
3758 $sphinxClient = new SphinxClient();
3759
3760 $sphinxClient->SetServer('localhost', 9312);
3761 $sphinxClient->SetConnectTimeout(1);
3762
3763 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3764 'feed_title' => 20));
3765
3766 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
3767 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
3768 $sphinxClient->SetLimits($offset, $limit, 1000);
3769 $sphinxClient->SetArrayResult(false);
3770 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3771
3772 $result = $sphinxClient->Query($query, SPHINX_INDEX);
3773
3774 $ids = array();
3775
3776 if (is_array($result['matches'])) {
3777 foreach (array_keys($result['matches']) as $int_id) {
3778 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3779 array_push($ids, $ref_id);
3780 }
3781 }
3782
3783 return $ids;
3784 }
3785
3786 function cleanup_tags($link, $days = 14, $limit = 1000) {
3787
3788 if (DB_TYPE == "pgsql") {
3789 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3790 } else if (DB_TYPE == "mysql") {
3791 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3792 }
3793
3794 $tags_deleted = 0;
3795
3796 while ($limit > 0) {
3797 $limit_part = 500;
3798
3799 $query = "SELECT ttrss_tags.id AS id
3800 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3801 WHERE post_int_id = int_id AND $interval_query AND
3802 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3803
3804 $result = db_query($link, $query);
3805
3806 $ids = array();
3807
3808 while ($line = db_fetch_assoc($result)) {
3809 array_push($ids, $line['id']);
3810 }
3811
3812 if (count($ids) > 0) {
3813 $ids = join(",", $ids);
3814
3815 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3816 $tags_deleted += db_affected_rows($link, $tmp_result);
3817 } else {
3818 break;
3819 }
3820
3821 $limit -= $limit_part;
3822 }
3823
3824 return $tags_deleted;
3825 }
3826
3827 function print_user_stylesheet($link) {
3828 $value = get_pref($link, 'USER_STYLESHEET');
3829
3830 if ($value) {
3831 print "<style type=\"text/css\">";
3832 print str_replace("<br/>", "\n", $value);
3833 print "</style>";
3834 }
3835
3836 }
3837
3838 function rewrite_urls($html) {
3839 libxml_use_internal_errors(true);
3840
3841 $charset_hack = '<head>
3842 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3843 </head>';
3844
3845 $doc = new DOMDocument();
3846 $doc->loadHTML($charset_hack . $html);
3847 $xpath = new DOMXPath($doc);
3848
3849 $entries = $xpath->query('//*/text()');
3850
3851 foreach ($entries as $entry) {
3852 if (strstr($entry->wholeText, "://") !== false) {
3853 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3854 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText);
3855
3856 if ($text != $entry->wholeText) {
3857 $cdoc = new DOMDocument();
3858 $cdoc->loadHTML($charset_hack . $text);
3859
3860
3861 foreach ($cdoc->childNodes as $cnode) {
3862 $cnode = $doc->importNode($cnode, true);
3863
3864 if ($cnode) {
3865 $entry->parentNode->insertBefore($cnode);
3866 }
3867 }
3868
3869 $entry->parentNode->removeChild($entry);
3870
3871 }
3872 }
3873 }
3874
3875 $node = $doc->getElementsByTagName('body')->item(0);
3876
3877 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3878 if ($node)
3879 return $doc->saveXML($node);
3880 else
3881 return $html;
3882 }
3883
3884 function filter_to_sql($link, $filter, $owner_uid) {
3885 $query = array();
3886
3887 if (DB_TYPE == "pgsql")
3888 $reg_qpart = "~";
3889 else
3890 $reg_qpart = "REGEXP";
3891
3892 foreach ($filter["rules"] AS $rule) {
3893 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3894 $rule['reg_exp']) !== FALSE;
3895
3896 if ($regexp_valid) {
3897
3898 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3899
3900 switch ($rule["type"]) {
3901 case "title":
3902 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3903 $rule['reg_exp'] . "')";
3904 break;
3905 case "content":
3906 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3907 $rule['reg_exp'] . "')";
3908 break;
3909 case "both":
3910 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3911 $rule['reg_exp'] . "') OR LOWER(" .
3912 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3913 break;
3914 case "tag":
3915 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3916 $rule['reg_exp'] . "')";
3917 break;
3918 case "link":
3919 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3920 $rule['reg_exp'] . "')";
3921 break;
3922 case "author":
3923 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3924 $rule['reg_exp'] . "')";
3925 break;
3926 }
3927
3928 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3929
3930 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3931 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3932 }
3933
3934 if (isset($rule["cat_id"])) {
3935
3936 if ($rule["cat_id"] > 0) {
3937 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3938 array_push($children, $rule["cat_id"]);
3939
3940 $children = join(",", $children);
3941
3942 $cat_qpart = "cat_id IN ($children)";
3943 } else {
3944 $cat_qpart = "cat_id IS NULL";
3945 }
3946
3947 $qpart .= " AND $cat_qpart";
3948 }
3949
3950 array_push($query, "($qpart)");
3951
3952 }
3953 }
3954
3955 if (count($query) > 0) {
3956 $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
3957 } else {
3958 $fullquery = "(false)";
3959 }
3960
3961 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3962
3963 return $fullquery;
3964 }
3965
3966 if (!function_exists('gzdecode')) {
3967 function gzdecode($string) { // no support for 2nd argument
3968 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3969 base64_encode($string));
3970 }
3971 }
3972
3973 function get_random_bytes($length) {
3974 if (function_exists('openssl_random_pseudo_bytes')) {
3975 return openssl_random_pseudo_bytes($length);
3976 } else {
3977 $output = "";
3978
3979 for ($i = 0; $i < $length; $i++)
3980 $output .= chr(mt_rand(0, 255));
3981
3982 return $output;
3983 }
3984 }
3985
3986 function read_stdin() {
3987 $fp = fopen("php://stdin", "r");
3988
3989 if ($fp) {
3990 $line = trim(fgets($fp));
3991 fclose($fp);
3992 return $line;
3993 }
3994
3995 return null;
3996 }
3997
3998 function tmpdirname($path, $prefix) {
3999 // Use PHP's tmpfile function to create a temporary
4000 // directory name. Delete the file and keep the name.
4001 $tempname = tempnam($path,$prefix);
4002 if (!$tempname)
4003 return false;
4004
4005 if (!unlink($tempname))
4006 return false;
4007
4008 return $tempname;
4009 }
4010
4011 function getFeedCategory($link, $feed) {
4012 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
4013 WHERE id = '$feed'");
4014
4015 if (db_num_rows($result) > 0) {
4016 return db_fetch_result($result, 0, "cat_id");
4017 } else {
4018 return false;
4019 }
4020
4021 }
4022
4023 function implements_interface($class, $interface) {
4024 return in_array($interface, class_implements($class));
4025 }
4026
4027 function geturl($url){
4028
4029 (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');
4030
4031 $curl = curl_init();
4032 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4033 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4034 $header[] = "Cache-Control: max-age=0";
4035 $header[] = "Connection: keep-alive";
4036 $header[] = "Keep-Alive: 300";
4037 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4038 $header[] = "Accept-Language: en-us,en;q=0.5";
4039 $header[] = "Pragma: ";
4040
4041 curl_setopt($curl, CURLOPT_URL, $url);
4042 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4043 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
4044 curl_setopt($curl, CURLOPT_HEADER, true);
4045 curl_setopt($curl, CURLOPT_REFERER, $url);
4046 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
4047 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
4048 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
4049 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4050 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
4051
4052 $html = curl_exec($curl);
4053
4054 $status = curl_getinfo($curl);
4055 curl_close($curl);
4056
4057 if($status['http_code']!=200){
4058 if($status['http_code'] == 301 || $status['http_code'] == 302) {
4059 list($header) = explode("\r\n\r\n", $html, 2);
4060 $matches = array();
4061 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4062 $url = trim(str_replace($matches[1],"",$matches[0]));
4063 $url_parsed = parse_url($url);
4064 return (isset($url_parsed))? geturl($url, $referer):'';
4065 }
4066 $oline='';
4067 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4068 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4069 # $handle = @fopen('./curl.error.log', 'a');
4070 # fwrite($handle, $line);
4071 return FALSE;
4072 }
4073 return $url;
4074 }
4075
4076 function get_minified_js($files) {
4077 require_once 'lib/jshrink/Minifier.php';
4078
4079 $rv = '';
4080
4081 foreach ($files as $js) {
4082 if (!isset($_GET['debug'])) {
4083 $cached_file = CACHE_DIR . "/js/$js.js";
4084
4085 if (file_exists($cached_file) &&
4086 is_readable($cached_file) &&
4087 filemtime($cached_file) >= filemtime("js/$js.js")) {
4088
4089 $rv .= file_get_contents($cached_file);
4090
4091 } else {
4092 $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
4093 file_put_contents($cached_file, $minified);
4094 $rv .= $minified;
4095 }
4096 } else {
4097 $rv .= file_get_contents("js/$js.js");
4098 }
4099 }
4100
4101 return $rv;
4102 }
4103
4104 function stylesheet_tag($filename) {
4105 $timestamp = filemtime($filename);
4106
4107 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4108 }
4109
4110 function javascript_tag($filename) {
4111 $query = "";
4112
4113 if (!(strpos($filename, "?") === FALSE)) {
4114 $query = substr($filename, strpos($filename, "?")+1);
4115 $filename = substr($filename, 0, strpos($filename, "?"));
4116 }
4117
4118 $timestamp = filemtime($filename);
4119
4120 if ($query) $timestamp .= "&$query";
4121
4122 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4123 }
4124
4125 function calculate_dep_timestamp() {
4126 $files = array_merge(glob("js/*.js"), glob("*.css"));
4127
4128 $max_ts = -1;
4129
4130 foreach ($files as $file) {
4131 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4132 }
4133
4134 return $max_ts;
4135 }
4136
4137 function T_js_decl($s1, $s2) {
4138 if ($s1 && $s2) {
4139 $s1 = preg_replace("/\n/", "", $s1);
4140 $s2 = preg_replace("/\n/", "", $s2);
4141
4142 $s1 = preg_replace("/\"/", "\\\"", $s1);
4143 $s2 = preg_replace("/\"/", "\\\"", $s2);
4144
4145 return "T_messages[\"$s1\"] = \"$s2\";\n";
4146 }
4147 }
4148
4149 function init_js_translations() {
4150
4151 print 'var T_messages = new Object();
4152
4153 function __(msg) {
4154 if (T_messages[msg]) {
4155 return T_messages[msg];
4156 } else {
4157 return msg;
4158 }
4159 }
4160
4161 function ngettext(msg1, msg2, n) {
4162 return (parseInt(n) > 1) ? msg2 : msg1;
4163 }';
4164
4165 $l10n = _get_reader();
4166
4167 for ($i = 0; $i < $l10n->total; $i++) {
4168 $orig = $l10n->get_original_string($i);
4169 $translation = __($orig);
4170
4171 print T_js_decl($orig, $translation);
4172 }
4173 }
4174
4175 function label_to_feed_id($label) {
4176 return LABEL_BASE_INDEX - 1 - abs($label);
4177 }
4178
4179 function feed_to_label_id($feed) {
4180 return LABEL_BASE_INDEX - 1 + abs($feed);
4181 }
4182
4183 ?>