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