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