From 20dc8adac071315d5a3a5f2b25c1a556811ae69e Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 25 Nov 2017 22:55:50 -0500 Subject: [PATCH] New: Validate NZBs before sending to download client --- .../Download/NzbValidationServiceFixture.cs | 43 +++++++ src/NzbDrone.Core.Test/Files/Nzbs/NoFiles.nzb | 6 + src/NzbDrone.Core.Test/Files/Nzbs/NotNzb.nzb | 102 +++++++++++++++++ .../Files/Nzbs/ValidNzb.nzb | 105 ++++++++++++++++++ .../NzbDrone.Core.Test.csproj | 10 ++ .../Clients/Blackhole/UsenetBlackhole.cs | 3 +- .../DownloadStation/UsenetDownloadStation.cs | 3 +- .../Download/Clients/NzbVortex/NzbVortex.cs | 3 +- .../Download/Clients/Nzbget/Nzbget.cs | 3 +- .../Download/Clients/Sabnzbd/Sabnzbd.cs | 3 +- .../Download/InvalidNzbException.cs | 29 +++++ .../Download/NzbValidationService.cs | 45 ++++++++ .../Download/UsenetClientBase.cs | 5 + src/NzbDrone.Core/NzbDrone.Core.csproj | 2 + 14 files changed, 357 insertions(+), 5 deletions(-) create mode 100644 src/NzbDrone.Core.Test/Download/NzbValidationServiceFixture.cs create mode 100644 src/NzbDrone.Core.Test/Files/Nzbs/NoFiles.nzb create mode 100644 src/NzbDrone.Core.Test/Files/Nzbs/NotNzb.nzb create mode 100644 src/NzbDrone.Core.Test/Files/Nzbs/ValidNzb.nzb create mode 100644 src/NzbDrone.Core/Download/InvalidNzbException.cs create mode 100644 src/NzbDrone.Core/Download/NzbValidationService.cs diff --git a/src/NzbDrone.Core.Test/Download/NzbValidationServiceFixture.cs b/src/NzbDrone.Core.Test/Download/NzbValidationServiceFixture.cs new file mode 100644 index 000000000..557a28ae0 --- /dev/null +++ b/src/NzbDrone.Core.Test/Download/NzbValidationServiceFixture.cs @@ -0,0 +1,43 @@ +using System.IO; +using NUnit.Framework; +using NzbDrone.Core.Download; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Download +{ + [TestFixture] + public class NzbValidationServiceFixture : CoreTest + { + private byte[] GivenNzbFile(string name) + { + return File.ReadAllBytes(GetTestPath("Files/Nzbs/" + name + ".nzb")); + } + + [Test] + public void should_throw_on_invalid_nzb() + { + var filename = "NotNzb"; + var fileContent = GivenNzbFile(filename); + + Assert.Throws(() => Subject.Validate(filename, fileContent)); + } + + [Test] + public void should_throw_when_no_files() + { + var filename = "NoFiles"; + var fileContent = GivenNzbFile(filename); + + Assert.Throws(() => Subject.Validate(filename, fileContent)); + } + + [Test] + public void should_validate_nzb() + { + var filename = "ValidNzb"; + var fileContent = GivenNzbFile(filename); + + Subject.Validate(filename, fileContent); + } + } +} diff --git a/src/NzbDrone.Core.Test/Files/Nzbs/NoFiles.nzb b/src/NzbDrone.Core.Test/Files/Nzbs/NoFiles.nzb new file mode 100644 index 000000000..8a38bcf83 --- /dev/null +++ b/src/NzbDrone.Core.Test/Files/Nzbs/NoFiles.nzb @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Files/Nzbs/NotNzb.nzb b/src/NzbDrone.Core.Test/Files/Nzbs/NotNzb.nzb new file mode 100644 index 000000000..8ad464218 --- /dev/null +++ b/src/NzbDrone.Core.Test/Files/Nzbs/NotNzb.nzb @@ -0,0 +1,102 @@ + + + + + alt.binaries.teevee + + + ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769 + + + + + alt.binaries.teevee + + + 405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54 + + + + + alt.binaries.teevee + + + 1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144 + 48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M + 0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968 + 5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B + B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A + O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240 + d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ + 05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9 + 245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19 + H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K + T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR + Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6 + S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T + 041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl + 1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl + 76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49 + 12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18 + 59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519 + 44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6 + oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1 + 1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms + u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57 + XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9 + X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1 + p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G + 50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D + FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N + q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ + 364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH + M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150 + 189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S + 189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5 + 5-L.555$139r45100-S23-59859@54844694q2.3EY9b + 641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k + 1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi + 0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5 + 77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1 + m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX + f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU + H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK + 9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3 + 0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B + l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm + A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB + V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne + A86H2P415S689$568152-025O45V@s079644915.Dd57p0 + 31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib + b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV + D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E + Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f + 7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f + 3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91 + j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5 + A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4 + 5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a + 04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x + K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1 + 0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B + 220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr + 5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5 + 6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb + 5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784 + 4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ + z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512 + F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z + 2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3 + + + + + alt.binaries.teevee + + + 16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1 + iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv + r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS + + + \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Files/Nzbs/ValidNzb.nzb b/src/NzbDrone.Core.Test/Files/Nzbs/ValidNzb.nzb new file mode 100644 index 000000000..138b0cd55 --- /dev/null +++ b/src/NzbDrone.Core.Test/Files/Nzbs/ValidNzb.nzb @@ -0,0 +1,105 @@ + + + + + + alt.binaries.teevee + + + ZQ9h749E781168561i4J0Q6-01m6Q3185@2894t-767038L.Pg7769 + + + + + alt.binaries.teevee + + + 405Z5Y4066010l377VP1k6$U4873W933@f32Bs90575538201.pj54 + + + + + alt.binaries.teevee + + + 1x9894417$M.1s25279485O1s1Fi95Z1_18Z554u440@D1k0854_134551.0794144 + 48JYp$W18B2R1s2rI24EG7$907$r89875n60@8xK3374080716.115545M + 0U93471uI59Y781x77Q8-4286308-4aU35$07-179z@u90567568251.4zgUW968 + 5119x6417a.s06F$1k46$2q89298-C0@G7C-7811268.bK9x00B + B8$1_h0b64Z14-16_O6$ESw481L421n9agj7731k@414.473581-K$4.0Zd5A + O-4731$tn71v05623J9GT.yc22O975111dR01r58065p@Da1G9L33q74h3095.5X240 + d9R03J$07w75945Z556197z50F0w.0-5.x9$58311S@J0-v50033110.4a440EYJ + 05e650149.5r1Hk$E0Bko7G5B.1107mz8l17PS8F@vr816$S6T19245w.042B9 + 245Xy0w4o$tN6428321b.n1816Q1n95bE0816Y@q-qv7E12k.F3672H.16E19 + H681i185g64H23101kP125z41101O91P384l@E9n597k05j798D94X.2ezz1K + T18.6136787.HLJ806.8$Si49m0459445101Z15-5@b80M7.788598D.gXu201cR + Vdl8H243Go28j1o865772039416v2@090a20-v365N5S7qf.G225s6 + S9769892v956069345.0TN.i05R@I04825Gt2706N.BAj1DT1T + 041800q6F28q44365799m5CQ4D43895@1Bf6268z_Q20F.045JXl + 1c-e034z4l$9K45i44218ss25$X5_5R-1i76$40-71P@Xt691t8B686Fgv.VBSl + 76l441W.R146a5368ed02cp_44171410hT.l@Z98.k.70X9c.5mZ1w49 + 12D035G5745-KO43wZ9920ttr1338@V7d871S2-t04t8520.uQ18 + 59V4O77211HA1f5T8h1-53952zV-55294K4M04v@kS878H3g4z.B5561.L330519 + 44-yi1-79$751944J7094$y7-y49994440d86cSn@5C82v-1O9N.wk8wMb6 + oF7Wj3$Ydh7e030oD4.e81JM464O791495lJ@Pm058Qt4-G8Wv.T1i1a6O1 + 1T7_71M9d10F2.5953VP.11.4h75L@5049bBn384.14Ms + u8601765028G662749SD41j0m57651Zq70u1@J5281423406375.z.6PDSx57 + XY0476$R87Y16g2n45OO335541589V140R026j@y2q9296x7f23C.sqK71b9 + X7N3440l08B9T5940na4Ls397-T2.P5M12241525J57@r44O419p594M6G4I.d66RQ1 + p4148978k45.t88w2K9886H4223y5553T7$7p287TN@N8e1T98b_0.mo55a14G + 50U0a9iP07$A66010-51h55w386f@c$42$S96V57F5u0Y.6UDV35D + FnKN4n2749v958xa36J2570506414D293S@8H1A1X490$z3bv.ut6KQ4N + q4$d0$X8x6rm85m0Ewh307m255N@t2C7484zq870u.1RLndQ + 364U4342$5I242404oH90-1W3c0t16705057m650Cq9f@K32rE5297347130W.UNs8evbH + M3081U097-r06Y.yy9-1A538001B27f@L2834Y80c7b1075.Dy150 + 189585554.NS66E5D840N4Yq5m07NC1n@51L0393057L528n.k1Mc3j0S + 189048V505q89216C149I5f$53x-T@0V9i8n7o95.I.Z1lBJ5 + 5-L.555$139r45100-S23-59859@54844694q2.3EY9b + 641655313y0.Z002L0g39AZ11716U-uX015PI5.v6y@veS44H89Js91903K8.P3MAvk4k + 1C8f-yz-U-b20.610.0P1M-6Z5418i229160865010s1@M7l210D48Nc.nB0sPmi + 0653$L0.58749-1U_1PS95-1h9gQ145@0117y0-1x1p-h94.za18yc5 + 77-Mo3-a6514904987865.K0W710G4HB9237@501F7910J6j50-Bh.6cHx1 + m4I47082655rz$b7P751u9W679475F.89p@f.o.XZv5O7y.855rgXX + f075$y56E57d.t11787.0$6D155735M_w89-Y57q2@x0t5H91021wZ52Vh.1h7vabU + H7U1331Ad7718$Y69T-q3w4$l247HV49s985J@vi800i0004p.YD5oK + 9nr786955Ker.M583315CoJ1-W65a817-704@IN-wU12$M1E0g466.5sMJ3 + 0.3R9mN.n2_V086N0-4.Z5gAgZo@ey3G316U382o537.f51Ed5B + l106Z1-N411r7j44197l628r.b5Uwc55@k4-Cl_n5xc.1B.xZbNm + A91LT1X591x81.TI4130N$555A57q0@L70-p5qa50.40GB + V5$765JR6503w0-K63099R615736843G$Qj0ev@mz776wM86445N0.4I56ne + A86H2P415S689$568152-025O45V@s079644915.Dd57p0 + 31x5o36q14y9554L42882X0Q10e360Z64W4K9Onx38D@5g1509788414q.Y8wib + b$6795157EX1044V964e14-Y9E68614O94C@4061937876$f5.6.19tV + D00v8X$b80m93181273J-g076Qj2p79867v5d9689Rb2@r0592.v900.j43E050E + Tf78L4e535.o86PK0S.M2R3-66012814z@q-5j89Y29J214Y902.53Ra0f + 7i01.23411-lQW0212-Er260e9.N5e256jx243EX@91-T.15v40K5Hj.Fo1f + 3A$H7m63$i595.4713vv0A4$A7Lk7Jsq@0cM0Tw4107f.B520.q5Z91 + j572m$3h87LS$37167Wp10k41541.T779-Fn@V53C11045619xJ.52.0PnnX4v5 + A.2d4599a720rk2IB32h0X523MjTL415v89706-7Z45y@R4746-B106358.t3g62r4 + 5q6100961jM-G9F7t755x366zxc102M1SdMF@7394521p651X1I.AL05545a + 04e851111$12u2213-80VR133125B@7x8865M4hQ9$5.1N345x + K2476D3600-73B4W363$008s888980421f27125V$q0@0Zc0a56-m7550.1637vAr1 + 0306u425024v448ZeCE3Q9825m9th1858@5648018-H0.2k7J4.12k0B + 220u4SK433564Cr2l004t0wP888545779g@19j360863S$55559m.70V7Ndr + 5u1q051C5Qq8Z9Iy$Z.5.1510NY.S2565n@7m.5-09$z235p74.8kW5 + 6F472C8nh2621_X0C1093P7n39643b5p2f76s60r@1T55203qQY6.wZml1Vb + 5qC4568844767324-o8i05983-0f.n4.y.OBZ41f@q36B50684KU66.0R1784 + 4P0g470-F59307aDf.JF070Xx959648dO3y00463J6s@71P$D961$C0.11.I096sQ + z5kod75077z01w11-A5h.wiG550.J5-p756$81.Db@5l01K49h3K.Ok4R5512 + F3JX28.B8h90T0075-08001X5w611V071@D75X9263$6$9f.OT050p5Z + 2B8sT.A650z101514671183y47977219.M4211xYp@0b0021p736BX92.B0lSm4J3 + + + + + alt.binaries.teevee + + + 16ND-8I545Pq-s107t0h07g8908870711@K401476783.5.0mFs1 + iYdZ2D11089F310711.ci-O7O4KG03@260c03388O84Kd.GCEgv + r63cDD59Mg1c95738Sn75085O4X7823V1@16V6-b87O21S1937O.lw17o1VS + + + \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 2a9160394..269ceb014 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -166,6 +166,7 @@ + @@ -347,6 +348,15 @@ + + Always + + + Always + + + Always + diff --git a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs index 0e999b67b..bb9dddc85 100644 --- a/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs +++ b/src/NzbDrone.Core/Download/Clients/Blackhole/UsenetBlackhole.cs @@ -24,8 +24,9 @@ namespace NzbDrone.Core.Download.Clients.Blackhole IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, logger) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) { _scanWatchFolder = scanWatchFolder; diff --git a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs index a2fb7f783..31d6c0f71 100644 --- a/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs +++ b/src/NzbDrone.Core/Download/Clients/DownloadStation/UsenetDownloadStation.cs @@ -32,9 +32,10 @@ namespace NzbDrone.Core.Download.Clients.DownloadStation IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger ) - : base(httpClient, configService, diskProvider, remotePathMappingService, logger) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) { _dsInfoProxy = dsInfoProxy; _dsTaskProxy = dsTaskProxy; diff --git a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs index f4a8db185..064b04d10 100644 --- a/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs +++ b/src/NzbDrone.Core/Download/Clients/NzbVortex/NzbVortex.cs @@ -23,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, logger) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs index d682b6a6c..9229bafe0 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/Nzbget.cs @@ -26,8 +26,9 @@ namespace NzbDrone.Core.Download.Clients.Nzbget IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, logger) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs index 93ba3df81..3ae3674d2 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/Sabnzbd.cs @@ -23,8 +23,9 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger) - : base(httpClient, configService, diskProvider, remotePathMappingService, logger) + : base(httpClient, configService, diskProvider, remotePathMappingService, nzbValidationService, logger) { _proxy = proxy; } diff --git a/src/NzbDrone.Core/Download/InvalidNzbException.cs b/src/NzbDrone.Core/Download/InvalidNzbException.cs new file mode 100644 index 000000000..0b2c37de8 --- /dev/null +++ b/src/NzbDrone.Core/Download/InvalidNzbException.cs @@ -0,0 +1,29 @@ +using System; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Download +{ + public class InvalidNzbException : NzbDroneException + { + public InvalidNzbException(string message, params object[] args) : base(message, args) + { + } + + public InvalidNzbException(string message) : base(message) + { + } + + public InvalidNzbException(string message, Exception innerException, params object[] args) : base(message, innerException, args) + { + } + + public InvalidNzbException(string message, Exception innerException) : base(message, innerException) + { + } + + public InvalidNzbException(string message, string nzbName) + : base($"{message} [{0}]", nzbName) + { + } + } +} diff --git a/src/NzbDrone.Core/Download/NzbValidationService.cs b/src/NzbDrone.Core/Download/NzbValidationService.cs new file mode 100644 index 000000000..14d4bc19d --- /dev/null +++ b/src/NzbDrone.Core/Download/NzbValidationService.cs @@ -0,0 +1,45 @@ +using System.IO; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using NzbDrone.Common.Extensions; + +namespace NzbDrone.Core.Download +{ + public interface IValidateNzbs + { + void Validate(string filename, byte[] fileContent); + } + + public class NzbValidationService : IValidateNzbs + { + public void Validate(string filename, byte[] fileContent) + { + var reader = new StreamReader(new MemoryStream(fileContent)); + + using (var xmlTextReader = XmlReader.Create(reader, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore, IgnoreComments = true })) + { + var xDoc = XDocument.Load(xmlTextReader); + var nzb = xDoc.Root; + + if (nzb == null) + { + throw new InvalidNzbException("No Root element", filename); + } + + if (!nzb.Name.LocalName.Equals("nzb")) + { + throw new InvalidNzbException("Invalid root element", filename); + } + + var ns = nzb.Name.Namespace; + var files = nzb.Elements(ns + "file").ToList(); + + if (files.Empty()) + { + throw new InvalidNzbException("No files", filename); + } + } + } + } +} diff --git a/src/NzbDrone.Core/Download/UsenetClientBase.cs b/src/NzbDrone.Core/Download/UsenetClientBase.cs index d5aed8f21..9b1057048 100644 --- a/src/NzbDrone.Core/Download/UsenetClientBase.cs +++ b/src/NzbDrone.Core/Download/UsenetClientBase.cs @@ -16,15 +16,18 @@ namespace NzbDrone.Core.Download where TSettings : IProviderConfig, new() { protected readonly IHttpClient _httpClient; + private readonly IValidateNzbs _nzbValidationService; protected UsenetClientBase(IHttpClient httpClient, IConfigService configService, IDiskProvider diskProvider, IRemotePathMappingService remotePathMappingService, + IValidateNzbs nzbValidationService, Logger logger) : base(configService, diskProvider, remotePathMappingService, logger) { _httpClient = httpClient; + _nzbValidationService = nzbValidationService; } public override DownloadProtocol Protocol => DownloadProtocol.Usenet; @@ -70,6 +73,8 @@ namespace NzbDrone.Core.Download throw new ReleaseDownloadException(remoteAlbum.Release, "Downloading nzb failed", ex); } + _nzbValidationService.Validate(filename, nzbData); + _logger.Info("Adding report [{0}] to the queue.", remoteAlbum.Release.Title); return AddFromNzbFile(remoteAlbum, filename, nzbData); } diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index b64168632..222ef0b0c 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -388,6 +388,8 @@ + +