<?php
# Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
# All rights reserved.  See LICENSE file for licensing details

header('Content-Type: text/xml; charset=utf-8');

@define('IN_RSS', true);
include('serendipity_config.inc.php');
include(S9Y_INCLUDE_PATH . 'include/functions_rss.inc.php');

if ($serendipity['cors']) {
    header('Access-Control-Allow-Origin: *'); // Allow RSS feeds to be read by javascript
}

$version     = $_GET['version'] ?? '';
$description = $serendipity['blogDescription'];
$title       = $serendipity['blogTitle'];
$comments    = FALSE;

if (empty($version)) {
    list($version) = serendipity_discover_rss($_GET['file'] ?? null, $_GET['ext'] ?? null);
} else {
    # be sure it is an allowed version, to prevent attackers sniffing for unrelated files on the file system
    $allowed_versions = ['opml1.0', '0.91', '1.0',  '2.0', 'atom0.3', 'atom1.0'];
    if (! in_array($version, $allowed_versions, true)) {
        header('Status: 404 Not Found');
        exit;
    }
}

if (isset($_GET['category'])) {
    $serendipity['GET']['category'] = $_GET['category'];
}

if (isset($_GET['viewAuthor'])) {
    $serendipity['GET']['viewAuthor'] = $_GET['viewAuthor'];
}

if (!isset($_GET['type'])) {
    $_GET['type'] = 'content';
}

if (!empty($_SERVER['HTTP_USER_AGENT']) && stristr($_SERVER['HTTP_USER_AGENT'], 'feedburner')) {
    $_GET['nocache'] = true;
}

$serendipity['view'] = 'feed';

switch ($_GET['type']) {
    case 'comments_and_trackbacks':
    case 'trackbacks':
    case 'comments':
        $latest_entry = serendipity_fetchComments(isset($_GET['cid']) ? $_GET['cid'] : null, 1, 'co.id desc', false, $_GET['type']);
        break;
    case 'content':
    default:
        $latest_entry = serendipity_fetchEntries(null, false, 1, false, false, 'last_modified DESC', '', false, true);
    break;
}

if (!isset($_GET['nocache'])) {
    /*
     * Caching logic - Do not send feed if nothing has changed
     * Implementation inspired by Simon Willison [http://simon.incutio.com/archive/2003/04/23/conditionalGet], Thiemo Maettig
     */

    // See if the client has provided the required headers.
    // Always convert the provided header into GMT timezone to allow comparing to the server-side last-modified header
    $modified_since = !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])
                    ? gmdate('D, d M Y H:i:s \G\M\T', strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE'])))
                    : false;
    $none_match     = !empty($_SERVER['HTTP_IF_NONE_MATCH'])
                    ? str_replace('"', '', stripslashes($_SERVER['HTTP_IF_NONE_MATCH']))
                    : false;

    if (is_array($latest_entry) && isset($latest_entry[0]['last_modified'])) {
        $last_modified = gmdate('D, d M Y H:i:s \G\M\T', serendipity_serverOffsetHour($latest_entry[0]['last_modified'], false));
        $etag          = '"' . $last_modified . '"';

        header('Last-Modified: ' . $last_modified);
        header('ETag: '          . $etag);

        if (($none_match == $last_modified && $modified_since == $last_modified) ||
            (!$none_match && $modified_since == $last_modified) ||
            (!$modified_since && $none_match == $last_modified)) {
            header('HTTP/1.0 304 Not Modified');
            header('Status: 304 Not Modified');
            return;
        }
    }
}

if (isset($modified_since) &&
        (stristr($_SERVER['HTTP_USER_AGENT'], 'planet') !== FALSE || $serendipity['enforce_RFC2616'])) {
    // People shall get a usual HTTP response according to RFC2616. See serendipity_config.inc.php for details
    $modified_since = FALSE;
}

