| Line 1... | Line 1... | 
      
        | 1 | <?php | 1 | <?php | 
      
        | 2 | # vim:et:ts=3:sts=3:sw=3:fdm=marker: | - |  | 
      
        | 3 |   | - |  | 
      
        | 4 | // WebSVN - Subversion repository viewing via the web using PHP | 2 | // WebSVN - Subversion repository viewing via the web using PHP | 
      
        | 5 | // Copyright © 2004-2006 Tim Armes, Matt Sicker | 3 | // Copyright (C) 2004-2006 Tim Armes | 
      
        | 6 | // | 4 | // | 
      
        | 7 | // This program is free software; you can redistribute it and/or modify | 5 | // This program is free software; you can redistribute it and/or modify | 
      
        | 8 | // it under the terms of the GNU General Public License as published by | 6 | // it under the terms of the GNU General Public License as published by | 
      
        | 9 | // the Free Software Foundation; either version 2 of the License, or | 7 | // the Free Software Foundation; either version 2 of the License, or | 
      
        | 10 | // (at your option) any later version. | 8 | // (at your option) any later version. | 
      
        | 11 | // | 9 | // | 
      
        | 12 | // This program is distributed in the hope that it will be useful, | 10 | // This program is distributed in the hope that it will be useful, | 
      
        | 13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
      
        | 14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
      
        | 15 | // GNU General Public License for more details. | 13 | // GNU General Public License for more details. | 
      
        | 16 | // | 14 | // | 
      
        | 17 | // You should have received a copy of the GNU General Public License | 15 | // You should have received a copy of the GNU General Public License | 
      
        | 18 | // along with this program; if not, write to the Free Software | 16 | // along with this program; if not, write to the Free Software | 
      
        | 19 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 
      
        | 20 | // | 18 | // | 
      
        | 21 | // -- | 19 | // -- | 
      
        | 22 | // | 20 | // | 
      
        | 23 | // rss.php | 21 | // rss.php | 
      
        | 24 | // | 22 | // | 
      
        | 25 | // Creates an rss feed for the given repository number | 23 | // Creates an rss feed for the given repository number | 
      
        | 26 |   | 24 |   | 
      
        | 27 | include("include/feedcreator.class.php"); | 25 | require_once 'include/setup.php'; | 
      
        | 28 |   | - |  | 
      
        | 29 | require_once("include/setup.inc"); | 26 | require_once 'include/svnlook.php'; | 
      
        | 30 | require_once("include/svnlook.inc"); | 27 | require_once 'include/utils.php'; | 
      
        | 31 | require_once("include/utils.inc"); | 28 | require_once 'include/template.php'; | 
      
        | 32 | require_once("include/template.inc"); | 29 | require_once 'include/bugtraq.php'; | 
      
        | - |  | 30 |   | 
      
        | - |  | 31 | $max = (int)@$_REQUEST['max']; | 
      
        | - |  | 32 | if ($max == 0) | 
      
        | - |  | 33 | 	$max = $config->getRssMaxEntries(); | 
      
        | - |  | 34 | $quiet = (isset($_REQUEST['quiet'])); | 
      
        | 33 |   | 35 |   | 
      
        | - |  | 36 | // Find the base URL name | 
      
        | - |  | 37 | if ($config->multiViews) { | 
      
        | - |  | 38 | 	$baseurl = ''; | 
      
        | - |  | 39 | } else { | 
      
        | 34 | $isDir = (@$_REQUEST["isdir"] == 1)?1:0; | 40 | 	$baseurl = dirname($_SERVER['PHP_SELF']); | 
      
        | - |  | 41 | 	if ($baseurl != '' && $baseurl != DIRECTORY_SEPARATOR && $baseurl != '\\' && $baseurl != '/') { | 
      
        | - |  | 42 | 		$baseurl .= '/'; | 
      
        | - |  | 43 | 	} else { | 
      
        | - |  | 44 | 		$baseurl = '/'; | 
      
        | - |  | 45 | 	} | 
      
        | - |  | 46 | } | 
      
        | 35 |   | 47 |   | 
      
        | - |  | 48 | if ($path == '' || $path[0] != '/') { | 
      
        | 36 | $maxmessages = 20; | 49 | 	$ppath = '/'.$path; | 
      
        | - |  | 50 | } else { | 
      
        | - |  | 51 | 	$ppath = $path; | 
      
        | - |  | 52 | } | 
      
        | 37 |   | 53 |   | 
      
        | 38 | // Find the base URL name | 54 | if (!$rep) { | 
      
        | 39 | if ($config->multiViews) | 55 | 	http_response_code(404); | 
      
        | 40 | { | - |  | 
      
        | 41 |    $baseurl = ""; | 56 | 	print 'Unable to access resource at path: '.xml_entities($path); | 
      
        | 42 | } | - |  | 
      
        | 43 | else | 57 | 	exit; | 
      
        | 44 | { | 58 | } | 
      
        | 45 |    $baseurl = dirname($_SERVER["PHP_SELF"]); | 59 | // Make sure that the user has full access to the specified directory | 
      
        | 46 |    if ($baseurl != "" && $baseurl != DIRECTORY_SEPARATOR && $baseurl != "\\" && $baseurl != "/" ) | 60 | if (!$rep->hasReadAccess($path, false)) { | 
      
        | 47 |       $baseurl .= "/"; | 61 | 	http_response_code(403); | 
      
        | - |  | 62 | 	print 'Unable to access resource at path: '.xml_entities($path); | 
      
        | 48 |    else | 63 | 	exit; | 
      
        | 49 |       $baseurl = "/"; | - |  | 
      
        | 50 | } | 64 | } | 
      
        | 51 |   | 65 |   | 
      
        | - |  | 66 | // If there's no revision info, go to the lastest revision for this path | 
      
        | 52 | $svnrep = new SVNRepository($rep); | 67 | $svnrep = new SVNRepository($rep); | 
      
        | - |  | 68 | $history = $svnrep->getLog($path, $rev, '', false, $max, $peg, !$quiet); | 
      
        | - |  | 69 | if (!$history) { | 
      
        | - |  | 70 | 	http_response_code(404); | 
      
        | - |  | 71 | 	echo $lang['NOPATH']; | 
      
        | - |  | 72 | 	exit; | 
      
        | - |  | 73 | } | 
      
        | 53 |   | 74 |   | 
      
        | 54 | if ($path == "" || $path{0} != "/") | - |  | 
      
        | 55 |    $ppath = "/".$path; | - |  | 
      
        | 56 | else | - |  | 
      
        | 57 |    $ppath = $path; | - |  | 
      
        | 58 |   | - |  | 
      
        | 59 | // Make sure that the user has full access to the specified directory | 75 | header('Content-Type: application/xml; charset=utf-8'); | 
      
        | 60 | if (!$rep->hasReadAccess($path, false)) | - |  | 
      
        | 61 |    exit; | - |  | 
      
        | 62 |   | 76 |   | 
      
        | 63 | $url = $config->getURL($rep, $path, "log"); | 77 | if ($rep->isRssCachingEnabled()) { | 
      
        | 64 | $listurl = $config->getURL($rep, $path, "dir"); | 78 | 	// Filename for storing a cached RSS feed for this particular path/revision | 
      
        | - |  | 79 | 	$cache = $locwebsvnreal.DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR.strtr($rep->getDisplayName().$path, '\\/:*?"<>|.', '__________').($peg ? '@'.$peg : '').($rev ? '_r'.$rev : '').'m'.$max.($quiet ? 'q' : '').'.rss.xml'; | 
      
        | - |  | 80 | 	// If a recent-enough cached version exists, use it and avoid the work below | 
      
        | - |  | 81 | 	if (file_exists($cache) && filemtime($cache) >= $history->curEntry->committime) { | 
      
        | - |  | 82 | 		readfile($cache); | 
      
        | - |  | 83 | 		exit(); | 
      
        | - |  | 84 | 	} | 
      
        | - |  | 85 | } | 
      
        | 65 |   | 86 |   | 
      
        | 66 | // If there's no revision info, go to the lastest revision for this path | - |  | 
      
        | 67 | $history = $svnrep->getLog($path, $rev, "", false, 20); | 87 | $bugtraq = new Bugtraq($rep, $svnrep, $ppath); | 
      
        | 68 | $youngest = $history->entries[0]->rev; | - |  | 
      
        | 69 |   | 88 |   | 
      
        | 70 | // Cachename reflecting full path to and rev for rssfeed. Must end with xml to work | 89 | // Generate RSS 2.0 feed | 
      
        | 71 | $cachename = strtr(getFullURL($listurl), ":/\\?", "____"); | 90 | $rss = '<?xml version="1.0" encoding="utf-8"?>'; | 
      
        | 72 | $cachename = $locwebsvnreal.DIRECTORY_SEPARATOR."cache".DIRECTORY_SEPARATOR.$cachename.@$_REQUEST["rev"]."_rssfeed.xml"; | 91 | $rss .= '<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel>'; | 
      
        | 73 |   | - |  | 
      
        | 74 | $rss = new UniversalFeedCreator(); | 92 | $rss .= '<title>'.escape($rep->getDisplayName()).($path ? ' – '.$path : '').'</title>'; | 
      
        | 75 | $rss->useCached("RSS2.0", $cachename); | 93 | $rss .= '<description>'.escape($lang['RSSFEEDTITLE']).' – '.escape($repname).'</description>'; | 
      
        | 76 | $rss->title = $rep->getDisplayName(); | 94 | $rss .= '<lastBuildDate>'.date('r').'</lastBuildDate>'; // RFC 2822 date format | 
      
        | 77 | $rss->description = "${lang["RSSFEEDTITLE"]} - $repname"; | 95 | $rss .= '<generator>WebSVN '.$vars['version'].'</generator>'; | 
      
        | 78 | $rss->link = html_entity_decode(getFullURL($baseurl.$listurl)); | 96 | $rss .= '<language>'.$lang['LANGUAGETAG'].'</language>'; | 
      
        | 79 | $rss->syndicationURL = $rss->link; | 97 | // URL for matching WebSVN log page | 
      
        | - |  | 98 | $rss .= '<link>'.getFullURL($baseurl.$config->getURL($rep, $path, 'log').(@$_REQUEST['isdir'] == 1 ? 'isdir=1&' : '').'max='.$max.'&'.createRevAndPegString($passrev, $peg)).'</link>'; | 
      
        | 80 | $rss->xslStyleSheet = ""; //required for UniversalFeedCreator since 1.7 | 99 | // URL where this original RSS feed can be found | 
      
        | 81 | $rss->cssStyleSheet = ""; //required for UniversalFeedCreator since 1.7 | 100 | $rss .= '<atom:link href="'.escape(getFullURL($_SERVER['REQUEST_URI'])).'" rel="self" type="application/rss+xml" />'."\n"; | 
      
        | 82 |   | - |  | 
      
        | 83 | //$divbox = "<div>"; | - |  | 
      
        | 84 | //$divfont = "<span>"; | 101 | if (is_array($history->entries)) { | 
      
        | 85 |   | - |  | 
      
        | 86 | foreach ($history->entries as $r) | 102 | 	$itemURL = $baseurl.$config->getURL($rep, $path, 'revision'); | 
      
        | 87 | { | - |  | 
      
        | 88 |    $thisrev = $r->rev; | 103 | 	if (@$_REQUEST['isdir'] == 1 || $path == '' || $path == '/') | 
      
        | 89 |    $changes = $r->mods; | 104 | 		$itemURL .= 'isdir=1&'; | 
      
        | 90 |    $files = count($changes); | 105 | 	foreach ($history->entries as $r) { | 
      
        | 91 |   | - |  | 
      
        | 92 |    // Add the trailing slash if we need to (svnlook history doesn't return trailing slashes!) | 106 | 		$wordLimit = 10; // Display only up to the first 10 words of the log message | 
      
        | 93 |    $rpath = $r->path; | 107 | 		$title = trim(explode('\n', $r->msg)[0]); | 
      
        | 94 |    if ($isDir && $rpath{strlen($rpath) - 1} != "/") | 108 | 		$title = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $title); | 
      
        | 95 |       $rpath .= "/"; | - |  | 
      
        | 96 |     | - |  | 
      
        | 97 |    // Find the parent path (or the whole path if it's already a directory) | 109 | 		$words = explode(' ', $title, $wordLimit + 1); | 
      
        | 98 |    $pos = strrpos($rpath, "/"); | 110 | 		if (count($words) > $wordLimit) { | 
      
        | 99 |    $parent = substr($rpath, 0, $pos + 1); | 111 | 			$title = implode(' ', array_slice($words, 0, $wordLimit)).' ...'; | 
      
        | 100 |   | 112 | 		} | 
      
        | 101 |    $url = $config->getURL($rep, $parent, "dir"); | 113 | 		$title = $lang['REV'].' '.$r->rev.' '.mb_convert_encoding('–', 'UTF-8', 'HTML-ENTITIES').' '.$title; | 
      
        | 102 |     | - |  | 
      
        | 103 |    $desc = $r->msg; | 114 | 		$description = '<div><strong>'.$r->author; | 
      
        | 104 |    $item = new FeedItem(); | 115 | 		if (!$quiet) { | 
      
        | - |  | 116 | 			$description .= ' '.mb_convert_encoding('–', 'UTF-8', 'HTML-ENTITIES').' '.count($r->mods).' '.$lang['FILESMODIFIED']; | 
      
        | 105 |     | 117 | 		} | 
      
        | 106 |    // For the title, we show the first 10 words of the description | 118 | 		$description .= '</strong><br/>'.nl2br($bugtraq->replaceIDs(create_anchors(str_replace('<', '<', $r->msg)))).'</div>'; | 
      
        | 107 |    $pos = 0; | 119 | 		if (!$quiet) { | 
      
        | 108 |    $len = strlen($desc); | 120 | 			usort($r->mods, 'SVNLogEntry_compare'); | 
      
        | 109 |    for ($i = 0; $i < 10; $i++) | 121 | 			foreach ($r->mods as $modifiedResource) { | 
      
        | 110 |    { | - |  | 
      
        | 111 |       if ($pos >= $len) break; | 122 | 				switch ($modifiedResource->action) { | 
      
        | 112 |        | - |  | 
      
        | 113 |       $pos = strpos($desc, " ", $pos); | 123 | 					case 'A': $description .= '+ '; break; | 
      
        | 114 |        | - |  | 
      
        | 115 |       if ($pos === FALSE) break; | 124 | 					case 'M': $description .= '~ '; break; | 
      
        | 116 |       $pos++; | 125 | 					case 'D': $description .= 'x '; break; | 
      
        | 117 |    } | 126 | 				} | 
      
        | 118 |     | - |  | 
      
        | 119 |    if ($pos !== FALSE) | 127 | 				$description .= $modifiedResource->path; | 
      
        | 120 |    { | - |  | 
      
        | 121 |       $sdesc = substr($desc, 0, $pos) . "..."; | 128 | 				if ($modifiedResource->copyfrom != '') { | 
      
        | - |  | 129 | 					$description .= ' <i>(copied from '.$modifiedResource->copyfrom.'@'.$modifiedResource->copyrev.')</i>'; | 
      
        | 122 |    } | 130 | 				} | 
      
        | 123 |    else | - |  | 
      
        | 124 |    { | - |  | 
      
        | 125 |       $sdesc = $desc; | 131 | 				$description .= '<br />'; | 
      
        | 126 |    } | 132 | 			} | 
      
        | 127 |     | - |  | 
      
        | 128 |    if ($desc == "") $sdesc = "${lang["REV"]} $thisrev"; | - |  | 
      
        | 129 |     | 133 | 		} | 
      
        | 130 |    $item->title = "$sdesc"; | - |  | 
      
        | 131 |    $item->link = html_entity_decode(getFullURL($baseurl."${url}rev=$thisrev&sc=$showchanged")); | - |  | 
      
        | 132 |    $item->description = "<div><strong>${lang["REV"]} $thisrev - ".$r->author."</strong> ($files ${lang["FILESMODIFIED"]})</div><div>".nl2br(create_anchors($desc))."</div>"; | - |  | 
      
        | 133 |   | 134 |   | 
      
        | 134 |    if ($showchanged) | 135 | 		// skip items with no access | 
      
        | 135 |    { | - |  | 
      
        | 136 |       foreach ($changes as $file) | - |  | 
      
        | 137 |       { | - |  | 
      
        | 138 |          switch ($file->action) | 136 | 		if ($r->committime) { | 
      
        | 139 |          { | - |  | 
      
        | 140 |             case "A": | 137 | 			$rss .= '<item>'; | 
      
        | 141 |                $item->description .= "+ ".$file->path."<br />";   | 138 | 			$rss .= '<pubDate>'.date('r', $r->committime).'</pubDate>'; | 
      
        | 142 |                break; | - |  | 
      
        | 143 |                                       | - |  | 
      
        | 144 |             case "M": | - |  | 
      
        | 145 |                $item->description .= "~ ".$file->path."<br />";   | 139 | 			$rss .= '<dc:creator>'.escape($r->author).'</dc:creator>'; | 
      
        | 146 |                break; | - |  | 
      
        | 147 |                                       | 140 | 			$rss .= '<title>'.escape($title).'</title>'; | 
      
        | 148 |     | - |  | 
      
        | 149 |             case "D": | - |  | 
      
        | 150 |                $item->description .= "-".$file->path."<br />";   | 141 | 			$rss .= '<description>'.escape($description).'</description>'; | 
      
        | 151 |                break; | - |  | 
      
        | 152 |                                       | 142 | 			$itemLink = getFullURL($itemURL.createRevAndPegString($r->rev,$peg)); | 
      
        | 153 |          } | - |  | 
      
        | 154 |       } | - |  | 
      
        | 155 |    } | - |  | 
      
        | 156 |   | - |  | 
      
        | 157 |    $item->date = $r->committime; | 143 | 			$rss .= '<link>'.$itemLink.'</link>'; | 
      
        | 158 |    $item->author = $r->author; | 144 | 			$rss .= '<guid>'.$itemLink.'</guid>'; | 
      
        | 159 |       | - |  | 
      
        | 160 |    $rss->addItem($item); | 145 | 			$rss .= '</item>'."\n"; | 
      
        | 161 | } | 146 | 		} | 
      
        | 162 |   | 147 | 	} | 
      
        | 163 | // valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1, MBOX, OPML | - |  | 
      
        | 164 |   | 148 | } | 
      
        | 165 | // Save the feed | - |  | 
      
        | 166 | $rss->saveFeed("RSS2.0",$cachename, false); | - |  | 
      
        | 167 | header("Content-Type: application/xml"); | - |  | 
      
        | 168 | echo $rss->createFeed("RSS2.0"); | 149 | $rss .= '</channel></rss>'; | 
      
        | 169 |   | 150 |   | 
      
        | - |  | 151 | if ($rep->isRssCachingEnabled()) { | 
      
        | - |  | 152 | 	$file = fopen($cache, 'w+'); | 
      
        | - |  | 153 | 	if ($file) { | 
      
        | - |  | 154 | 		fputs($file, $rss); | 
      
        | - |  | 155 | 		fclose($file); | 
      
        | - |  | 156 | 		touch($cache, $history->curEntry->committime); // set timestamp to commit time | 
      
        | - |  | 157 | 	} else { | 
      
        | - |  | 158 | 		echo 'Error creating RSS cache file, please check write permissions.'; | 
      
        | 170 | ?> | 159 | 	} | 
      
        | - |  | 160 | } | 
      
        | - |  | 161 | echo $rss; |