static resource URLs are now case sensitive.

pull/4/head
kay.one 12 years ago
parent e89a35522e
commit 478caf15f8

@ -58,8 +58,8 @@ module.exports = function (grunt) {
expand: true,
src : [
'UI/Content/base.less',
'UI/Content/Overrides.less',
'UI/Series/Series.less',
'UI/Content/overrides.less',
'UI/Series/series.less',
'UI/AddSeries/addSeries.less',
'UI/Calendar/calendar.less',
'UI/Cells/cells.less',

@ -16,14 +16,14 @@ namespace NzbDrone.Api.Frontend
public string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = path.Trim(Path.DirectorySeparatorChar).ToLower();
path = path.Trim(Path.DirectorySeparatorChar);
return Path.Combine(_appFolderInfo.GetAppDataPath(), path);
}
public bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/mediacover");
return resourceUrl.StartsWith("/MediaCover");
}
}
}

@ -32,10 +32,10 @@ namespace NzbDrone.Api.Frontend
public string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = path.Trim(Path.DirectorySeparatorChar).ToLower();
path = path.Trim(Path.DirectorySeparatorChar);
return Path.Combine(_appFolderInfo.StartUpFolder, "ui", path);
return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path);
}
public bool CanHandle(string resourceUrl)

@ -5,6 +5,7 @@ using NLog;
using Nancy;
using Nancy.Responses;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend
{
@ -20,6 +21,8 @@ namespace NzbDrone.Api.Frontend
private readonly IAddCacheHeaders _addCacheHeaders;
private readonly Logger _logger;
private readonly bool _caseSensitive;
public StaticResourceProvider(IDiskProvider diskProvider,
IEnumerable<IMapHttpRequestsToDisk> requestMappers,
IAddCacheHeaders addCacheHeaders,
@ -29,11 +32,16 @@ namespace NzbDrone.Api.Frontend
_requestMappers = requestMappers;
_addCacheHeaders = addCacheHeaders;
_logger = logger;
if (!RuntimeInfo.IsProduction)
{
_caseSensitive = true;
}
}
public Response ProcessStaticResourceRequest(NancyContext context, string workingFolder)
{
var path = context.Request.Url.Path.ToLower();
var path = context.Request.Url.Path;
if (string.IsNullOrWhiteSpace(path))
{
@ -46,7 +54,7 @@ namespace NzbDrone.Api.Frontend
{
var filePath = mapper.Map(path);
if (_diskProvider.FileExists(filePath))
if (_diskProvider.FileExists(filePath, _caseSensitive))
{
var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
_addCacheHeaders.ToResponse(context.Request, response);

@ -78,12 +78,12 @@ namespace NzbDrone.Common.Test.CacheTests
{
hitCount++;
return null;
}, TimeSpan.FromMilliseconds(200));
}, TimeSpan.FromMilliseconds(300));
Thread.Sleep(10);
}
hitCount.Should().BeInRange(4, 6);
hitCount.Should().BeInRange(3, 6);
}
}