switch ($_GET['type']) {
    case 'comments_and_trackbacks':
    case 'trackbacks':
    case 'comments':
        $entries     = serendipity_fetchComments(isset($_GET['cid']) ? $_GET['cid'] : null, (int)$serendipity['RSSfetchLimit'], 'co.id desc', false, $_GET['type']);
        $description = $title . ' - ' . $description;
        if (isset($_GET['cid'])) {
            $title   = $title . ' - ' . COMMENTS_FROM . ' "' . $latest_entry[0]['title'] . '"';
        } else {
            $title   = $title . ' - ' . COMMENTS;
        }
        $comments    = TRUE;
        break;

    case 'content':
    default:
        if (isset($_GET['all']) && $_GET['all']) {
            // Fetch all entries in reverse order for later importing. Fetch sticky entries as normal entries.
            $entries = serendipity_fetchEntries(null, true, '', false, false, 'id ASC', '', false, true);
        } else {
            $entries = serendipity_fetchEntries(null, true, $serendipity['RSSfetchLimit'], false, (isset($modified_since) ? $modified_since : false), 'timestamp DESC', '', false, true);
        }
        break;
}

if (isset($serendipity['serendipityRealname'])) {
    $title .= ' (' . LOGIN . ': ' . $serendipity['serendipityRealname'] . ')';
}

if (!empty($serendipity['GET']['category'])) {
    $cInfo       = serendipity_fetchCategoryInfo((int)$serendipity['GET']['category']);
    $title       = serendipity_utf8_encode(serendipity_specialchars($title . ' - '. $cInfo['category_name']));
} elseif (!empty($serendipity['GET']['viewAuthor'])) {
    list($aInfo) = serendipity_fetchAuthor((int)$serendipity['GET']['viewAuthor']);
    $title       = serendipity_utf8_encode(serendipity_specialchars($aInfo['realname'] . ' - '. $title ));
} else {
    $title       = serendipity_utf8_encode(serendipity_specialchars($title));
}

$description = serendipity_utf8_encode(serendipity_specialchars($description));

$metadata = array(
    'title'             => $title,
    'description'       => $description,
    'language'          => $serendipity['lang'],
    'additional_fields' => array(),
    'link'              => $serendipity['baseURL'],
    'email'             => $serendipity['blogMail'],
    'fullFeed'          => false,
    'showMail'          => false,
    'version'           => $version
);

if (serendipity_get_config_var('feedBannerURL') != '') {
    $img = serendipity_get_config_var('feedBannerURL');
    $w   = serendipity_get_config_var('feedBannerWidth');
    $h   = serendipity_get_config_var('feedBannerHeight');
} elseif (($banner = serendipity_getTemplateFile('img/rss_banner.png', 'serendipityPath'))) {
    $img = serendipity_getTemplateFile('img/rss_banner.png', 'baseURL');
    $i   = getimagesize($banner);
    $w   = $i[0];
    $h   = $i[1];
} else {
    $img = serendipity_getTemplateFile('img/s9y_banner_small.png', 'baseURL');
    $w   = 100;
    $h   = 21;
}

$metadata['additional_fields']['image'] = <<<IMAGE
<image>
    <url>$img</url>
    <title>RSS: $title - $description</title>
    <link>{$serendipity['baseURL']}</link>
    <width>$w</width>
    <height>$h</height>
</image>
IMAGE;

$metadata['additional_fields']['image_atom1.0'] = <<<IMAGE
<icon>$img</icon>
IMAGE;

$metadata['additional_fields']['image_rss1.0_channel'] = '<image rdf:resource="' . $img . '" />';
$metadata['additional_fields']['image_rss1.0_rdf'] = <<<IMAGE
<image rdf:about="$img">
    <url>$img</url>
    <title>RSS: $title - $description</title>
    <link>{$serendipity['baseURL']}</link>
    <width>$w</width>
    <height>$h</height>
</image>
IMAGE;

// Now, if set, stitch together any fields that have been configured in the syndication plugin.
// First, do some sanity checks
$metadata['additional_fields']['channel'] = '';
$rssFields = array('feedManagingEditor' => 'managingEditor', 'feedWebmaster' => 'webMaster', 'feedTtl' => 'ttl', 'feedPubDate' => 'pubDate');
foreach( $rssFields as $configName => $field) {
    $fieldValue = serendipity_get_config_var($configName);

    switch($field) {
        case 'pubDate':
            if (serendipity_db_bool($fieldValue)) {
                $fieldValue  = gmdate('D, d M Y H:i:s \G\M\T', $entries[0]['last_modified']);
            } else {
                $fieldValue  = '';
            }
            break;

        // Each new RSS-field which needs rewrite of its content should get its own case here.

        default:
            break;
    }

    if ($fieldValue != '') {
        $metadata['additional_fields']['channel'] .= '<' . $field . '>' . $fieldValue . '</' . $field . '>' . "\n";
    }
}

