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