PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/rss.php

http://github.com/s9y/Serendipity
PHP | 320 lines | 254 code | 48 blank | 18 comment | 51 complexity | 03f01fb54bd716847838058e2738bd55 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0
  1. <?php
  2. # Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
  3. # All rights reserved. See LICENSE file for licensing details
  4. header('Content-Type: text/xml; charset=utf-8');
  5. @define('IN_RSS', true);
  6. include('serendipity_config.inc.php');
  7. include(S9Y_INCLUDE_PATH . 'include/functions_rss.inc.php');
  8. if ($serendipity['cors']) {
  9. header('Access-Control-Allow-Origin: *'); // Allow RSS feeds to be read by javascript
  10. }
  11. $version = $_GET['version'] ?? '';
  12. $description = $serendipity['blogDescription'];
  13. $title = $serendipity['blogTitle'];
  14. $comments = FALSE;
  15. if (empty($version)) {
  16. list($version) = serendipity_discover_rss($_GET['file'] ?? null, $_GET['ext'] ?? null);
  17. } else {
  18. # be sure it is an allowed version, to prevent attackers sniffing for unrelated files on the file system
  19. $allowed_versions = ['opml1.0', '0.91', '1.0', '2.0', 'atom0.3', 'atom1.0'];
  20. if (! in_array($version, $allowed_versions, true)) {
  21. header('Status: 404 Not Found');
  22. exit;
  23. }
  24. }
  25. if (isset($_GET['category'])) {
  26. $serendipity['GET']['category'] = $_GET['category'];
  27. }
  28. if (isset($_GET['viewAuthor'])) {
  29. $serendipity['GET']['viewAuthor'] = $_GET['viewAuthor'];
  30. }
  31. if (!isset($_GET['type'])) {
  32. $_GET['type'] = 'content';
  33. }
  34. if (!empty($_SERVER['HTTP_USER_AGENT']) && stristr($_SERVER['HTTP_USER_AGENT'], 'feedburner')) {
  35. $_GET['nocache'] = true;
  36. }
  37. $serendipity['view'] = 'feed';
  38. switch ($_GET['type']) {
  39. case 'comments_and_trackbacks':
  40. case 'trackbacks':
  41. case 'comments':
  42. $latest_entry = serendipity_fetchComments(isset($_GET['cid']) ? $_GET['cid'] : null, 1, 'co.id desc', false, $_GET['type']);
  43. break;
  44. case 'content':
  45. default:
  46. $latest_entry = serendipity_fetchEntries(null, false, 1, false, false, 'last_modified DESC', '', false, true);
  47. break;
  48. }
  49. if (!isset($_GET['nocache'])) {
  50. /*
  51. * Caching logic - Do not send feed if nothing has changed
  52. * Implementation inspired by Simon Willison [http://simon.incutio.com/archive/2003/04/23/conditionalGet], Thiemo Maettig
  53. */
  54. // See if the client has provided the required headers.
  55. // Always convert the provided header into GMT timezone to allow comparing to the server-side last-modified header
  56. $modified_since = !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])
  57. ? gmdate('D, d M Y H:i:s \G\M\T', strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])))
  58. : false;
  59. $none_match = !empty($_SERVER['HTTP_IF_NONE_MATCH'])
  60. ? str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH']))
  61. : false;
  62. if (is_array($latest_entry) && isset($latest_entry[0]['last_modified'])) {
  63. $last_modified = gmdate('D, d M Y H:i:s \G\M\T', serendipity_serverOffsetHour($latest_entry[0]['last_modified'], false));
  64. $etag = '"' . $last_modified . '"';
  65. header('Last-Modified: ' . $last_modified);
  66. header('ETag: ' . $etag);
  67. if (($none_match == $last_modified && $modified_since == $last_modified) ||
  68. (!$none_match && $modified_since == $last_modified) ||
  69. (!$modified_since && $none_match == $last_modified)) {
  70. header('HTTP/1.0 304 Not Modified');
  71. header('Status: 304 Not Modified');
  72. return;
  73. }
  74. }
  75. }
  76. if (isset($modified_since) &&
  77. (stristr($_SERVER['HTTP_USER_AGENT'], 'planet') !== FALSE || $serendipity['enforce_RFC2616'])) {
  78. // People shall get a usual HTTP response according to RFC2616. See serendipity_config.inc.php for details
  79. $modified_since = FALSE;
  80. }
  81. switch ($_GET['type']) {
  82. case 'comments_and_trackbacks':
  83. case 'trackbacks':
  84. case 'comments':
  85. $entries = serendipity_fetchComments(isset($_GET['cid']) ? $_GET['cid'] : null, (int)$serendipity['RSSfetchLimit'], 'co.id desc', false, $_GET['type']);
  86. $description = $title . ' - ' . $description;
  87. if (isset($_GET['cid'])) {
  88. $title = $title . ' - ' . COMMENTS_FROM . ' "' . $latest_entry[0]['title'] . '"';
  89. } else {
  90. $title = $title . ' - ' . COMMENTS;
  91. }
  92. $comments = TRUE;
  93. break;
  94. case 'content':
  95. default:
  96. if (isset($_GET['all']) && $_GET['all']) {
  97. // Fetch all entries in reverse order for later importing. Fetch sticky entries as normal entries.
  98. $entries = serendipity_fetchEntries(null, true, '', false, false, 'id ASC', '', false, true);
  99. } else {
  100. $entries = serendipity_fetchEntries(null, true, $serendipity['RSSfetchLimit'], false, (isset($modified_since) ? $modified_since : false), 'timestamp DESC', '', false, true);
  101. }
  102. break;
  103. }
  104. if (isset($serendipity['serendipityRealname'])) {
  105. $title .= ' (' . LOGIN . ': ' . $serendipity['serendipityRealname'] . ')';
  106. }
  107. if (!empty($serendipity['GET']['category'])) {
  108. $cInfo = serendipity_fetchCategoryInfo((int)$serendipity['GET']['category']);
  109. $title = serendipity_utf8_encode(serendipity_specialchars($title . ' - '. $cInfo['category_name']));
  110. } elseif (!empty($serendipity['GET']['viewAuthor'])) {
  111. list($aInfo) = serendipity_fetchAuthor((int)$serendipity['GET']['viewAuthor']);
  112. $title = serendipity_utf8_encode(serendipity_specialchars($aInfo['realname'] . ' - '. $title ));
  113. } else {
  114. $title = serendipity_utf8_encode(serendipity_specialchars($title));
  115. }
  116. $description = serendipity_utf8_encode(serendipity_specialchars($description));
  117. $metadata = array(
  118. 'title' => $title,
  119. 'description' => $description,
  120. 'language' => $serendipity['lang'],
  121. 'additional_fields' => array(),
  122. 'link' => $serendipity['baseURL'],
  123. 'email' => $serendipity['blogMail'],
  124. 'fullFeed' => false,
  125. 'showMail' => false,
  126. 'version' => $version
  127. );
  128. if (serendipity_get_config_var('feedBannerURL') != '') {
  129. $img = serendipity_get_config_var('feedBannerURL');
  130. $w = serendipity_get_config_var('feedBannerWidth');
  131. $h = serendipity_get_config_var('feedBannerHeight');
  132. } elseif (($banner = serendipity_getTemplateFile('img/rss_banner.png', 'serendipityPath'))) {
  133. $img = serendipity_getTemplateFile('img/rss_banner.png', 'baseURL');
  134. $i = getimagesize($banner);
  135. $w = $i[0];
  136. $h = $i[1];
  137. } else {
  138. $img = serendipity_getTemplateFile('img/s9y_banner_small.png', 'baseURL');
  139. $w = 100;
  140. $h = 21;
  141. }
  142. $metadata['additional_fields']['image'] = <<<IMAGE
  143. <image>
  144. <url>$img</url>
  145. <title>RSS: $title - $description</title>
  146. <link>{$serendipity['baseURL']}</link>
  147. <width>$w</width>
  148. <height>$h</height>
  149. </image>
  150. IMAGE;
  151. $metadata['additional_fields']['image_atom1.0'] = <<<IMAGE
  152. <icon>$img</icon>
  153. IMAGE;
  154. $metadata['additional_fields']['image_rss1.0_channel'] = '<image rdf:resource="' . $img . '" />';
  155. $metadata['additional_fields']['image_rss1.0_rdf'] = <<<IMAGE
  156. <image rdf:about="$img">
  157. <url>$img</url>
  158. <title>RSS: $title - $description</title>
  159. <link>{$serendipity['baseURL']}</link>
  160. <width>$w</width>
  161. <height>$h</height>
  162. </image>
  163. IMAGE;
  164. // Now, if set, stitch together any fields that have been configured in the syndication plugin.
  165. // First, do some sanity checks
  166. $metadata['additional_fields']['channel'] = '';
  167. $rssFields = array('feedManagingEditor' => 'managingEditor', 'feedWebmaster' => 'webMaster', 'feedTtl' => 'ttl', 'feedPubDate' => 'pubDate');
  168. foreach( $rssFields as $configName => $field) {
  169. $fieldValue = serendipity_get_config_var($configName);
  170. switch($field) {
  171. case 'pubDate':
  172. if (serendipity_db_bool($fieldValue)) {
  173. $fieldValue = gmdate('D, d M Y H:i:s \G\M\T', $entries[0]['last_modified']);
  174. } else {
  175. $fieldValue = '';
  176. }
  177. break;
  178. // Each new RSS-field which needs rewrite of its content should get its own case here.
  179. default:
  180. break;
  181. }
  182. if ($fieldValue != '') {
  183. $metadata['additional_fields']['channel'] .= '<' . $field . '>' . $fieldValue . '</' . $field . '>' . "\n";
  184. }
  185. }
  186. if (is_array($metadata['additional_fields'])) {
  187. // Fix up array keys, because "." are not allowed when wanting to output using Smarty
  188. foreach($metadata['additional_fields'] AS $_aid => $af) {
  189. $aid = str_replace('.', '', $_aid);
  190. $metadata['additional_fields'][$aid] = $af;
  191. }
  192. }
  193. $metadata['fullFeed'] = serendipity_get_config_var('feedFull', false);
  194. if ($metadata['fullFeed'] === 'client') {
  195. if ($_GET['fullFeed'] || $serendipity['GET']['fullFeed']) {
  196. $metadata['fullFeed'] = true;
  197. } else {
  198. $metadata['fullFeed'] = false;
  199. }
  200. }
  201. if ($_GET['type'] == 'content' &&
  202. !isset($_GET['category']) &&
  203. !isset($serendipity['GET']['tag']) &&
  204. serendipity_db_bool(serendipity_get_config_var('feedForceCustom', false)) &&
  205. !preg_match('@FeedBurn@i', $_SERVER['HTTP_USER_AGENT']) && // the hardcoded pass for feedburner is for BC. New services should just use the forceLocal-param
  206. !isset($_GET['forceLocal'])
  207. ) {
  208. header('Status: 302 Found');
  209. header('Location: ' . serendipity_get_config_var('feedCustom'));
  210. exit;
  211. }
  212. $metadata['showMail'] = serendipity_db_bool(serendipity_get_config_var('feedShowMail', $metadata['showMail']));
  213. $file_version = preg_replace('@[^0-9a-z\.-_]@i', '', $version);
  214. $metadata['template_file'] = 'feed_' . $file_version . '.tpl';
  215. serendipity_smarty_init();
  216. serendipity_plugin_api::hook_event('frontend_rss', $metadata);
  217. $self_url = ($_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . serendipity_specialchars($_SERVER['REQUEST_URI']);
  218. if (!is_array($entries)) {
  219. $entries = array();
  220. }
  221. if ($entries[0]['last_modified']) {
  222. $gm_modified = gmdate('Y-m-d\TH:i:s\Z', serendipity_serverOffsetHour($entries[0]['last_modified']));
  223. } else {
  224. $gm_modified = gmdate('Y-m-d\TH:i:s\Z', serendipity_serverOffsetHour());
  225. }
  226. serendipity_printEntries_rss($entries, $version, $comments, $metadata['fullFeed'], $metadata['showMail']);
  227. $namespace_hook = 'frontend_display:unknown:namespace';
  228. $once_display_dat = '';
  229. switch($version) {
  230. case 'opml1.0':
  231. $namespace_hook = 'frontend_display:opml-1.0:namespace';
  232. break;
  233. case '0.91':
  234. $namespace_hook = 'frontend_display:rss-0.91:namespace';
  235. break;
  236. case '1.0':
  237. $namespace_hook = 'frontend_display:rss-1.0:namespace';
  238. serendipity_plugin_api::hook_event('frontend_display:rss-1.0:once', $entries);
  239. $once_display_dat = $entries['display_dat'];
  240. unset($entries['display_dat']);
  241. break;
  242. case '2.0':
  243. $namespace_hook = 'frontend_display:rss-2.0:namespace';
  244. break;
  245. case 'atom0.3':
  246. $namespace_hook = 'frontend_display:atom-0.3:namespace';
  247. break;
  248. case 'atom1.0':
  249. // For people wanting extra RFC compliance
  250. // header('Content-Type: application/atom+xml; charset=utf-8');
  251. $namespace_hook = 'frontend_display:atom-1.0:namespace';
  252. break;
  253. }
  254. serendipity_plugin_api::hook_event($namespace_hook, $entries);
  255. $namespace_display_dat = $entries['display_dat'] ?? null;
  256. $channel_display_dat = $entries['channel_dat'] ?? null;
  257. unset($entries['display_dat']);
  258. unset($entries['channel_dat']);
  259. $serendipity['smarty']->assignByRef('metadata', $metadata);
  260. $serendipity['smarty']->assignByRef('entries', $entries);
  261. $serendipity['smarty']->assignByRef('namespace_display_dat', $namespace_display_dat);
  262. $serendipity['smarty']->assignByRef('channel_display_dat', $channel_display_dat);
  263. $serendipity['smarty']->assignByRef('once_display_dat', $once_display_dat);
  264. $serendipity['smarty']->assign(
  265. array(
  266. 'is_comments' => $comments,
  267. 'last_modified' => $gm_modified,
  268. 'self_url' => $self_url,
  269. )
  270. );
  271. $serendipity['smarty']->display($metadata['template_file']);
  272. /* vim: set sts=4 ts=4 expandtab : */