@ -1,4 +1,6 @@
using System;
using System.Diagnostics;
using System.IO;
using FluentAssertions;
using Moq;
using NUnit.Framework;
@ -69,6 +71,40 @@ namespace NzbDrone.Common.Test
ExceptionVerification.ExpectedWarns(1);
}
[Test]
public void get_actual_casing_for_none_existing_file_should_throw()
{
WindowsOnly();
Assert.Throws<DirectoryNotFoundException>(() => "C:\\InValidFolder\\invalidfile.exe".GetActualCasing());
}
[Test]
public void get_actual_casing_should_return_actual_casing_for_local_file()
{
var path = Process.GetCurrentProcess().MainModule.FileName;
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
}
[Test]
public void get_actual_casing_should_return_actual_casing_for_local_dir()
{
var path = Directory.GetCurrentDirectory();
path.ToUpper().GetActualCasing().Should().Be(path);
path.ToLower().GetActualCasing().Should().Be(path);
}
[Test]
[Explicit]
public void get_actual_casing_should_return_original_casing_for_shares()
{
var path = @"\\server\Pool\Apps";
path.GetActualCasing().Should().Be(path);
}
[Test]
public void AppDataDirectory_path_test()
@ -76,7 +112,6 @@ namespace NzbDrone.Common.Test
GetIAppDirectoryInfo().GetAppDataPath().Should().BeEquivalentTo(@"C:\NzbDrone\");
}
[Test]
public void Config_path_test()
{

@ -14,8 +14,10 @@ namespace NzbDrone.Common
DateTime GetLastFolderWrite(string path);
DateTime GetLastFileWrite(string path);
void EnsureFolder(string path);
bool FolderExists(string path, bool caseSensitive);
bool FolderExists(string path);
bool FileExists(string path);
bool FileExists(string path, bool caseSensitive);
string[] GetDirectories(string path);
string[] GetFiles(string path, SearchOption searchOption);
long GetFolderSize(string path);
@ -97,17 +99,36 @@ namespace NzbDrone.Common
public virtual bool FolderExists(string path)
{
Ensure.That(() => path).IsValidPath();
return Directory.Exists(path);
}
public virtual bool FolderExists(string path, bool caseSensitive)
{
if (caseSensitive)
{
return FolderExists(path) && path == path.GetActualCasing();
}
return FolderExists(path);
}
public virtual bool FileExists(string path)
{
Ensure.That(() => path).IsValidPath();
return File.Exists(path);
}
public virtual bool FileExists(string path, bool caseSensitive)
{
if (caseSensitive)
{
return FileExists(path) && path == path.GetActualCasing();
}
return FileExists(path);
}
public virtual string[] GetDirectories(string path)
{
Ensure.That(() => path).IsValidPath();

@ -41,18 +41,33 @@ namespace NzbDrone.Common
private static string GetProperCapitalization(DirectoryInfo dirInfo)
{
var parentDirInfo = dirInfo.Parent;
if (null == parentDirInfo)
return dirInfo.Name;
return Path.Combine(GetProperCapitalization(parentDirInfo),
parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
if (parentDirInfo == null)
{
//Drive letter
return dirInfo.Name.ToUpper();
}
return Path.Combine(GetProperCapitalization(parentDirInfo), parentDirInfo.GetDirectories(dirInfo.Name)[0].Name);
}
public static string GetActualCasing(this string filename)
public static string GetActualCasing(this string path)
{
var attributes = File.GetAttributes(path);
if (OsInfo.IsLinux || path.StartsWith("\\"))
{
var fileInfo = new FileInfo(filename);
return path;
}
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
{
return GetProperCapitalization(new DirectoryInfo(path));
}
var fileInfo = new FileInfo(path);
DirectoryInfo dirInfo = fileInfo.Directory;
return Path.Combine(GetProperCapitalization(dirInfo),
dirInfo.GetFiles(fileInfo.Name)[0].Name);
return Path.Combine(GetProperCapitalization(dirInfo), dirInfo.GetFiles(fileInfo.Name)[0].Name);
}

@ -37,7 +37,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
Subject.ConvertToLocalUrls(12, covers);
covers.Single().Url.Should().Be("/mediacover/12/banner.jpg?lastWrite=1234");
covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg?lastWrite=1234");
}
[Test]
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
Subject.ConvertToLocalUrls(12, covers);
covers.Single().Url.Should().Be("/mediacover/12/banner.jpg");
covers.Single().Url.Should().Be("/MediaCover/12/banner.jpg");
}
}

