@ -15,19 +15,27 @@ namespace Jellyfin.SocketSharp
{
int ap = header . IndexOf ( attr ) ;
if ( ap = = - 1 )
{
return null ;
}
ap + = attr . Length ;
if ( ap > = header . Length )
{
return null ;
}
char ending = header [ ap ] ;
if ( ending ! = '"' )
{
ending = ' ' ;
}
int end = header . IndexOf ( ending , ap + 1 ) ;
if ( end = = - 1 )
{
return ending = = '"' ? null : header . Substring ( ap ) ;
}
return header . Substring ( ap + 1 , end - ap - 1 ) ;
}
@ -36,7 +44,9 @@ namespace Jellyfin.SocketSharp
{
string boundary = GetParameter ( ContentType , "; boundary=" ) ;
if ( boundary = = null )
{
return ;
}
using ( var requestStream = InputStream )
{
@ -124,7 +134,9 @@ namespace Jellyfin.SocketSharp
{
string v = "\"" + value + "\"" ;
if ( v . Length > 20 )
{
v = v . Substring ( 0 , 16 ) + "...\"" ;
}
string msg = string . Format ( "A potentially dangerous Request.{0} value was " +
"detected from the client ({1}={2})." , name , key , v ) ;
@ -135,21 +147,23 @@ namespace Jellyfin.SocketSharp
static void ValidateNameValueCollection ( string name , QueryParamCollection coll )
{
if ( coll = = null )
{
return ;
}
foreach ( var pair in coll )
{
var key = pair . Name ;
var val = pair . Value ;
if ( val ! = null & & val . Length > 0 & & IsInvalidString ( val ) )
{
ThrowValidationException ( name , key , val ) ;
}
}
}
internal static bool IsInvalidString ( string val )
{
return IsInvalidString ( val , out var validationFailureIndex ) ;
}
= > IsInvalidString ( val , out var validationFailureIndex ) ;
internal static bool IsInvalidString ( string val , out int validationFailureIndex )
{
@ -157,7 +171,9 @@ namespace Jellyfin.SocketSharp
int len = val . Length ;
if ( len < 2 )
{
return false ;
}
char current = val [ 0 ] ;
for ( int idx = 1 ; idx < len ; idx + + )
@ -195,10 +211,15 @@ namespace Jellyfin.SocketSharp
bool IsContentType ( string ct , bool starts_with )
{
if ( ct = = null | | ContentType = = null ) return false ;
if ( ct = = null | | ContentType = = null )
{
return false ;
}
if ( starts_with )
{
return StrUtils . StartsWith ( ContentType , ct , true ) ;
}
return string . Equals ( ContentType , ct , StringComparison . OrdinalIgnoreCase ) ;
}
@ -231,7 +252,9 @@ namespace Jellyfin.SocketSharp
break ;
}
else
{
value . Append ( ( char ) c ) ;
}
}
if ( c = = - 1 )
{
@ -240,22 +263,26 @@ namespace Jellyfin.SocketSharp
}
}
else if ( c = = '&' )
{
AddRawKeyValue ( form , key , value ) ;
}
else
{
key . Append ( ( char ) c ) ;
}
}
if ( c = = - 1 )
{
AddRawKeyValue ( form , key , value ) ;
}
}
}
}
}
void AddRawKeyValue ( WebROCollection form , StringBuilder key , StringBuilder value )
static void AddRawKeyValue ( WebROCollection form , StringBuilder key , StringBuilder value )
{
string decodedKey = WebUtility . UrlDecode ( key . ToString ( ) ) ;
form . Add ( decodedKey ,
WebUtility . UrlDecode ( value . ToString ( ) ) ) ;
form . Add ( WebUtility . UrlDecode ( key . ToString ( ) ) , WebUtility . UrlDecode ( value . ToString ( ) ) ) ;
key . Length = 0 ;
value . Length = 0 ;
@ -271,7 +298,9 @@ namespace Jellyfin.SocketSharp
foreach ( var pair in this )
{
if ( result . Length > 0 )
{
result . Append ( '&' ) ;
}
var key = pair . Name ;
if ( key ! = null & & key . Length > 0 )
@ -314,33 +343,52 @@ namespace Jellyfin.SocketSharp
public override int Read ( byte [ ] buffer , int dest_offset , int count )
{
if ( buffer = = null )
{
throw new ArgumentNullException ( nameof ( buffer ) ) ;
}
if ( dest_offset < 0 )
{
throw new ArgumentOutOfRangeException ( nameof ( dest_offset ) , "< 0" ) ;
}
if ( count < 0 )
{
throw new ArgumentOutOfRangeException ( nameof ( count ) , "< 0" ) ;
}
int len = buffer . Length ;
if ( dest_offset > len )
{
throw new ArgumentException ( "destination offset is beyond array size" ) ;
}
// reordered to avoid possible integer overflow
if ( dest_offset > len - count )
{
throw new ArgumentException ( "Reading would overrun buffer" ) ;
}
if ( count > end - position )
{
count = ( int ) ( end - position ) ;
}
if ( count < = 0 )
{
return 0 ;
}
s . Position = position ;
int result = s . Read ( buffer , dest_offset , count ) ;
if ( result > 0 )
{
position + = result ;
}
else
{
position = end ;
}
return result ;
}
@ -348,14 +396,20 @@ namespace Jellyfin.SocketSharp
public override int ReadByte ( )
{
if ( position > = end )
{
return - 1 ;
}
s . Position = position ;
int result = s . ReadByte ( ) ;
if ( result < 0 )
{
position = end ;
}
else
{
position + + ;
}
return result ;
}
@ -380,7 +434,9 @@ namespace Jellyfin.SocketSharp
long virt = real - offset ;
if ( virt < 0 | | virt > Length )
{
throw new ArgumentException ( ) ;
}
position = s . Seek ( real , SeekOrigin . Begin ) ;
return position ;
@ -410,7 +466,9 @@ namespace Jellyfin.SocketSharp
set
{
if ( value > Length )
throw new ArgumentOutOfRangeException ( ) ;
{
throw new ArgumentOutOfRangeException ( nameof ( value ) ) ;
}
position = Seek ( value , SeekOrigin . Begin ) ;
}
@ -438,7 +496,7 @@ namespace Jellyfin.SocketSharp
public static readonly CultureInfo InvariantCulture = CultureInfo . InvariantCulture ;
}
internal s ealed class StrUtils
internal s tatic class StrUtils
{
public static bool StartsWith ( string str1 , string str2 , bool ignore_case )
{
@ -455,11 +513,15 @@ namespace Jellyfin.SocketSharp
{
int l2 = str2 . Length ;
if ( l2 = = 0 )
{
return true ;
}
int l1 = str1 . Length ;
if ( l2 > l1 )
{
return false ;
}
var comparison = ignore_case ? StringComparison . OrdinalIgnoreCase : StringComparison . Ordinal ;
return str1 . IndexOf ( str2 , comparison ) = = str1 . Length - str2 . Length - 1 ;
@ -493,7 +555,7 @@ namespace Jellyfin.SocketSharp
Encoding encoding ;
StringBuilder sb ;
const byte HYPHEN = ( byte ) '-' , LF = ( byte ) '\n' , CR = ( byte ) '\r' ;
const byte LF = ( byte ) '\n' , CR = ( byte ) '\r' ;
// See RFC 2046
// In the case of multipart entities, in which one or more different
@ -520,7 +582,7 @@ namespace Jellyfin.SocketSharp
sb = new StringBuilder ( ) ;
}
string ReadLine ( )
private string ReadLine ( )
{
// CRLF or LF are ok as line endings.
bool got_cr = false ;
@ -543,58 +605,86 @@ namespace Jellyfin.SocketSharp
}
if ( got_cr )
{
sb . Length - - ;
}
return sb . ToString ( ) ;
}
static string GetContentDispositionAttribute ( string l , string name )
private static string GetContentDispositionAttribute ( string l , string name )
{
int idx = l . IndexOf ( name + "=\"" ) ;
if ( idx < 0 )
{
return null ;
}
int begin = idx + name . Length + "=\"" . Length ;
int end = l . IndexOf ( '"' , begin ) ;
if ( end < 0 )
{
return null ;
}
if ( begin = = end )
return "" ;
{
return string . Empty ;
}
return l . Substring ( begin , end - begin ) ;
}
string GetContentDispositionAttributeWithEncoding ( string l , string name )
private string GetContentDispositionAttributeWithEncoding ( string l , string name )
{
int idx = l . IndexOf ( name + "=\"" ) ;
if ( idx < 0 )
{
return null ;
}
int begin = idx + name . Length + "=\"" . Length ;
int end = l . IndexOf ( '"' , begin ) ;
if ( end < 0 )
{
return null ;
}
if ( begin = = end )
return "" ;
{
return string . Empty ;
}
string temp = l . Substring ( begin , end - begin ) ;
byte [ ] source = new byte [ temp . Length ] ;
for ( int i = temp . Length - 1 ; i > = 0 ; i - - )
{
source [ i ] = ( byte ) temp [ i ] ;
}
return encoding . GetString ( source , 0 , source . Length ) ;
}
bool ReadBoundary ( )
private bool ReadBoundary ( )
{
try
{
string line = ReadLine ( ) ;
while ( line = = "" )
while ( line = = string . Empty )
{
line = ReadLine ( ) ;
}
if ( line [ 0 ] ! = '-' | | line [ 1 ] ! = '-' )
{
return false ;
}
if ( ! StrUtils . EndsWith ( line , boundary , false ) )
{
return true ;
}
}
catch
{
@ -603,25 +693,31 @@ namespace Jellyfin.SocketSharp
return false ;
}
string ReadHeaders ( )
private string ReadHeaders ( )
{
string s = ReadLine ( ) ;
if ( s = = "" )
if ( s . Length = = 0 )
{
return null ;
}
return s ;
}
bool CompareBytes ( byte [ ] orig , byte [ ] other )
private static bool CompareBytes ( byte [ ] orig , byte [ ] other )
{
for ( int i = orig . Length - 1 ; i > = 0 ; i - - )
{
if ( orig [ i ] ! = other [ i ] )
{
return false ;
}
}
return true ;
}
long MoveToNextBoundary ( )
private long MoveToNextBoundary ( )
{
long retval = 0 ;
bool got_cr = false ;
@ -631,13 +727,18 @@ namespace Jellyfin.SocketSharp
while ( true )
{
if ( c = = - 1 )
{
return - 1 ;
}
if ( state = = 0 & & c = = LF )
{
retval = data . Position - 1 ;
if ( got_cr )
{
retval - - ;
}
state = 1 ;
c = data . ReadByte ( ) ;
}
@ -650,7 +751,9 @@ namespace Jellyfin.SocketSharp
{
c = data . ReadByte ( ) ;
if ( c = = - 1 )
{
return - 1 ;
}
if ( c ! = '-' )
{
@ -662,7 +765,9 @@ namespace Jellyfin.SocketSharp
int nread = data . Read ( buffer , 0 , buffer . Length ) ;
int bl = buffer . Length ;
if ( nread ! = bl )
{
return - 1 ;
}
if ( ! CompareBytes ( boundary_bytes , buffer ) )
{
@ -673,6 +778,7 @@ namespace Jellyfin.SocketSharp
data . Position + + ;
got_cr = false ;
}
c = data . ReadByte ( ) ;
continue ;
}
@ -690,12 +796,16 @@ namespace Jellyfin.SocketSharp
data . Position + + ;
got_cr = false ;
}
c = data . ReadByte ( ) ;
continue ;
}
data . Position = retval + 2 ;
if ( got_cr )
{
data . Position + + ;
}
break ;
}
else
@ -711,7 +821,9 @@ namespace Jellyfin.SocketSharp
public Element ReadNextElement ( )
{
if ( at_eof | | ReadBoundary ( ) )
{
return null ;
}
var elem = new Element ( ) ;
string header ;
@ -734,19 +846,27 @@ namespace Jellyfin.SocketSharp
elem . Start = start ;
long pos = MoveToNextBoundary ( ) ;
if ( pos = = - 1 )
{
return null ;
}
elem . Length = pos - start ;
return elem ;
}
static string StripPath ( string path )
private static string StripPath ( string path )
{
if ( path = = null | | path . Length = = 0 )
{
return path ;
}
if ( path . IndexOf ( ":\\" ) ! = 1 & & ! path . StartsWith ( "\\\\" ) )
if ( path . IndexOf ( ":\\" , StringComparison . Ordinal ) ! = 1
& & ! path . StartsWith ( "\\\\" , StringComparison . Ordinal ) )
{
return path ;
}
return path . Substring ( path . LastIndexOf ( '\\' ) + 1 ) ;
}
}