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