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