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