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