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