private unsafe char[] GetCanonicalPath(char[] dest, ref int pos, UriFormat formatAs)
{
if (InFact(Flags.FirstSlashAbsent))
dest[pos++] = '/';
if (_info.Offset.Path == _info.Offset.Query)
return dest;
int end = pos;
int dosPathIdx = SecuredPathIndex;
// Note that unescaping and then escaping back is not transitive hence not safe.
// We are vulnerable due to the way the UserEscaped flag is processed.
// Try to unescape only needed chars.
if (formatAs == UriFormat.UriEscaped)
{
if (InFact(Flags.ShouldBeCompressed))
{
_string.CopyTo(_info.Offset.Path, dest, end, _info.Offset.Query - _info.Offset.Path);
end += (_info.Offset.Query - _info.Offset.Path);
// If the path was found as needed compression and contains escaped characters, unescape only
// interesting characters (safe)
if (_syntax.InFact(UriSyntaxFlags.UnEscapeDotsAndSlashes) && InFact(Flags.PathNotCanonical)
&& !IsImplicitFile)
{
fixed (char* pdest = dest)
{
UnescapeOnly(pdest, pos, ref end, '.', '/',
_syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) ? '\\' : c_DummyChar);
}
}
}
else
{
//Note: we may produce non escaped Uri characters on the wire
if (InFact(Flags.E_PathNotCanonical) && NotAny(Flags.UserEscaped))
{
string str = _string;
// Check on not canonical disk designation like C|\, should be rare, rare case
if (dosPathIdx != 0 && str[dosPathIdx + _info.Offset.Path - 1] == '|')
{
str = str.Remove(dosPathIdx + _info.Offset.Path - 1, 1);
str = str.Insert(dosPathIdx + _info.Offset.Path - 1, ":");
}
dest = UriHelper.EscapeString(str, _info.Offset.Path, _info.Offset.Query, dest, ref end, true,
'?', '#', IsImplicitFile ? c_DummyChar : '%');
}
else
{
_string.CopyTo(_info.Offset.Path, dest, end, _info.Offset.Query - _info.Offset.Path);
end += (_info.Offset.Query - _info.Offset.Path);
}
}
}
else
{
_string.CopyTo(_info.Offset.Path, dest, end, _info.Offset.Query - _info.Offset.Path);
end += (_info.Offset.Query - _info.Offset.Path);
if (InFact(Flags.ShouldBeCompressed))
{
// If the path was found as needed compression and contains escaped characters,
// unescape only interesting characters (safe)
if (_syntax.InFact(UriSyntaxFlags.UnEscapeDotsAndSlashes) && InFact(Flags.PathNotCanonical)
&& !IsImplicitFile)
{
fixed (char* pdest = dest)
{
UnescapeOnly(pdest, pos, ref end, '.', '/',
_syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) ? '\\' : c_DummyChar);
}
}
}
}
// Here we already got output data as copied into dest array
// We just may need more processing of that data
//
// if this URI is using 'non-proprietary' disk drive designation, convert to MS-style
//
// (path is already >= 3 chars if recognized as a DOS-like)
//
if (dosPathIdx != 0 && dest[dosPathIdx + pos - 1] == '|')
dest[dosPathIdx + pos - 1] = ':';
if (InFact(Flags.ShouldBeCompressed))
{
// It will also convert back slashes if needed
dest = Compress(dest, (ushort)(pos + dosPathIdx), ref end, _syntax);
if (dest[pos] == '\\')
dest[pos] = '/';
// Escape path if requested and found as not fully escaped
if (formatAs == UriFormat.UriEscaped && NotAny(Flags.UserEscaped) && InFact(Flags.E_PathNotCanonical))
{
//Note: Flags.UserEscaped check is solely based on trusting the user
string srcString = new string(dest, pos, end - pos);
dest = UriHelper.EscapeString(srcString, 0, end - pos, dest, ref pos, true, '?', '#',
IsImplicitFile ? c_DummyChar : '%');
end = pos;
}
}
else if (_syntax.InFact(UriSyntaxFlags.ConvertPathSlashes) && InFact(Flags.BackslashInPath))
{
for (int i = pos; i < end; ++i)
if (dest[i] == '\\') dest[i] = '/';
}
if (formatAs != UriFormat.UriEscaped && InFact(Flags.PathNotCanonical))
{
UnescapeMode mode;
if (InFact(Flags.PathNotCanonical))
{
switch (formatAs)
{
case V1ToStringUnescape:
mode = (InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape)
| UnescapeMode.V1ToStringFlag;
if (IsImplicitFile)
mode &= ~UnescapeMode.Unescape;
break;
case UriFormat.Unescaped:
mode = IsImplicitFile ? UnescapeMode.CopyOnly
: UnescapeMode.Unescape | UnescapeMode.UnescapeAll;
break;
default: // UriFormat.SafeUnescaped
mode = InFact(Flags.UserEscaped) ? UnescapeMode.Unescape : UnescapeMode.EscapeUnescape;
if (IsImplicitFile)
mode &= ~UnescapeMode.Unescape;
break;
}
}
else
{
mode = UnescapeMode.CopyOnly;
}
char[] dest1 = new char[dest.Length];
Buffer.BlockCopy(dest, 0, dest1, 0, end << 1);
fixed (char* pdest = dest1)
{
dest = UriHelper.UnescapeString(pdest, pos, end, dest, ref pos, '?', '#', c_DummyChar, mode,
_syntax, false);
}
}
else
{
pos = end;
}
return dest;
}