]> git.wh0rd.org Git - tt-rss.git/blob - include/functions2.php
make_init_params: add plugins
[tt-rss.git] / include / functions2.php
1 <?php
2         function make_init_params() {
3                 $params = array();
4
5                 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
6                         "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
7                         "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
8                         "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
9
10                                  $params[strtolower($param)] = (int) get_pref($param);
11                  }
12
13                 $params["icons_url"] = ICONS_URL;
14                 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
15                 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
16                 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
17                 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
18                 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
19                 $params["label_base_index"] = (int) LABEL_BASE_INDEX;
20                 $params["theme"] = get_pref("USER_CSS_THEME", false, false);
21                 $params["plugins"] = implode(", ", PluginHost::getInstance()->get_plugin_names());
22
23                 $params["php_platform"] = PHP_OS;
24                 $params["php_version"] = PHP_VERSION;
25
26                 $params["sanity_checksum"] = sha1(file_get_contents("include/sanity_check.php"));
27
28                 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
29                         ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
30
31                 $max_feed_id = db_fetch_result($result, 0, "mid");
32                 $num_feeds = db_fetch_result($result, 0, "nf");
33
34                 $params["max_feed_id"] = (int) $max_feed_id;
35                 $params["num_feeds"] = (int) $num_feeds;
36
37                 $params["hotkeys"] = get_hotkeys_map();
38
39                 $params["csrf_token"] = $_SESSION["csrf_token"];
40                 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
41
42                 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
43
44                 return $params;
45         }
46
47         function get_hotkeys_info() {
48                 $hotkeys = array(
49                         __("Navigation") => array(
50                                 "next_feed" => __("Open next feed"),
51                                 "prev_feed" => __("Open previous feed"),
52                                 "next_article" => __("Open next article"),
53                                 "prev_article" => __("Open previous article"),
54                                 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
55                                 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
56                                 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
57                                 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
58                                 "search_dialog" => __("Show search dialog")),
59                         __("Article") => array(
60                                 "toggle_mark" => __("Toggle starred"),
61                                 "toggle_publ" => __("Toggle published"),
62                                 "toggle_unread" => __("Toggle unread"),
63                                 "edit_tags" => __("Edit tags"),
64                                 "dismiss_selected" => __("Dismiss selected"),
65                                 "dismiss_read" => __("Dismiss read"),
66                                 "open_in_new_window" => __("Open in new window"),
67                                 "catchup_below" => __("Mark below as read"),
68                                 "catchup_above" => __("Mark above as read"),
69                                 "article_scroll_down" => __("Scroll down"),
70                                 "article_scroll_up" => __("Scroll up"),
71                                 "select_article_cursor" => __("Select article under cursor"),
72                                 "email_article" => __("Email article"),
73                                 "close_article" => __("Close/collapse article"),
74                                 "toggle_expand" => __("Toggle article expansion (combined mode)"),
75                                 "toggle_widescreen" => __("Toggle widescreen mode"),
76                                 "toggle_embed_original" => __("Toggle embed original")),
77                         __("Article selection") => array(
78                                 "select_all" => __("Select all articles"),
79                                 "select_unread" => __("Select unread"),
80                                 "select_marked" => __("Select starred"),
81                                 "select_published" => __("Select published"),
82                                 "select_invert" => __("Invert selection"),
83                                 "select_none" => __("Deselect everything")),
84                         __("Feed") => array(
85                                 "feed_refresh" => __("Refresh current feed"),
86                                 "feed_unhide_read" => __("Un/hide read feeds"),
87                                 "feed_subscribe" => __("Subscribe to feed"),
88                                 "feed_edit" => __("Edit feed"),
89                                 "feed_catchup" => __("Mark as read"),
90                                 "feed_reverse" => __("Reverse headlines"),
91                                 "feed_debug_update" => __("Debug feed update"),
92                                 "catchup_all" => __("Mark all feeds as read"),
93                                 "cat_toggle_collapse" => __("Un/collapse current category"),
94                                 "toggle_combined_mode" => __("Toggle combined mode"),
95                                 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
96                         __("Go to") => array(
97                                 "goto_all" => __("All articles"),
98                                 "goto_fresh" => __("Fresh"),
99                                 "goto_marked" => __("Starred"),
100                                 "goto_published" => __("Published"),
101                                 "goto_tagcloud" => __("Tag cloud"),
102                                 "goto_prefs" => __("Preferences")),
103                         __("Other") => array(
104                                 "create_label" => __("Create label"),
105                                 "create_filter" => __("Create filter"),
106                                 "collapse_sidebar" => __("Un/collapse sidebar"),
107                                 "help_dialog" => __("Show help dialog"))
108                         );
109
110                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_INFO) as $plugin) {
111                         $hotkeys = $plugin->hook_hotkey_info($hotkeys);
112                 }
113
114                 return $hotkeys;
115         }
116
117         function get_hotkeys_map() {
118                 $hotkeys = array(
119 //                      "navigation" => array(
120                                 "k" => "next_feed",
121                                 "j" => "prev_feed",
122                                 "n" => "next_article",
123                                 "p" => "prev_article",
124                                 "(38)|up" => "prev_article",
125                                 "(40)|down" => "next_article",
126 //                              "^(38)|Ctrl-up" => "prev_article_noscroll",
127 //                              "^(40)|Ctrl-down" => "next_article_noscroll",
128                                 "(191)|/" => "search_dialog",
129 //                      "article" => array(
130                                 "s" => "toggle_mark",
131                                 "*s" => "toggle_publ",
132                                 "u" => "toggle_unread",
133                                 "*t" => "edit_tags",
134                                 "*d" => "dismiss_selected",
135                                 "*x" => "dismiss_read",
136                                 "o" => "open_in_new_window",
137                                 "c p" => "catchup_below",
138                                 "c n" => "catchup_above",
139                                 "*n" => "article_scroll_down",
140                                 "*p" => "article_scroll_up",
141                                 "*(38)|Shift+up" => "article_scroll_up",
142                                 "*(40)|Shift+down" => "article_scroll_down",
143                                 "a *w" => "toggle_widescreen",
144                                 "a e" => "toggle_embed_original",
145                                 "e" => "email_article",
146                                 "a q" => "close_article",
147 //                      "article_selection" => array(
148                                 "a a" => "select_all",
149                                 "a u" => "select_unread",
150                                 "a *u" => "select_marked",
151                                 "a p" => "select_published",
152                                 "a i" => "select_invert",
153                                 "a n" => "select_none",
154 //                      "feed" => array(
155                                 "f r" => "feed_refresh",
156                                 "f a" => "feed_unhide_read",
157                                 "f s" => "feed_subscribe",
158                                 "f e" => "feed_edit",
159                                 "f q" => "feed_catchup",
160                                 "f x" => "feed_reverse",
161                                 "f *d" => "feed_debug_update",
162                                 "f *c" => "toggle_combined_mode",
163                                 "f c" => "toggle_cdm_expanded",
164                                 "*q" => "catchup_all",
165                                 "x" => "cat_toggle_collapse",
166 //                      "goto" => array(
167                                 "g a" => "goto_all",
168                                 "g f" => "goto_fresh",
169                                 "g s" => "goto_marked",
170                                 "g p" => "goto_published",
171                                 "g t" => "goto_tagcloud",
172                                 "g *p" => "goto_prefs",
173 //                      "other" => array(
174                                 "(9)|Tab" => "select_article_cursor", // tab
175                                 "c l" => "create_label",
176                                 "c f" => "create_filter",
177                                 "c s" => "collapse_sidebar",
178                                 "^(191)|Ctrl+/" => "help_dialog",
179                         );
180
181                 if (get_pref('COMBINED_DISPLAY_MODE')) {
182                         $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
183                         $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
184                 }
185
186                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_MAP) as $plugin) {
187                         $hotkeys = $plugin->hook_hotkey_map($hotkeys);
188                 }
189
190                 $prefixes = array();
191
192                 foreach (array_keys($hotkeys) as $hotkey) {
193                         $pair = explode(" ", $hotkey, 2);
194
195                         if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
196                                 array_push($prefixes, $pair[0]);
197                         }
198                 }
199
200                 return array($prefixes, $hotkeys);
201         }
202
203         function make_runtime_info() {
204                 $data = array();
205
206                 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
207                         ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
208
209                 $max_feed_id = db_fetch_result($result, 0, "mid");
210                 $num_feeds = db_fetch_result($result, 0, "nf");
211
212                 $data["max_feed_id"] = (int) $max_feed_id;
213                 $data["num_feeds"] = (int) $num_feeds;
214
215                 $data['last_article_id'] = getLastArticleId();
216                 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
217
218                 $data['dep_ts'] = calculate_dep_timestamp();
219                 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
220
221                 if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
222
223                         $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
224
225                         if (time() - $_SESSION["daemon_stamp_check"] > 30) {
226
227                                 $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
228
229                                 if ($stamp) {
230                                         $stamp_delta = time() - $stamp;
231
232                                         if ($stamp_delta > 1800) {
233                                                 $stamp_check = 0;
234                                         } else {
235                                                 $stamp_check = 1;
236                                                 $_SESSION["daemon_stamp_check"] = time();
237                                         }
238
239                                         $data['daemon_stamp_ok'] = $stamp_check;
240
241                                         $stamp_fmt = date("Y.m.d, G:i", $stamp);
242
243                                         $data['daemon_stamp'] = $stamp_fmt;
244                                 }
245                         }
246                 }
247
248                 if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
249                                 $new_version_details = @check_for_update();
250
251                                 $data['new_version_available'] = (int) ($new_version_details != false);
252
253                                 $_SESSION["last_version_check"] = time();
254                                 $_SESSION["version_data"] = $new_version_details;
255                 }
256
257                 return $data;
258         }
259
260         function search_to_sql($search) {
261
262                 $search_query_part = "";
263
264                 $keywords = str_getcsv($search, " ");
265                 $query_keywords = array();
266                 $search_words = array();
267
268                 foreach ($keywords as $k) {
269                         if (strpos($k, "-") === 0) {
270                                 $k = substr($k, 1);
271                                 $not = "NOT";
272                         } else {
273                                 $not = "";
274                         }
275
276                         $commandpair = explode(":", mb_strtolower($k), 2);
277
278                         switch ($commandpair[0]) {
279                         case "title":
280                                 if ($commandpair[1]) {
281                                         array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
282                                                 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
283                                 } else {
284                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
285                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
286                                         array_push($search_words, $k);
287                                 }
288                                 break;
289                         case "author":
290                                 if ($commandpair[1]) {
291                                         array_push($query_keywords, "($not (LOWER(author) LIKE '%".
292                                                 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
293                                 } else {
294                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
295                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
296                                         array_push($search_words, $k);
297                                 }
298                                 break;
299                         case "note":
300                                 if ($commandpair[1]) {
301                                         if ($commandpair[1] == "true")
302                                                 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
303                                         else if ($commandpair[1] == "false")
304                                                 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
305                                         else
306                                                 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
307                                                         db_escape_string(mb_strtolower($commandpair[1]))."%'))");
308                                 } else {
309                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
310                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
311                                         if (!$not) array_push($search_words, $k);
312                                 }
313                                 break;
314                         case "star":
315
316                                 if ($commandpair[1]) {
317                                         if ($commandpair[1] == "true")
318                                                 array_push($query_keywords, "($not (marked = true))");
319                                         else
320                                                 array_push($query_keywords, "($not (marked = false))");
321                                 } else {
322                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
323                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
324                                         if (!$not) array_push($search_words, $k);
325                                 }
326                                 break;
327                         case "pub":
328                                 if ($commandpair[1]) {
329                                         if ($commandpair[1] == "true")
330                                                 array_push($query_keywords, "($not (published = true))");
331                                         else
332                                                 array_push($query_keywords, "($not (published = false))");
333
334                                 } else {
335                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
336                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
337                                         if (!$not) array_push($search_words, $k);
338                                 }
339                                 break;
340                         default:
341                                 if (strpos($k, "@") === 0) {
342
343                                         $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
344                                         $orig_ts = strtotime(substr($k, 1));
345                                         $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
346
347                                         //$k = date("Y-m-d", strtotime(substr($k, 1)));
348
349                                         array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
350                                 } else {
351                                         array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
352                                                         OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
353
354                                         if (!$not) array_push($search_words, $k);
355                                 }
356                         }
357                 }
358
359                 $search_query_part = implode("AND", $query_keywords);
360
361                 return array($search_query_part, $search_words);
362         }
363
364         function getParentCategories($cat, $owner_uid) {
365                 $rv = array();
366
367                 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
368                         WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
369
370                 while ($line = db_fetch_assoc($result)) {
371                         array_push($rv, $line["parent_cat"]);
372                         $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
373                 }
374
375                 return $rv;
376         }
377
378         function getChildCategories($cat, $owner_uid) {
379                 $rv = array();
380
381                 $result = db_query("SELECT id FROM ttrss_feed_categories
382                         WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
383
384                 while ($line = db_fetch_assoc($result)) {
385                         array_push($rv, $line["id"]);
386                         $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
387                 }
388
389                 return $rv;
390         }
391
392         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, $override_strategy = false, $override_vfeed = false, $start_ts = false) {
393
394                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
395
396                 $ext_tables_part = "";
397                 $search_words = array();
398
399                         if ($search) {
400
401                                 if (SPHINX_ENABLED) {
402                                         $ids = join(",", @sphinx_search($search, 0, 500));
403
404                                         if ($ids)
405                                                 $search_query_part = "ref_id IN ($ids) AND ";
406                                         else
407                                                 $search_query_part = "ref_id = -1 AND ";
408
409                                 } else {
410                                         list($search_query_part, $search_words) = search_to_sql($search);
411                                         $search_query_part .= " AND ";
412                                 }
413
414                         } else {
415                                 $search_query_part = "";
416                         }
417
418                         if ($filter) {
419
420                                 if (DB_TYPE == "pgsql") {
421                                         $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
422                                 } else {
423                                         $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
424                                 }
425
426                                 $override_order = "updated DESC";
427
428                                 $filter_query_part = filter_to_sql($filter, $owner_uid);
429
430                                 // Try to check if SQL regexp implementation chokes on a valid regexp
431
432
433                                 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
434                                         ttrss_user_entries, ttrss_feeds
435                                         WHERE $filter_query_part LIMIT 1", false);
436
437                                 if ($result) {
438                                         $test = db_fetch_result($result, 0, "true_val");
439
440                                         if (!$test) {
441                                                 $filter_query_part = "false AND";
442                                         } else {
443                                                 $filter_query_part .= " AND";
444                                         }
445                                 } else {
446                                         $filter_query_part = "false AND";
447                                 }
448
449                         } else {
450                                 $filter_query_part = "";
451                         }
452
453                         if ($since_id) {
454                                 $since_id_part = "ttrss_entries.id > $since_id AND ";
455                         } else {
456                                 $since_id_part = "";
457                         }
458
459                         $view_query_part = "";
460
461                         if ($view_mode == "adaptive") {
462                                 if ($search) {
463                                         $view_query_part = " ";
464                                 } else if ($feed != -1) {
465
466                                         $unread = getFeedUnread($feed, $cat_view);
467
468                                         if ($cat_view && $feed > 0 && $include_children)
469                                                 $unread += getCategoryChildrenUnread($feed);
470
471                                         if ($unread > 0)
472                                 $view_query_part = " unread = true AND ";
473
474                                 }
475                         }
476
477                         if ($view_mode == "marked") {
478                                 $view_query_part = " marked = true AND ";
479                         }
480
481                         if ($view_mode == "has_note") {
482                                 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
483                         }
484
485                         if ($view_mode == "published") {
486                                 $view_query_part = " published = true AND ";
487                         }
488
489                         if ($view_mode == "unread" && $feed != -6) {
490                                 $view_query_part = " unread = true AND ";
491                         }
492
493                         if ($limit > 0) {
494                                 $limit_query_part = "LIMIT " . $limit;
495                         }
496
497                         $allow_archived = false;
498
499                         $vfeed_query_part = "";
500
501                         // override query strategy and enable feed display when searching globally
502                         if ($search && $search_mode == "all_feeds") {
503                                 $query_strategy_part = "true";
504                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
505                         /* tags */
506                         } else if (!is_numeric($feed)) {
507                                 $query_strategy_part = "true";
508                                 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
509                                         id = feed_id) as feed_title,";
510                         } else if ($search && $search_mode == "this_cat") {
511                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
512
513                                 if ($feed > 0) {
514                                         if ($include_children) {
515                                                 $subcats = getChildCategories($feed, $owner_uid);
516                                                 array_push($subcats, $feed);
517                                                 $cats_qpart = join(",", $subcats);
518                                         } else {
519                                                 $cats_qpart = $feed;
520                                         }
521
522                                         $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
523
524                                 } else {
525                                         $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
526                                 }
527
528                         } else if ($feed > 0) {
529
530                                 if ($cat_view) {
531
532                                         if ($feed > 0) {
533                                                 if ($include_children) {
534                                                         # sub-cats
535                                                         $subcats = getChildCategories($feed, $owner_uid);
536
537                                                         array_push($subcats, $feed);
538                                                         $query_strategy_part = "cat_id IN (".
539                                                                         implode(",", $subcats).")";
540
541                                                 } else {
542                                                         $query_strategy_part = "cat_id = '$feed'";
543                                                 }
544
545                                         } else {
546                                                 $query_strategy_part = "cat_id IS NULL";
547                                         }
548
549                                         $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
550
551                                 } else {
552                                         $query_strategy_part = "feed_id = '$feed'";
553                                 }
554                         } else if ($feed == 0 && !$cat_view) { // archive virtual feed
555                                 $query_strategy_part = "feed_id IS NULL";
556                                 $allow_archived = true;
557                         } else if ($feed == 0 && $cat_view) { // uncategorized
558                                 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
559                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
560                         } else if ($feed == -1) { // starred virtual feed
561                                 $query_strategy_part = "marked = true";
562                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
563                                 $allow_archived = true;
564
565                                 if (!$override_order) {
566                                         $override_order = "last_marked DESC, date_entered DESC, updated DESC";
567                                 }
568
569                         } else if ($feed == -2) { // published virtual feed OR labels category
570
571                                 if (!$cat_view) {
572                                         $query_strategy_part = "published = true";
573                                         $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
574                                         $allow_archived = true;
575
576                                         if (!$override_order) {
577                                                 $override_order = "last_published DESC, date_entered DESC, updated DESC";
578                                         }
579
580                                 } else {
581                                         $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
582
583                                         $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
584
585                                         $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
586                                                 ttrss_user_labels2.article_id = ref_id";
587
588                                 }
589                         } else if ($feed == -6) { // recently read
590                                 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
591                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
592                                 $allow_archived = true;
593                                 $ignore_vfeed_group = true;
594
595                                 if (!$override_order) $override_order = "last_read DESC";
596
597 /*                      } else if ($feed == -7) { // shared
598                                 $query_strategy_part = "uuid != ''";
599                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
600                                 $allow_archived = true; */
601                         } else if ($feed == -3) { // fresh virtual feed
602                                 $query_strategy_part = "unread = true AND score >= 0";
603
604                                 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
605
606                                 if (DB_TYPE == "pgsql") {
607                                         $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
608                                 } else {
609                                         $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
610                                 }
611
612                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
613                         } else if ($feed == -4) { // all articles virtual feed
614                                 $allow_archived = true;
615                                 $query_strategy_part = "true";
616                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
617                         } else if ($feed <= LABEL_BASE_INDEX) { // labels
618                                 $label_id = feed_to_label_id($feed);
619
620                                 $query_strategy_part = "label_id = '$label_id' AND
621                                         ttrss_labels2.id = ttrss_user_labels2.label_id AND
622                                         ttrss_user_labels2.article_id = ref_id";
623
624                                 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
625                                 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
626                                 $allow_archived = true;
627
628                         } else {
629                                 $query_strategy_part = "true";
630                         }
631
632                         $order_by = "score DESC, date_entered DESC, updated DESC";
633
634                         if ($view_mode == "unread_first") {
635                                 $order_by = "unread DESC, $order_by";
636                         }
637
638                         if ($override_order) {
639                                 $order_by = $override_order;
640                         }
641
642                         if ($override_strategy) {
643                                 $query_strategy_part = $override_strategy;
644                         }
645
646                         if ($override_vfeed) {
647                                 $vfeed_query_part = $override_vfeed;
648                         }
649
650                         $feed_title = "";
651
652                         if ($search) {
653                                 $feed_title = T_sprintf("Search results: %s", $search);
654                         } else {
655                                 if ($cat_view) {
656                                         $feed_title = getCategoryTitle($feed);
657                                 } else {
658                                         if (is_numeric($feed) && $feed > 0) {
659                                                 $result = db_query("SELECT title,site_url,last_error,last_updated
660                                                         FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
661
662                                                 $feed_title = db_fetch_result($result, 0, "title");
663                                                 $feed_site_url = db_fetch_result($result, 0, "site_url");
664                                                 $last_error = db_fetch_result($result, 0, "last_error");
665                                                 $last_updated = db_fetch_result($result, 0, "last_updated");
666                                         } else {
667                                                 $feed_title = getFeedTitle($feed);
668                                         }
669                                 }
670                         }
671
672
673                         $content_query_part = "content, ";
674
675
676                         if (is_numeric($feed)) {
677
678                                 if ($feed >= 0) {
679                                         $feed_kind = "Feeds";
680                                 } else {
681                                         $feed_kind = "Labels";
682                                 }
683
684                                 if ($limit_query_part) {
685                                         $offset_query_part = "OFFSET $offset";
686                                 }
687
688                                 // proper override_order applied above
689                                 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
690                                         if (!$override_order) {
691                                                 $order_by = "ttrss_feeds.title, $order_by";
692                                         } else {
693                                                 $order_by = "ttrss_feeds.title, $override_order";
694                                         }
695                                 }
696
697                                 if (!$allow_archived) {
698                                         $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
699                                         $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
700
701                                 } else {
702                                         $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
703                                                 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
704                                 }
705
706                                 if ($vfeed_query_part)
707                                         $vfeed_query_part .= "favicon_avg_color,";
708
709                                 if ($start_ts) {
710                                         $start_ts_formatted = date("Y/m/d H:i:s", strtotime($start_ts));
711                                         $start_ts_query_part = "date_entered >= '$start_ts_formatted' AND";
712                                 } else {
713                                         $start_ts_query_part = "";
714                                 }
715
716                                 $query = "SELECT DISTINCT
717                                                 date_entered,
718                                                 guid,
719                                                 ttrss_entries.id,ttrss_entries.title,
720                                                 updated,
721                                                 label_cache,
722                                                 tag_cache,
723                                                 always_display_enclosures,
724                                                 site_url,
725                                                 note,
726                                                 num_comments,
727                                                 comments,
728                                                 int_id,
729                                                 uuid,
730                                                 lang,
731                                                 hide_images,
732                                                 unread,feed_id,marked,published,link,last_read,orig_feed_id,
733                                                 last_marked, last_published,
734                                                 $vfeed_query_part
735                                                 $content_query_part
736                                                 author,score
737                                         FROM
738                                                 $from_qpart
739                                         WHERE
740                                         $feed_check_qpart
741                                         ttrss_user_entries.ref_id = ttrss_entries.id AND
742                                         ttrss_user_entries.owner_uid = '$owner_uid' AND
743                                         $search_query_part
744                                         $start_ts_query_part
745                                         $filter_query_part
746                                         $view_query_part
747                                         $since_id_part
748                                         $query_strategy_part ORDER BY $order_by
749                                         $limit_query_part $offset_query_part";
750
751                                 if ($_REQUEST["debug"]) print $query;
752
753                                 $result = db_query($query);
754
755                         } else {
756                                 // browsing by tag
757
758                                 $select_qpart = "SELECT DISTINCT " .
759                                                                 "date_entered," .
760                                                                 "guid," .
761                                                                 "note," .
762                                                                 "ttrss_entries.id as id," .
763                                                                 "title," .
764                                                                 "updated," .
765                                                                 "unread," .
766                                                                 "feed_id," .
767                                                                 "orig_feed_id," .
768                                                                 "marked," .
769                                                                 "num_comments, " .
770                                                                 "comments, " .
771                                                                 "tag_cache," .
772                                                                 "label_cache," .
773                                                                 "link," .
774                                                                 "lang," .
775                                                                 "uuid," .
776                                                                 "last_read," .
777                                                                 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
778                                                                 "last_marked, last_published, " .
779                                                                 $since_id_part .
780                                                                 $vfeed_query_part .
781                                                                 $content_query_part .
782                                                                 "score ";
783
784                                 $feed_kind = "Tags";
785                                 $all_tags = explode(",", $feed);
786                                 if ($search_mode == 'any') {
787                                         $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
788                                         $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
789                                         $where_qpart = " WHERE " .
790                                                                    "ref_id = ttrss_entries.id AND " .
791                                                                    "ttrss_user_entries.owner_uid = $owner_uid AND " .
792                                                                    "post_int_id = int_id AND $tag_sql AND " .
793                                                                    $view_query_part .
794                                                                    $search_query_part .
795                                                                    $query_strategy_part . " ORDER BY $order_by " .
796                                                                    $limit_query_part;
797
798                                 } else {
799                                         $i = 1;
800                                         $sub_selects = array();
801                                         $sub_ands = array();
802                                         foreach ($all_tags as $term) {
803                                                 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");
804                                                 $i++;
805                                         }
806                                         if ($i > 2) {
807                                                 $x = 1;
808                                                 $y = 2;
809                                                 do {
810                                                         array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
811                                                         $x++;
812                                                         $y++;
813                                                 } while ($y < $i);
814                                         }
815                                         array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
816                                         array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
817                                         $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
818                                         $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
819                                 }
820                                 //                              error_log("TAG SQL: " . $tag_sql);
821                                 // $tag_sql = "tag_name = '$feed'";   DEFAULT way
822
823                                 //                              error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
824                                 $result = db_query($select_qpart . $from_qpart . $where_qpart);
825                         }
826
827                         return array($result, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words);
828
829         }
830
831         function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
832                 if (!$owner) $owner = $_SESSION["uid"];
833
834                 $res = trim($str); if (!$res) return '';
835
836                 $charset_hack = '<head>
837                         <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
838                 </head>';
839
840                 $res = trim($res); if (!$res) return '';
841
842                 libxml_use_internal_errors(true);
843
844                 $doc = new DOMDocument();
845                 $doc->loadHTML($charset_hack . $res);
846                 $xpath = new DOMXPath($doc);
847
848                 $entries = $xpath->query('(//a[@href]|//img[@src])');
849
850                 foreach ($entries as $entry) {
851
852                         if ($site_url) {
853
854                                 if ($entry->hasAttribute('href')) {
855                                         $entry->setAttribute('href',
856                                                 rewrite_relative_url($site_url, $entry->getAttribute('href')));
857
858                                         $entry->setAttribute('rel', 'noreferrer');
859                                 }
860
861                                 if ($entry->hasAttribute('src')) {
862                                         $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
863
864                                         $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
865
866                                         if (file_exists($cached_filename)) {
867                                                 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
868                                         }
869
870                                         $entry->setAttribute('src', $src);
871                                 }
872
873                                 if ($entry->nodeName == 'img') {
874                                         if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
875                                                         $force_remove_images || $_SESSION["bw_limit"]) {
876
877                                                 $p = $doc->createElement('p');
878
879                                                 $a = $doc->createElement('a');
880                                                 $a->setAttribute('href', $entry->getAttribute('src'));
881
882                                                 $a->appendChild(new DOMText($entry->getAttribute('src')));
883                                                 $a->setAttribute('target', '_blank');
884
885                                                 $p->appendChild($a);
886
887                                                 $entry->parentNode->replaceChild($p, $entry);
888                                         }
889                                 }
890                         }
891
892                         if (strtolower($entry->nodeName) == "a") {
893                                 $entry->setAttribute("target", "_blank");
894                         }
895                 }
896
897                 $entries = $xpath->query('//iframe');
898                 foreach ($entries as $entry) {
899                         $entry->setAttribute('sandbox', 'allow-scripts');
900
901                 }
902
903                 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
904                         'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
905                         'caption', 'cite', 'center', 'code', 'col', 'colgroup',
906                         'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
907                         'dt', 'em', 'footer', 'figure', 'figcaption',
908                         'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
909                         'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
910                         'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
911                         'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
912                         'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
913                         'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
914
915                 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
916
917                 $disallowed_attributes = array('id', 'style', 'class');
918
919                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) {
920                         $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id);
921                         if (is_array($retval)) {
922                                 $doc = $retval[0];
923                                 $allowed_elements = $retval[1];
924                                 $disallowed_attributes = $retval[2];
925                         } else {
926                                 $doc = $retval;
927                         }
928                 }
929
930                 $doc->removeChild($doc->firstChild); //remove doctype
931                 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
932
933                 if ($highlight_words) {
934                         foreach ($highlight_words as $word) {
935
936                                 // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
937
938                                 $elements = $xpath->query("//*/text()");
939
940                                 foreach ($elements as $child) {
941
942                                         $fragment = $doc->createDocumentFragment();
943                                         $text = $child->textContent;
944
945                                         while (($pos = mb_stripos($text, $word)) !== false) {
946                                                 $fragment->appendChild(new DomText(mb_substr($text, 0, $pos)));
947                                                 $word = mb_substr($text, $pos, mb_strlen($word));
948                                                 $highlight = $doc->createElement('span');
949                                                 $highlight->appendChild(new DomText($word));
950                                                 $highlight->setAttribute('class', 'highlight');
951                                                 $fragment->appendChild($highlight);
952                                                 $text = mb_substr($text, $pos + mb_strlen($word));
953                                         }
954
955                                         if (!empty($text)) $fragment->appendChild(new DomText($text));
956
957                                         $child->parentNode->replaceChild($fragment, $child);
958                                 }
959                         }
960                 }
961
962                 $res = $doc->saveHTML();
963
964                 return $res;
965         }
966
967         function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
968                 $xpath = new DOMXPath($doc);
969                 $entries = $xpath->query('//*');
970
971                 foreach ($entries as $entry) {
972                         if (!in_array($entry->nodeName, $allowed_elements)) {
973                                 $entry->parentNode->removeChild($entry);
974                         }
975
976                         if ($entry->hasAttributes()) {
977                                 $attrs_to_remove = array();
978
979                                 foreach ($entry->attributes as $attr) {
980
981                                         if (strpos($attr->nodeName, 'on') === 0) {
982                                                 array_push($attrs_to_remove, $attr);
983                                         }
984
985                                         if (in_array($attr->nodeName, $disallowed_attributes)) {
986                                                 array_push($attrs_to_remove, $attr);
987                                         }
988                                 }
989
990                                 foreach ($attrs_to_remove as $attr) {
991                                         $entry->removeAttributeNode($attr);
992                                 }
993                         }
994                 }
995
996                 return $doc;
997         }
998
999         function check_for_update() {
1000                 if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
1001                         $version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
1002                                 "&iid=" . sha1(SELF_URL_PATH);
1003
1004                         $version_data = @fetch_file_contents($version_url);
1005
1006                         if ($version_data) {
1007                                 $version_data = json_decode($version_data, true);
1008                                 if ($version_data && $version_data['version']) {
1009                                         if (version_compare(VERSION_STATIC, $version_data['version']) == -1) {
1010                                                 return $version_data;
1011                                         }
1012                                 }
1013                         }
1014                 }
1015                 return false;
1016         }
1017
1018         function catchupArticlesById($ids, $cmode, $owner_uid = false) {
1019
1020                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1021                 if (count($ids) == 0) return;
1022
1023                 $tmp_ids = array();
1024
1025                 foreach ($ids as $id) {
1026                         array_push($tmp_ids, "ref_id = '$id'");
1027                 }
1028
1029                 $ids_qpart = join(" OR ", $tmp_ids);
1030
1031                 if ($cmode == 0) {
1032                         db_query("UPDATE ttrss_user_entries SET
1033                         unread = false,last_read = NOW()
1034                         WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1035                 } else if ($cmode == 1) {
1036                         db_query("UPDATE ttrss_user_entries SET
1037                         unread = true
1038                         WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1039                 } else {
1040                         db_query("UPDATE ttrss_user_entries SET
1041                         unread = NOT unread,last_read = NOW()
1042                         WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1043                 }
1044
1045                 /* update ccache */
1046
1047                 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
1048                         WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1049
1050                 while ($line = db_fetch_assoc($result)) {
1051                         ccache_update($line["feed_id"], $owner_uid);
1052                 }
1053         }
1054
1055         function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
1056
1057                 $a_id = db_escape_string($id);
1058
1059                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1060
1061                 $query = "SELECT DISTINCT tag_name,
1062                         owner_uid as owner FROM
1063                         ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
1064                         ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
1065
1066                 $tags = array();
1067
1068                 /* check cache first */
1069
1070                 if ($tag_cache === false) {
1071                         $result = db_query("SELECT tag_cache FROM ttrss_user_entries
1072                                 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
1073
1074                         $tag_cache = db_fetch_result($result, 0, "tag_cache");
1075                 }
1076
1077                 if ($tag_cache) {
1078                         $tags = explode(",", $tag_cache);
1079                 } else {
1080
1081                         /* do it the hard way */
1082
1083                         $tmp_result = db_query($query);
1084
1085                         while ($tmp_line = db_fetch_assoc($tmp_result)) {
1086                                 array_push($tags, $tmp_line["tag_name"]);
1087                         }
1088
1089                         /* update the cache */
1090
1091                         $tags_str = db_escape_string(join(",", $tags));
1092
1093                         db_query("UPDATE ttrss_user_entries
1094                                 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
1095                                 AND owner_uid = $owner_uid");
1096                 }
1097
1098                 return $tags;
1099         }
1100
1101         function trim_array($array) {
1102                 $tmp = $array;
1103                 array_walk($tmp, 'trim');
1104                 return $tmp;
1105         }
1106
1107         function tag_is_valid($tag) {
1108                 if ($tag == '') return false;
1109                 if (preg_match("/^[0-9]*$/", $tag)) return false;
1110                 if (mb_strlen($tag) > 250) return false;
1111
1112                 if (!$tag) return false;
1113
1114                 return true;
1115         }
1116
1117         function render_login_form() {
1118                 header('Cache-Control: public');
1119
1120                 require_once "login_form.php";
1121                 exit;
1122         }
1123
1124         function format_warning($msg, $id = "") {
1125                 return "<div class=\"warning\" id=\"$id\">
1126                         <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
1127         }
1128
1129         function format_notice($msg, $id = "") {
1130                 return "<div class=\"notice\" id=\"$id\">
1131                         <span><img src=\"images/information.png\"></span><span>$msg</span></div>";
1132         }
1133
1134         function format_error($msg, $id = "") {
1135                 return "<div class=\"error\" id=\"$id\">
1136                         <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
1137         }
1138
1139         function print_notice($msg) {
1140                 return print format_notice($msg);
1141         }
1142
1143         function print_warning($msg) {
1144                 return print format_warning($msg);
1145         }
1146
1147         function print_error($msg) {
1148                 return print format_error($msg);
1149         }
1150
1151
1152         function T_sprintf() {
1153                 $args = func_get_args();
1154                 return vsprintf(__(array_shift($args)), $args);
1155         }
1156
1157         function format_inline_player($url, $ctype) {
1158
1159                 $entry = "";
1160
1161                 $url = htmlspecialchars($url);
1162
1163                 if (strpos($ctype, "audio/") === 0) {
1164
1165                         if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
1166                                 $_SESSION["hasMp3"])) {
1167
1168                                 $entry .= "<audio preload=\"none\" controls>
1169                                         <source type=\"$ctype\" src=\"$url\"></source>
1170                                         </audio>";
1171
1172                         } else {
1173
1174                                 $entry .= "<object type=\"application/x-shockwave-flash\"
1175                                         data=\"lib/button/musicplayer.swf?song_url=$url\"
1176                                         width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
1177                                         <param name=\"movie\"
1178                                                 value=\"lib/button/musicplayer.swf?song_url=$url\" />
1179                                         </object>";
1180                         }
1181
1182                         if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
1183                                 href=\"$url\">" . basename($url) . "</a>";
1184
1185                         return $entry;
1186
1187                 }
1188
1189                 return "";
1190
1191 /*              $filename = substr($url, strrpos($url, "/")+1);
1192
1193                 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1194                         $filename . " (" . $ctype . ")" . "</a>"; */
1195
1196         }
1197
1198         function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
1199                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1200
1201                 $rv = array();
1202
1203                 $rv['id'] = $id;
1204
1205                 /* we can figure out feed_id from article id anyway, why do we
1206                  * pass feed_id here? let's ignore the argument :(*/
1207
1208                 $result = db_query("SELECT feed_id FROM ttrss_user_entries
1209                         WHERE ref_id = '$id'");
1210
1211                 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
1212
1213                 $rv['feed_id'] = $feed_id;
1214
1215                 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
1216
1217                 if ($mark_as_read) {
1218                         $result = db_query("UPDATE ttrss_user_entries
1219                                 SET unread = false,last_read = NOW()
1220                                 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
1221
1222                         ccache_update($feed_id, $owner_uid);
1223                 }
1224
1225                 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang,
1226                         ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
1227                         (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
1228                         (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
1229                         (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
1230                         (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
1231                         num_comments,
1232                         tag_cache,
1233                         author,
1234                         orig_feed_id,
1235                         note
1236                         FROM ttrss_entries,ttrss_user_entries
1237                         WHERE   id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
1238
1239                 if ($result) {
1240
1241                         $line = db_fetch_assoc($result);
1242
1243                         $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
1244                         unset($line["tag_cache"]);
1245
1246                         $line["content"] = sanitize($line["content"],
1247                                 sql_bool_to_bool($line['hide_images']),
1248                                 $owner_uid, $line["site_url"], false, $line["id"]);
1249
1250                         foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) {
1251                                 $line = $p->hook_render_article($line);
1252                         }
1253
1254                         $num_comments = $line["num_comments"];
1255                         $entry_comments = "";
1256
1257                         if ($num_comments > 0) {
1258                                 if ($line["comments"]) {
1259                                         $comments_url = htmlspecialchars($line["comments"]);
1260                                 } else {
1261                                         $comments_url = htmlspecialchars($line["link"]);
1262                                 }
1263                                 $entry_comments = "<a class=\"postComments\"
1264                                         target='_blank' href=\"$comments_url\">$num_comments ".
1265                                         _ngettext("comment", "comments", $num_comments)."</a>";
1266
1267                         } else {
1268                                 if ($line["comments"] && $line["link"] != $line["comments"]) {
1269                                         $entry_comments = "<a class=\"postComments\" target='_blank' href=\"".htmlspecialchars($line["comments"])."\">".__("comments")."</a>";
1270                                 }
1271                         }
1272
1273                         if ($zoom_mode) {
1274                                 header("Content-Type: text/html");
1275                                 $rv['content'] .= "<html><head>
1276                                                 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
1277                                                 <title>Tiny Tiny RSS - ".$line["title"]."</title>".
1278                                                 stylesheet_tag("css/tt-rss.css").
1279                                                 stylesheet_tag("css/zoom.css").
1280                                                 stylesheet_tag("css/dijit.css")."
1281
1282                                                 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
1283                                                 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
1284
1285                                                 <script type=\"text/javascript\">
1286                                                 function openSelectedAttachment(elem) {
1287                                                         try {
1288                                                                 var url = elem[elem.selectedIndex].value;
1289
1290                                                                 if (url) {
1291                                                                         window.open(url);
1292                                                                         elem.selectedIndex = 0;
1293                                                                 }
1294
1295                                                         } catch (e) {
1296                                                                 exception_error(\"openSelectedAttachment\", e);
1297                                                         }
1298                                                 }
1299                                         </script>
1300                                         </head><body id=\"ttrssZoom\">";
1301                         }
1302
1303                         $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
1304
1305                         $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
1306
1307                         $entry_author = $line["author"];
1308
1309                         if ($entry_author) {
1310                                 $entry_author = __(" - ") . $entry_author;
1311                         }
1312
1313                         $parsed_updated = make_local_datetime($line["updated"], true,
1314                                 $owner_uid, true);
1315
1316                         if (!$zoom_mode)
1317                                 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
1318
1319                         if ($line["link"]) {
1320                                 $rv['content'] .= "<div class='postTitle'><a target='_blank'
1321                                         title=\"".htmlspecialchars($line['title'])."\"
1322                                         href=\"" .
1323                                         htmlspecialchars($line["link"]) . "\">" .
1324                                         $line["title"] . "</a>" .
1325                                         "<span class='author'>$entry_author</span></div>";
1326                         } else {
1327                                 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
1328                         }
1329
1330                         if ($zoom_mode) {
1331                                 $feed_title = "<a href=\"".htmlspecialchars($line["site_url"]).
1332                                         "\" target=\"_blank\">".
1333                                         htmlspecialchars($line["feed_title"])."</a>";
1334
1335                                 $rv['content'] .= "<div class=\"postFeedTitle\">$feed_title</div>";
1336
1337                                 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
1338                         }
1339
1340                         $tags_str = format_tags_string($line["tags"], $id);
1341                         $tags_str_full = join(", ", $line["tags"]);
1342
1343                         if (!$tags_str_full) $tags_str_full = __("no tags");
1344
1345                         if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
1346
1347                         $rv['content'] .= "<div class='postTags' style='float : right'>
1348                                 <img src='images/tag.png'
1349                                 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
1350
1351                         if (!$zoom_mode) {
1352                                 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
1353                                         <a title=\"".__('Edit tags for this article')."\"
1354                                         href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
1355
1356                                 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
1357                                         id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
1358                                         position=\"below\">$tags_str_full</div>";
1359
1360                                 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
1361                                         $rv['content'] .= $p->hook_article_button($line);
1362                                 }
1363
1364                         } else {
1365                                 $tags_str = strip_tags($tags_str);
1366                                 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
1367                         }
1368                         $rv['content'] .= "</div>";
1369                         $rv['content'] .= "<div clear='both'>";
1370
1371                         foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
1372                                 $rv['content'] .= $p->hook_article_left_button($line);
1373                         }
1374
1375                         $rv['content'] .= "$entry_comments</div>";
1376
1377                         if ($line["orig_feed_id"]) {
1378
1379                                 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
1380                                         WHERE id = ".$line["orig_feed_id"]);
1381
1382                                 if (db_num_rows($tmp_result) != 0) {
1383
1384                                         $rv['content'] .= "<div clear='both'>";
1385                                         $rv['content'] .= __("Originally from:");
1386
1387                                         $rv['content'] .= "&nbsp;";
1388
1389                                         $tmp_line = db_fetch_assoc($tmp_result);
1390
1391                                         $rv['content'] .= "<a target='_blank'
1392                                                 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
1393                                                 $tmp_line['title'] . "</a>";
1394
1395                                         $rv['content'] .= "&nbsp;";
1396
1397                                         $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
1398                                         $rv['content'] .= "<img title='".__('Feed URL')."' class='tinyFeedIcon' src='images/pub_set.png'></a>";
1399
1400                                         $rv['content'] .= "</div>";
1401                                 }
1402                         }
1403
1404                         $rv['content'] .= "</div>";
1405
1406                         $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
1407                                 if ($line['note']) {
1408                                         $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
1409                                 }
1410                         $rv['content'] .= "</div>";
1411
1412                         if (!$line['lang']) $line['lang'] = 'en';
1413
1414                         $rv['content'] .= "<div class=\"postContent\" lang=\"".$line['lang']."\">";
1415
1416                         $rv['content'] .= $line["content"];
1417                         $rv['content'] .= format_article_enclosures($id,
1418                                 sql_bool_to_bool($line["always_display_enclosures"]),
1419                                 $line["content"],
1420                                 sql_bool_to_bool($line["hide_images"]));
1421
1422                         $rv['content'] .= "</div>";
1423
1424                         $rv['content'] .= "</div>";
1425
1426                 }
1427
1428                 if ($zoom_mode) {
1429                         $rv['content'] .= "
1430                                 <div class='footer'>
1431                                 <button onclick=\"return window.close()\">".
1432                                         __("Close this window")."</button></div>";
1433                         $rv['content'] .= "</body></html>";
1434                 }
1435
1436                 return $rv;
1437
1438         }
1439
1440         function print_checkpoint($n, $s) {
1441                 $ts = microtime(true);
1442                 echo sprintf("<!-- CP[$n] %.4f seconds -->\n", $ts - $s);
1443                 return $ts;
1444         }
1445
1446         function sanitize_tag($tag) {
1447                 $tag = trim($tag);
1448
1449                 $tag = mb_strtolower($tag, 'utf-8');
1450
1451                 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
1452
1453 //              $tag = str_replace('"', "", $tag);
1454 //              $tag = str_replace("+", " ", $tag);
1455                 $tag = str_replace("technorati tag: ", "", $tag);
1456
1457                 return $tag;
1458         }
1459
1460         function get_self_url_prefix() {
1461                 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
1462                         return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
1463                 } else {
1464                         return SELF_URL_PATH;
1465                 }
1466         }
1467
1468         /**
1469          * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
1470          *
1471          * @return string The Mozilla Firefox feed adding URL.
1472          */
1473         function add_feed_url() {
1474                 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' :  'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
1475
1476                 $url_path = get_self_url_prefix() .
1477                         "/public.php?op=subscribe&feed_url=%s";
1478                 return $url_path;
1479         } // function add_feed_url
1480
1481         function encrypt_password($pass, $salt = '', $mode2 = false) {
1482                 if ($salt && $mode2) {
1483                         return "MODE2:" . hash('sha256', $salt . $pass);
1484                 } else if ($salt) {
1485                         return "SHA1X:" . sha1("$salt:$pass");
1486                 } else {
1487                         return "SHA1:" . sha1($pass);
1488                 }
1489         } // function encrypt_password
1490
1491         function load_filters($feed_id, $owner_uid, $action_id = false) {
1492                 $filters = array();
1493
1494                 $cat_id = (int)getFeedCategory($feed_id);
1495
1496                 if ($cat_id == 0)
1497                         $null_cat_qpart = "cat_id IS NULL OR";
1498                 else
1499                         $null_cat_qpart = "";
1500
1501                 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
1502                         owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
1503
1504                 $check_cats = join(",", array_merge(
1505                         getParentCategories($cat_id, $owner_uid),
1506                         array($cat_id)));
1507
1508                 while ($line = db_fetch_assoc($result)) {
1509                         $filter_id = $line["id"];
1510
1511                         $result2 = db_query("SELECT
1512                                 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
1513                                 FROM ttrss_filters2_rules AS r,
1514                                 ttrss_filter_types AS t
1515                                 WHERE
1516                                         ($null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ($check_cats)) AND
1517                                         (feed_id IS NULL OR feed_id = '$feed_id') AND
1518                                         filter_type = t.id AND filter_id = '$filter_id'");
1519
1520                         $rules = array();
1521                         $actions = array();
1522
1523                         while ($rule_line = db_fetch_assoc($result2)) {
1524 #                               print_r($rule_line);
1525
1526                                 $rule = array();
1527                                 $rule["reg_exp"] = $rule_line["reg_exp"];
1528                                 $rule["type"] = $rule_line["type_name"];
1529                                 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
1530
1531                                 array_push($rules, $rule);
1532                         }
1533
1534                         $result2 = db_query("SELECT a.action_param,t.name AS type_name
1535                                 FROM ttrss_filters2_actions AS a,
1536                                 ttrss_filter_actions AS t
1537                                 WHERE
1538                                         action_id = t.id AND filter_id = '$filter_id'");
1539
1540                         while ($action_line = db_fetch_assoc($result2)) {
1541 #                               print_r($action_line);
1542
1543                                 $action = array();
1544                                 $action["type"] = $action_line["type_name"];
1545                                 $action["param"] = $action_line["action_param"];
1546
1547                                 array_push($actions, $action);
1548                         }
1549
1550
1551                         $filter = array();
1552                         $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
1553                         $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
1554                         $filter["rules"] = $rules;
1555                         $filter["actions"] = $actions;
1556
1557                         if (count($rules) > 0 && count($actions) > 0) {
1558                                 array_push($filters, $filter);
1559                         }
1560                 }
1561
1562                 return $filters;
1563         }
1564
1565         function get_score_pic($score) {
1566                 if ($score > 100) {
1567                         return "score_high.png";
1568                 } else if ($score > 0) {
1569                         return "score_half_high.png";
1570                 } else if ($score < -100) {
1571                         return "score_low.png";
1572                 } else if ($score < 0) {
1573                         return "score_half_low.png";
1574                 } else {
1575                         return "score_neutral.png";
1576                 }
1577         }
1578
1579         function feed_has_icon($id) {
1580                 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
1581         }
1582
1583         function init_plugins() {
1584                 PluginHost::getInstance()->load(PLUGINS, PluginHost::KIND_ALL);
1585
1586                 return true;
1587         }
1588
1589         function format_tags_string($tags, $id) {
1590                 if (!is_array($tags) || count($tags) == 0) {
1591                         return __("no tags");
1592                 } else {
1593                         $maxtags = min(5, count($tags));
1594
1595                         for ($i = 0; $i < $maxtags; $i++) {
1596                                 $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."')\">" . $tags[$i] . "</a>, ";
1597                         }
1598
1599                         $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2);
1600
1601                         if (count($tags) > $maxtags)
1602                                 $tags_str .= ", &hellip;";
1603
1604                         return $tags_str;
1605                 }
1606         }
1607
1608         function format_article_labels($labels, $id) {
1609
1610                 if (!is_array($labels)) return '';
1611
1612                 $labels_str = "";
1613
1614                 foreach ($labels as $l) {
1615                         $labels_str .= sprintf("<span class='hlLabelRef'
1616                                 style='color : %s; background-color : %s'>%s</span>",
1617                                         $l[2], $l[3], $l[1]);
1618                         }
1619
1620                 return $labels_str;
1621
1622         }
1623
1624         function format_article_note($id, $note, $allow_edit = true) {
1625
1626                 $str = "<div class='articleNote'        onclick=\"editArticleNote($id)\">
1627                         <div class='noteEdit' onclick=\"editArticleNote($id)\">".
1628                         ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
1629
1630                 return $str;
1631         }
1632
1633
1634         function get_feed_category($feed_cat, $parent_cat_id = false) {
1635                 if ($parent_cat_id) {
1636                         $parent_qpart = "parent_cat = '$parent_cat_id'";
1637                         $parent_insert = "'$parent_cat_id'";
1638                 } else {
1639                         $parent_qpart = "parent_cat IS NULL";
1640                         $parent_insert = "NULL";
1641                 }
1642
1643                 $result = db_query(
1644                         "SELECT id FROM ttrss_feed_categories
1645                         WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
1646
1647                 if (db_num_rows($result) == 0) {
1648                         return false;
1649                 } else {
1650                         return db_fetch_result($result, 0, "id");
1651                 }
1652         }
1653
1654         function add_feed_category($feed_cat, $parent_cat_id = false) {
1655
1656                 if (!$feed_cat) return false;
1657
1658                 db_query("BEGIN");
1659
1660                 if ($parent_cat_id) {
1661                         $parent_qpart = "parent_cat = '$parent_cat_id'";
1662                         $parent_insert = "'$parent_cat_id'";
1663                 } else {
1664                         $parent_qpart = "parent_cat IS NULL";
1665                         $parent_insert = "NULL";
1666                 }
1667
1668                 $feed_cat = mb_substr($feed_cat, 0, 250);
1669
1670                 $result = db_query(
1671                         "SELECT id FROM ttrss_feed_categories
1672                         WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
1673
1674                 if (db_num_rows($result) == 0) {
1675
1676                         $result = db_query(
1677                                 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
1678                                 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
1679
1680                         db_query("COMMIT");
1681
1682                         return true;
1683                 }
1684
1685                 return false;
1686         }
1687
1688         function getArticleFeed($id) {
1689                 $result = db_query("SELECT feed_id FROM ttrss_user_entries
1690                         WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
1691
1692                 if (db_num_rows($result) != 0) {
1693                         return db_fetch_result($result, 0, "feed_id");
1694                 } else {
1695                         return 0;
1696                 }
1697         }
1698
1699         /**
1700          * Fixes incomplete URLs by prepending "http://".
1701          * Also replaces feed:// with http://, and
1702          * prepends a trailing slash if the url is a domain name only.
1703          *
1704          * @param string $url Possibly incomplete URL
1705          *
1706          * @return string Fixed URL.
1707          */
1708         function fix_url($url) {
1709                 if (strpos($url, '://') === false) {
1710                         $url = 'http://' . $url;
1711                 } else if (substr($url, 0, 5) == 'feed:') {
1712                         $url = 'http:' . substr($url, 5);
1713                 }
1714
1715                 //prepend slash if the URL has no slash in it
1716                 // "http://www.example" -> "http://www.example/"
1717                 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
1718                         $url .= '/';
1719                 }
1720
1721                 if ($url != "http:///")
1722                         return $url;
1723                 else
1724                         return '';
1725         }
1726
1727         function validate_feed_url($url) {
1728                 $parts = parse_url($url);
1729
1730                 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
1731
1732         }
1733
1734         function get_article_enclosures($id) {
1735
1736                 $query = "SELECT * FROM ttrss_enclosures
1737                         WHERE post_id = '$id' AND content_url != ''";
1738
1739                 $rv = array();
1740
1741                 $result = db_query($query);
1742
1743                 if (db_num_rows($result) > 0) {
1744                         while ($line = db_fetch_assoc($result)) {
1745                                 array_push($rv, $line);
1746                         }
1747                 }
1748
1749                 return $rv;
1750         }
1751
1752         function save_email_address($email) {
1753                 // FIXME: implement persistent storage of emails
1754
1755                 if (!$_SESSION['stored_emails'])
1756                         $_SESSION['stored_emails'] = array();
1757
1758                 if (!in_array($email, $_SESSION['stored_emails']))
1759                         array_push($_SESSION['stored_emails'], $email);
1760         }
1761
1762
1763         function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
1764
1765                 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1766
1767                 $sql_is_cat = bool_to_sql_bool($is_cat);
1768
1769                 $result = db_query("SELECT access_key FROM ttrss_access_keys
1770                         WHERE feed_id = '$feed_id'      AND is_cat = $sql_is_cat
1771                         AND owner_uid = " . $owner_uid);
1772
1773                 if (db_num_rows($result) == 1) {
1774                         return db_fetch_result($result, 0, "access_key");
1775                 } else {
1776                         $key = db_escape_string(uniqid(base_convert(rand(), 10, 36)));
1777
1778                         $result = db_query("INSERT INTO ttrss_access_keys
1779                                 (access_key, feed_id, is_cat, owner_uid)
1780                                 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
1781
1782                         return $key;
1783                 }
1784                 return false;
1785         }
1786
1787         function get_feeds_from_html($url, $content)
1788         {
1789                 $url     = fix_url($url);
1790                 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
1791
1792                 libxml_use_internal_errors(true);
1793
1794                 $doc = new DOMDocument();
1795                 $doc->loadHTML($content);
1796                 $xpath = new DOMXPath($doc);
1797                 $entries = $xpath->query('/html/head/link[@rel="alternate" and '.
1798                         '(contains(@type,"rss") or contains(@type,"atom"))]|/html/head/link[@rel="feed"]');
1799                 $feedUrls = array();
1800                 foreach ($entries as $entry) {
1801                         if ($entry->hasAttribute('href')) {
1802                                 $title = $entry->getAttribute('title');
1803                                 if ($title == '') {
1804                                         $title = $entry->getAttribute('type');
1805                                 }
1806                                 $feedUrl = rewrite_relative_url(
1807                                         $baseUrl, $entry->getAttribute('href')
1808                                 );
1809                                 $feedUrls[$feedUrl] = $title;
1810                         }
1811                 }
1812                 return $feedUrls;
1813         }
1814
1815         function is_html($content) {
1816                 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
1817         }
1818
1819         function url_is_html($url, $login = false, $pass = false) {
1820                 return is_html(fetch_file_contents($url, false, $login, $pass));
1821         }
1822
1823         function print_label_select($name, $value, $attributes = "") {
1824
1825                 $result = db_query("SELECT caption FROM ttrss_labels2
1826                         WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
1827
1828                 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
1829                         "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
1830
1831                 while ($line = db_fetch_assoc($result)) {
1832
1833                         $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
1834
1835                         print "<option value=\"".htmlspecialchars($line["caption"])."\"
1836                                 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
1837
1838                 }
1839
1840 #               print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
1841
1842                 print "</select>";
1843
1844
1845         }
1846
1847         function format_article_enclosures($id, $always_display_enclosures,
1848                                         $article_content, $hide_images = false) {
1849
1850                 $result = get_article_enclosures($id);
1851                 $rv = '';
1852
1853                 if (count($result) > 0) {
1854
1855                         $entries_html = array();
1856                         $entries = array();
1857                         $entries_inline = array();
1858
1859                         foreach ($result as $line) {
1860
1861                                 $url = $line["content_url"];
1862                                 $ctype = $line["content_type"];
1863                                 $title = $line["title"];
1864
1865                                 if (!$ctype) $ctype = __("unknown type");
1866
1867                                 $filename = substr($url, strrpos($url, "/")+1);
1868
1869                                 $player = format_inline_player($url, $ctype);
1870
1871                                 if ($player) array_push($entries_inline, $player);
1872
1873 #                               $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1874 #                                       $filename . " (" . $ctype . ")" . "</a>";
1875
1876                                 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
1877                                         dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
1878
1879                                 array_push($entries_html, $entry);
1880
1881                                 $entry = array();
1882
1883                                 $entry["type"] = $ctype;
1884                                 $entry["filename"] = $filename;
1885                                 $entry["url"] = $url;
1886                                 $entry["title"] = $title;
1887
1888                                 array_push($entries, $entry);
1889                         }
1890
1891                         if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
1892                                 if ($always_display_enclosures ||
1893                                                         !preg_match("/<img/i", $article_content)) {
1894
1895                                         foreach ($entries as $entry) {
1896
1897                                                 if (preg_match("/image/", $entry["type"]) ||
1898                                                                 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
1899
1900                                                                 if (!$hide_images) {
1901                                                                         $rv .= "<p><img
1902                                                                         alt=\"".htmlspecialchars($entry["filename"])."\"
1903                                                                         src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
1904                                                                 } else {
1905                                                                         $rv .= "<p><a target=\"_blank\"
1906                                                                         href=\"".htmlspecialchars($entry["url"])."\"
1907                                                                         >" .htmlspecialchars($entry["url"]) . "</a></p>";
1908                                                                 }
1909
1910                                                                 if ($entry['title']) {
1911                                                                         $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>";
1912                                                                 }
1913                                                 }
1914                                         }
1915                                 }
1916                         }
1917
1918                         if (count($entries_inline) > 0) {
1919                                 $rv .= "<hr clear='both'/>";
1920                                 foreach ($entries_inline as $entry) { $rv .= $entry; };
1921                                 $rv .= "<hr clear='both'/>";
1922                         }
1923
1924                         $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
1925                                 "<option value=''>" . __('Attachments')."</option>";
1926
1927                         foreach ($entries as $entry) {
1928                                 if ($entry["title"])
1929                                         $title = "&mdash; " . truncate_string($entry["title"], 30);
1930                                 else
1931                                         $title = "";
1932
1933                                 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "$title</option>";
1934
1935                         };
1936
1937                         $rv .= "</select>";
1938                 }
1939
1940                 return $rv;
1941         }
1942
1943         function getLastArticleId() {
1944                 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
1945                         WHERE owner_uid = " . $_SESSION["uid"]);
1946
1947                 if (db_num_rows($result) == 1) {
1948                         return db_fetch_result($result, 0, "id");
1949                 } else {
1950                         return -1;
1951                 }
1952         }
1953
1954         function build_url($parts) {
1955                 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
1956         }
1957
1958         /**
1959          * Converts a (possibly) relative URL to a absolute one.
1960          *
1961          * @param string $url     Base URL (i.e. from where the document is)
1962          * @param string $rel_url Possibly relative URL in the document
1963          *
1964          * @return string Absolute URL
1965          */
1966         function rewrite_relative_url($url, $rel_url) {
1967                 if (strpos($rel_url, ":") !== false) {
1968                         return $rel_url;
1969                 } else if (strpos($rel_url, "://") !== false) {
1970                         return $rel_url;
1971                 } else if (strpos($rel_url, "//") === 0) {
1972                         # protocol-relative URL (rare but they exist)
1973                         return $rel_url;
1974                 } else if (strpos($rel_url, "/") === 0)
1975                 {
1976                         $parts = parse_url($url);
1977                         $parts['path'] = $rel_url;
1978
1979                         return build_url($parts);
1980
1981                 } else {
1982                         $parts = parse_url($url);
1983                         if (!isset($parts['path'])) {
1984                                 $parts['path'] = '/';
1985                         }
1986                         $dir = $parts['path'];
1987                         if (substr($dir, -1) !== '/') {
1988                                 $dir = dirname($parts['path']);
1989                                 $dir !== '/' && $dir .= '/';
1990                         }
1991                         $parts['path'] = $dir . $rel_url;
1992
1993                         return build_url($parts);
1994                 }
1995         }
1996
1997         function sphinx_search($query, $offset = 0, $limit = 30) {
1998                 require_once 'lib/sphinxapi.php';
1999
2000                 $sphinxClient = new SphinxClient();
2001
2002                 $sphinxpair = explode(":", SPHINX_SERVER, 2);
2003
2004                 $sphinxClient->SetServer($sphinxpair[0], (int)$sphinxpair[1]);
2005                 $sphinxClient->SetConnectTimeout(1);
2006
2007                 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
2008                         'feed_title' => 20));
2009
2010                 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
2011                 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
2012                 $sphinxClient->SetLimits($offset, $limit, 1000);
2013                 $sphinxClient->SetArrayResult(false);
2014                 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
2015
2016                 $result = $sphinxClient->Query($query, SPHINX_INDEX);
2017
2018                 $ids = array();
2019
2020                 if (is_array($result['matches'])) {
2021                         foreach (array_keys($result['matches']) as $int_id) {
2022                                 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
2023                                 array_push($ids, $ref_id);
2024                         }
2025                 }
2026
2027                 return $ids;
2028         }
2029
2030         function cleanup_tags($days = 14, $limit = 1000) {
2031
2032                 if (DB_TYPE == "pgsql") {
2033                         $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
2034                 } else if (DB_TYPE == "mysql") {
2035                         $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
2036                 }
2037
2038                 $tags_deleted = 0;
2039
2040                 while ($limit > 0) {
2041                         $limit_part = 500;
2042
2043                         $query = "SELECT ttrss_tags.id AS id
2044                                 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
2045                                 WHERE post_int_id = int_id AND $interval_query AND
2046                                 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
2047
2048                         $result = db_query($query);
2049
2050                         $ids = array();
2051
2052                         while ($line = db_fetch_assoc($result)) {
2053                                 array_push($ids, $line['id']);
2054                         }
2055
2056                         if (count($ids) > 0) {
2057                                 $ids = join(",", $ids);
2058
2059                                 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
2060                                 $tags_deleted += db_affected_rows($tmp_result);
2061                         } else {
2062                                 break;
2063                         }
2064
2065                         $limit -= $limit_part;
2066                 }
2067
2068                 return $tags_deleted;
2069         }
2070
2071         function print_user_stylesheet() {
2072                 $value = get_pref('USER_STYLESHEET');
2073
2074                 if ($value) {
2075                         print "<style type=\"text/css\">";
2076                         print str_replace("<br/>", "\n", $value);
2077                         print "</style>";
2078                 }
2079
2080         }
2081
2082         function filter_to_sql($filter, $owner_uid) {
2083                 $query = array();
2084
2085                 if (DB_TYPE == "pgsql")
2086                         $reg_qpart = "~";
2087                 else
2088                         $reg_qpart = "REGEXP";
2089
2090                 foreach ($filter["rules"] AS $rule) {
2091                         $rule['reg_exp'] = str_replace('/', '\/', $rule["reg_exp"]);
2092                         $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
2093                                 $rule['reg_exp']) !== FALSE;
2094
2095                         if ($regexp_valid) {
2096
2097                                 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
2098
2099                                         switch ($rule["type"]) {
2100                                         case "title":
2101                                                 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
2102                                                         $rule['reg_exp'] . "')";
2103                                                 break;
2104                                         case "content":
2105                                                 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
2106                                                         $rule['reg_exp'] . "')";
2107                                                 break;
2108                                         case "both":
2109                                                 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
2110                                                         $rule['reg_exp'] . "') OR LOWER(" .
2111                                                         "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
2112                                                 break;
2113                                         case "tag":
2114                                                 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
2115                                                         $rule['reg_exp'] . "')";
2116                                                 break;
2117                                         case "link":
2118                                                 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
2119                                                         $rule['reg_exp'] . "')";
2120                                                 break;
2121                                         case "author":
2122                                                 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
2123                                                         $rule['reg_exp'] . "')";
2124                                                 break;
2125                                 }
2126
2127                                 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
2128
2129                                 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
2130                                         $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
2131                                 }
2132
2133                                 if (isset($rule["cat_id"])) {
2134
2135                                         if ($rule["cat_id"] > 0) {
2136                                                 $children = getChildCategories($rule["cat_id"], $owner_uid);
2137                                                 array_push($children, $rule["cat_id"]);
2138
2139                                                 $children = join(",", $children);
2140
2141                                                 $cat_qpart = "cat_id IN ($children)";
2142                                         } else {
2143                                                 $cat_qpart = "cat_id IS NULL";
2144                                         }
2145
2146                                         $qpart .= " AND $cat_qpart";
2147                                 }
2148
2149                                 $qpart .= " AND feed_id IS NOT NULL";
2150
2151                                 array_push($query, "($qpart)");
2152
2153                         }
2154                 }
2155
2156                 if (count($query) > 0) {
2157                         $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
2158                 } else {
2159                         $fullquery = "(false)";
2160                 }
2161
2162                 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
2163
2164                 return $fullquery;
2165         }
2166
2167         if (!function_exists('gzdecode')) {
2168                 function gzdecode($string) { // no support for 2nd argument
2169                         return file_get_contents('compress.zlib://data:who/cares;base64,'.
2170                                 base64_encode($string));
2171                 }
2172         }
2173
2174         function get_random_bytes($length) {
2175                 if (function_exists('openssl_random_pseudo_bytes')) {
2176                         return openssl_random_pseudo_bytes($length);
2177                 } else {
2178                         $output = "";
2179
2180                         for ($i = 0; $i < $length; $i++)
2181                                 $output .= chr(mt_rand(0, 255));
2182
2183                         return $output;
2184                 }
2185         }
2186
2187         function read_stdin() {
2188                 $fp = fopen("php://stdin", "r");
2189
2190                 if ($fp) {
2191                         $line = trim(fgets($fp));
2192                         fclose($fp);
2193                         return $line;
2194                 }
2195
2196                 return null;
2197         }
2198
2199         function tmpdirname($path, $prefix) {
2200                 // Use PHP's tmpfile function to create a temporary
2201                 // directory name. Delete the file and keep the name.
2202                 $tempname = tempnam($path,$prefix);
2203                 if (!$tempname)
2204                         return false;
2205
2206                 if (!unlink($tempname))
2207                         return false;
2208
2209        return $tempname;
2210         }
2211
2212         function getFeedCategory($feed) {
2213                 $result = db_query("SELECT cat_id FROM ttrss_feeds
2214                         WHERE id = '$feed'");
2215
2216                 if (db_num_rows($result) > 0) {
2217                         return db_fetch_result($result, 0, "cat_id");
2218                 } else {
2219                         return false;
2220                 }
2221
2222         }
2223
2224         function implements_interface($class, $interface) {
2225                 return in_array($interface, class_implements($class));
2226         }
2227
2228         function geturl($url, $depth = 0){
2229
2230                 if ($depth == 20) return $url;
2231
2232                 if (!function_exists('curl_init'))
2233                         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);
2234
2235                 $curl = curl_init();
2236                 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
2237                 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
2238                 $header[] = "Cache-Control: max-age=0";
2239                 $header[] = "Connection: keep-alive";
2240                 $header[] = "Keep-Alive: 300";
2241                 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
2242                 $header[] = "Accept-Language: en-us,en;q=0.5";
2243                 $header[] = "Pragma: ";
2244
2245                 curl_setopt($curl, CURLOPT_URL, $url);
2246                 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
2247                 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
2248                 curl_setopt($curl, CURLOPT_HEADER, true);
2249                 curl_setopt($curl, CURLOPT_REFERER, $url);
2250                 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
2251                 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
2252                 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
2253                 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
2254                 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
2255                 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
2256
2257                 if (defined('_CURL_HTTP_PROXY')) {
2258                         curl_setopt($curl, CURLOPT_PROXY, _CURL_HTTP_PROXY);
2259                 }
2260
2261                 if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
2262                         curl_setopt($curl, CURLOPT_SSLVERSION, 3);
2263                 }
2264
2265                 $html = curl_exec($curl);
2266
2267                 $status = curl_getinfo($curl);
2268
2269                 if($status['http_code']!=200){
2270                         if($status['http_code'] == 301 || $status['http_code'] == 302) {
2271                                 curl_close($curl);
2272                                 list($header) = explode("\r\n\r\n", $html, 2);
2273                                 $matches = array();
2274                                 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
2275                                 $url = trim(str_replace($matches[1],"",$matches[0]));
2276                                 $url_parsed = parse_url($url);
2277                                 return (isset($url_parsed))? geturl($url, $depth + 1):'';
2278                         }
2279
2280                         global $fetch_last_error;
2281
2282                         $fetch_last_error = curl_errno($curl) . " " . curl_error($curl);
2283                         curl_close($curl);
2284
2285 #                       $oline='';
2286 #                       foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
2287 #                       $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
2288 #                       $handle = @fopen('./curl.error.log', 'a');
2289 #                       fwrite($handle, $line);
2290                         return FALSE;
2291                 }
2292                 curl_close($curl);
2293                 return $url;
2294         }
2295
2296         function get_minified_js($files) {
2297                 require_once 'lib/jshrink/Minifier.php';
2298
2299                 $rv = '';
2300
2301                 foreach ($files as $js) {
2302                         if (!isset($_GET['debug'])) {
2303                                 $cached_file = CACHE_DIR . "/js/".basename($js).".js";
2304
2305                                 if (file_exists($cached_file) &&
2306                                                 is_readable($cached_file) &&
2307                                                 filemtime($cached_file) >= filemtime("js/$js.js")) {
2308
2309                                         $rv .= file_get_contents($cached_file);
2310
2311                                 } else {
2312                                         $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
2313                                         file_put_contents($cached_file, $minified);
2314                                         $rv .= $minified;
2315                                 }
2316                         } else {
2317                                 $rv .= file_get_contents("js/$js.js");
2318                         }
2319                 }
2320
2321                 return $rv;
2322         }
2323
2324         function stylesheet_tag($filename) {
2325                 $timestamp = filemtime($filename);
2326
2327                 return "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
2328         }
2329
2330         function javascript_tag($filename) {
2331                 $query = "";
2332
2333                 if (!(strpos($filename, "?") === FALSE)) {
2334                         $query = substr($filename, strpos($filename, "?")+1);
2335                         $filename = substr($filename, 0, strpos($filename, "?"));
2336                 }
2337
2338                 $timestamp = filemtime($filename);
2339
2340                 if ($query) $timestamp .= "&$query";
2341
2342                 return "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
2343         }
2344
2345         function calculate_dep_timestamp() {
2346                 $files = array_merge(glob("js/*.js"), glob("css/*.css"));
2347
2348                 $max_ts = -1;
2349
2350                 foreach ($files as $file) {
2351                         if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
2352                 }
2353
2354                 return $max_ts;
2355         }
2356
2357         function T_js_decl($s1, $s2) {
2358                 if ($s1 && $s2) {
2359                         $s1 = preg_replace("/\n/", "", $s1);
2360                         $s2 = preg_replace("/\n/", "", $s2);
2361
2362                         $s1 = preg_replace("/\"/", "\\\"", $s1);
2363                         $s2 = preg_replace("/\"/", "\\\"", $s2);
2364
2365                         return "T_messages[\"$s1\"] = \"$s2\";\n";
2366                 }
2367         }
2368
2369         function init_js_translations() {
2370
2371         print 'var T_messages = new Object();
2372
2373                 function __(msg) {
2374                         if (T_messages[msg]) {
2375                                 return T_messages[msg];
2376                         } else {
2377                                 return msg;
2378                         }
2379                 }
2380
2381                 function ngettext(msg1, msg2, n) {
2382                         return __((parseInt(n) > 1) ? msg2 : msg1);
2383                 }';
2384
2385                 $l10n = _get_reader();
2386
2387                 for ($i = 0; $i < $l10n->total; $i++) {
2388                         $orig = $l10n->get_original_string($i);
2389                         if(strpos($orig, "\000") !== FALSE) { // Plural forms
2390                                 $key = explode(chr(0), $orig);
2391                                 print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
2392                                 print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
2393                         } else {
2394                                 $translation = __($orig);
2395                                 print T_js_decl($orig, $translation);
2396                         }
2397                 }
2398         }
2399
2400         function label_to_feed_id($label) {
2401                 return LABEL_BASE_INDEX - 1 - abs($label);
2402         }
2403
2404         function feed_to_label_id($feed) {
2405                 return LABEL_BASE_INDEX - 1 + abs($feed);
2406         }
2407
2408         function format_libxml_error($error) {
2409                 return T_sprintf("LibXML error %s at line %d (column %d): %s",
2410                                 $error->code, $error->line, $error->column,
2411                                 $error->message);
2412         }
2413 ?>