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