@ -95,7 +95,7 @@ namespace NzbDrone.Core.MediaCover
{
var filePath = GetCoverPath(seriesId, mediaCover.CoverType);
mediaCover.Url = @"/mediacover/" + seriesId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
mediaCover.Url = @"/MediaCover/" + seriesId + "/" + mediaCover.CoverType.ToString().ToLower() + ".jpg";
if (_diskProvider.FileExists(filePath))
{

@ -3,7 +3,7 @@ define(
[
'backbone',
'AddSeries/RootFolders/Model',
'mixins/backbone.signalr.mixin'
'Mixins/backbone.signalr.mixin'
], function (Backbone, RootFolderModel) {
var RootFolderCollection = Backbone.Collection.extend({

@ -2,70 +2,34 @@
font-family: 'Open Sans';
font-style: normal;
font-weight: 300;
src: url('/content/fonts/opensans-light.eot');
src: url('/Content/fonts/OpenSans-Light.eot');
src: local('Open Sans Light'),
local('OpenSans-Light'),
url('/content/fonts/opensans-light.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-light.woff') format('woff'),
url('/content/fonts/opensans-light.ttf') format('truetype');
url('/Content/fonts/OpenSans-Light.eot?#iefix') format('embedded-opentype'),
url('/Content/fonts/OpenSans-Light.woff') format('woff'),
url('/Content/fonts/OpenSans-Light.ttf') format('truetype');
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 400;
src: url('/content/fonts/opensans-regular.eot');
src: url('/Content/fonts/OpenSans-Regular.eot');
src: local('Open Sans'),
local('OpenSans'),
url('/content/fonts/opensans-regular.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-regular.woff') format('woff'),
url('/content/fonts/opensans-regular.ttf') format('truetype')
url('/Content/fonts/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
url('/Content/fonts/OpenSans-Regular.woff') format('woff'),
url('/Content/fonts/OpenSans-Regular.ttf') format('truetype')
}
@font-face {
font-family: 'Open Sans';
font-style: normal;
font-weight: 600;
src: url('/content/fonts/opensans-semibold.eot');
src: local('Open Sans Semibold'),
local('OpenSans-Semibold'),
url('/content/fonts/opensans-semibold.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-semibold.woff') format('woff'),
url('/content/fonts/opensans-semibold.ttf') format('truetype')
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 300;
src: url('/content/fonts/opensans-lightitalic.eot');
src: local('Open Sans Light Italic'),
local('OpenSansLight-Italic'),
url('/content/fonts/opensans-lightitalic.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-lightitalic.woff') format('woff'),
url('/content/fonts/opensans-lightitalic.ttf') format('truetype')
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 400;
src: url('/content/fonts/opensans-italic.eot');
src: local('Open Sans Italic'),
local('OpenSans-Italic'),
url('/content/fonts/opensans-italic.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-italic.woff') format('woff'),
url('/content/fonts/opensans-italic.woff.ttf') format('truetype')
}
@font-face {
font-family: 'Open Sans';
font-style: italic;
font-weight: 600;
src: url('/content/fonts/opensans-semibolditalic.eot');
src: local('Open Sans Semibold Italic'),
local('OpenSans-SemiboldItalic'),
url('/content/fonts/opensans-semibolditalic.eot?#iefix') format('embedded-opentype'),
url('/content/fonts/opensans-semibolditalic.woff') format('woff'),
url('/content/fonts/opensans-semibolditalic.ttf') format('truetype')
src: url('/Content/fonts/OpenSans-SemiBold.eot');
src: local('Open Sans SemiBold'),
local('OpenSans-SemiBold'),
url('/Content/fonts/OpenSans-SemiBold.eot?#iefix') format('embedded-opentype'),
url('/Content/fonts/OpenSans-SemiBold.woff') format('woff'),
url('/Content/fonts/OpenSans-SemiBold.ttf') format('truetype')
}

@ -2,7 +2,7 @@
body {
background-color : #1c1c1c;
background-image : url('../content/images/pattern.png');
background-image : url('../Content/Images/pattern.png');
margin-bottom : 100px;
p {
font-size : 0.9em;

@ -5,6 +5,6 @@ define(
'handlebars'
], function (Handlebars) {
Handlebars.registerHelper('defaultImg', function () {
return new Handlebars.SafeString('onerror=this.src=\'/content/images/poster-dark.jpg\';');
return new Handlebars.SafeString('onerror=this.src=\'/Content/Images/poster-dark.jpg\';');
});
});

@ -3,17 +3,17 @@
<head runat="server">
<title>NzbDrone</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<link href="/Content/Bootstrap.css" rel='stylesheet' type='text/css'/>
<link href="/Content/bootstrap.css" rel='stylesheet' type='text/css'/>
<link href="/Content/bootstrap.toggle-switch.css" rel='stylesheet' type='text/css'/>
<link href="/Content/Messenger/messenger.css" rel='stylesheet' type='text/css'/>
<link href="/Content/Messenger/messenger.future.css" rel='stylesheet' type='text/css'/>
<link href="/Content/fullcalendar.css" rel='stylesheet' type='text/css'>
<link href="/Content/base.css" rel='stylesheet' type='text/css'/>
<link href="/Cells/Cells.css" rel='stylesheet' type='text/css'>
<link href="/Series/Series.css" rel='stylesheet' type='text/css'/>
<link href="/Cells/cells.css" rel='stylesheet' type='text/css'>
<link href="/Series/series.css" rel='stylesheet' type='text/css'/>
<link href="/Logs/logs.css" rel='stylesheet' type='text/css'/>
<link href="/Settings/settings.css" rel='stylesheet' type='text/css'/>
<link href="/AddSeries/Addseries.css" rel='stylesheet' type='text/css'/>
<link href="/AddSeries/addSeries.css" rel='stylesheet' type='text/css'/>
<link href="/Calendar/calendar.css" rel='stylesheet' type='text/css'/>
<link href="/Content/overrides.css" rel='stylesheet' type='text/css'/>

@ -73,7 +73,7 @@ require.config({
backbone: {
deps :
[
'mixins/backbone.ajax',
'Mixins/backbone.ajax',
'underscore',
'$'
],
@ -87,7 +87,7 @@ require.config({
'backbone.deepmodel': {
deps:
[
'mixins/underscore.mixin.deepExtend'
'Mixins/underscore.mixin.deepExtend'
]
},
@ -96,7 +96,7 @@ require.config({
[
'backbone',
'Handlebars/backbone.marionette.templates',
'mixins/AsNamedView'
'Mixins/AsNamedView'
],
exports: 'Marionette',

Loading…
Cancel
Save