if (is_array($metadata['additional_fields'])) {
    // Fix up array keys, because "." are not allowed when wanting to output using Smarty
    foreach($metadata['additional_fields'] AS $_aid => $af) {
        $aid = str_replace('.', '', $_aid);
        $metadata['additional_fields'][$aid] = $af;
    }
}
$metadata['fullFeed'] = serendipity_get_config_var('feedFull', false);
if ($metadata['fullFeed'] === 'client') {
    if ($_GET['fullFeed'] || $serendipity['GET']['fullFeed']) {
        $metadata['fullFeed'] = true;
    } else {
        $metadata['fullFeed'] = false;
    }
}

if ($_GET['type'] == 'content' &&
    !isset($_GET['category']) &&
    !isset($serendipity['GET']['tag']) &&
    serendipity_db_bool(serendipity_get_config_var('feedForceCustom', false)) &&
    !preg_match('@FeedBurn@i', $_SERVER['HTTP_USER_AGENT']) &&  // the hardcoded pass for feedburner is for BC. New services should just use the forceLocal-param
    !isset($_GET['forceLocal'])
   ) {
    header('Status: 302 Found');
    header('Location: ' . serendipity_get_config_var('feedCustom'));
    exit;
}
$metadata['showMail'] = serendipity_db_bool(serendipity_get_config_var('feedShowMail', $metadata['showMail']));

$file_version  = preg_replace('@[^0-9a-z\.-_]@i', '', $version);
$metadata['template_file'] = 'feed_' . $file_version . '.tpl';

serendipity_smarty_init();
serendipity_plugin_api::hook_event('frontend_rss', $metadata);

$self_url = ($_SERVER['HTTPS'] == 'on' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . serendipity_specialchars($_SERVER['REQUEST_URI']);
if (!is_array($entries)) {
    $entries = array();
}

if ($entries[0]['last_modified']) {
    $gm_modified = gmdate('Y-m-d\TH:i:s\Z', serendipity_serverOffsetHour($entries[0]['last_modified']));
} else {
    $gm_modified = gmdate('Y-m-d\TH:i:s\Z', serendipity_serverOffsetHour());
}

serendipity_printEntries_rss($entries, $version, $comments, $metadata['fullFeed'], $metadata['showMail']);

$namespace_hook   = 'frontend_display:unknown:namespace';
$once_display_dat = '';
switch($version) {
    case 'opml1.0':
        $namespace_hook = 'frontend_display:opml-1.0:namespace';
        break;

    case '0.91':
        $namespace_hook = 'frontend_display:rss-0.91:namespace';
        break;

    case '1.0':
        $namespace_hook = 'frontend_display:rss-1.0:namespace';
        serendipity_plugin_api::hook_event('frontend_display:rss-1.0:once', $entries);
        $once_display_dat = $entries['display_dat'] ?? '';
        unset($entries['display_dat']);
        break;

    case '2.0':
        $namespace_hook = 'frontend_display:rss-2.0:namespace';
        break;

    case 'atom0.3':
        $namespace_hook = 'frontend_display:atom-0.3:namespace';
        break;

    case 'atom1.0':
        // For people wanting extra RFC compliance
        // header('Content-Type: application/atom+xml; charset=utf-8');
        $namespace_hook = 'frontend_display:atom-1.0:namespace';
        break;
}

serendipity_plugin_api::hook_event($namespace_hook, $entries);
$namespace_display_dat = $entries['display_dat'] ?? null;
$channel_display_dat = $entries['channel_dat'] ?? null;
unset($entries['display_dat']);
unset($entries['channel_dat']);
$serendipity['smarty']->assignByRef('metadata', $metadata);
$serendipity['smarty']->assignByRef('entries', $entries);
$serendipity['smarty']->assignByRef('namespace_display_dat', $namespace_display_dat);
$serendipity['smarty']->assignByRef('channel_display_dat', $channel_display_dat);
$serendipity['smarty']->assignByRef('once_display_dat', $once_display_dat);

$serendipity['smarty']->assign(
    array(
        'is_comments'           => $comments,
        'last_modified'         => $gm_modified,
        'self_url'              => $self_url,
    )
);
$serendipity['smarty']->display($metadata['template_file']);

/* vim: set sts=4 ts=4 expandtab : */
