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