init commit
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public abstract class BaseInputStream : Stream
|
||||
{
|
||||
private bool closed;
|
||||
|
||||
public sealed override bool CanRead => !closed;
|
||||
|
||||
public sealed override bool CanSeek => false;
|
||||
|
||||
public sealed override bool CanWrite => false;
|
||||
|
||||
public sealed override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
closed = true;
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public sealed override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int num = offset;
|
||||
try
|
||||
{
|
||||
int num2 = offset + count;
|
||||
while (num < num2)
|
||||
{
|
||||
int num3 = ReadByte();
|
||||
if (num3 != -1)
|
||||
{
|
||||
buffer[num++] = (byte)num3;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (num == offset)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return num - offset;
|
||||
}
|
||||
|
||||
public sealed override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public abstract class BaseOutputStream : Stream
|
||||
{
|
||||
private bool closed;
|
||||
|
||||
public sealed override bool CanRead => false;
|
||||
|
||||
public sealed override bool CanSeek => false;
|
||||
|
||||
public sealed override bool CanWrite => !closed;
|
||||
|
||||
public sealed override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
closed = true;
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public sealed override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public sealed override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int num = offset + count;
|
||||
for (int i = offset; i < num; i++)
|
||||
{
|
||||
WriteByte(buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Write(params byte[] buffer)
|
||||
{
|
||||
Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
public override void WriteByte(byte b)
|
||||
{
|
||||
Write(new byte[1] { b }, 0, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class FilterStream : Stream
|
||||
{
|
||||
protected readonly Stream s;
|
||||
|
||||
public override bool CanRead => s.CanRead;
|
||||
|
||||
public override bool CanSeek => s.CanSeek;
|
||||
|
||||
public override bool CanWrite => s.CanWrite;
|
||||
|
||||
public override long Length => s.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return s.Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
s.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
public FilterStream(Stream s)
|
||||
{
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Platform.Dispose(s);
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
s.Flush();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
return s.Seek(offset, origin);
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
s.SetLength(value);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return s.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
return s.ReadByte();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
s.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
s.WriteByte(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class MemoryInputStream : MemoryStream
|
||||
{
|
||||
public sealed override bool CanWrite => false;
|
||||
|
||||
public MemoryInputStream(byte[] buffer)
|
||||
: base(buffer, writable: false)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class MemoryOutputStream : MemoryStream
|
||||
{
|
||||
public sealed override bool CanRead => false;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
internal class NullOutputStream : BaseOutputStream
|
||||
{
|
||||
public override void WriteByte(byte b)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
[Serializable]
|
||||
public class PemGenerationException : Exception
|
||||
{
|
||||
public PemGenerationException()
|
||||
{
|
||||
}
|
||||
|
||||
public PemGenerationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public PemGenerationException(string message, Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public class PemHeader
|
||||
{
|
||||
private string name;
|
||||
|
||||
private string val;
|
||||
|
||||
public virtual string Name => name;
|
||||
|
||||
public virtual string Value => val;
|
||||
|
||||
public PemHeader(string name, string val)
|
||||
{
|
||||
this.name = name;
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return GetHashCode(name) + 31 * GetHashCode(val);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!(obj is PemHeader))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PemHeader pemHeader = (PemHeader)obj;
|
||||
if (object.Equals(name, pemHeader.name))
|
||||
{
|
||||
return object.Equals(val, pemHeader.val);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int GetHashCode(string s)
|
||||
{
|
||||
return s?.GetHashCode() ?? 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public class PemObject : PemObjectGenerator
|
||||
{
|
||||
private string type;
|
||||
|
||||
private IList headers;
|
||||
|
||||
private byte[] content;
|
||||
|
||||
public string Type => type;
|
||||
|
||||
public IList Headers => headers;
|
||||
|
||||
public byte[] Content => content;
|
||||
|
||||
public PemObject(string type, byte[] content)
|
||||
: this(type, Platform.CreateArrayList(), content)
|
||||
{
|
||||
}
|
||||
|
||||
public PemObject(string type, IList headers, byte[] content)
|
||||
{
|
||||
this.type = type;
|
||||
this.headers = Platform.CreateArrayList(headers);
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public PemObject Generate()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public interface PemObjectGenerator
|
||||
{
|
||||
PemObject Generate();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public interface PemObjectParser
|
||||
{
|
||||
object ParseObject(PemObject obj);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Utilities.Encoders;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public class PemReader
|
||||
{
|
||||
private const string BeginString = "-----BEGIN ";
|
||||
|
||||
private const string EndString = "-----END ";
|
||||
|
||||
private readonly TextReader reader;
|
||||
|
||||
public TextReader Reader => reader;
|
||||
|
||||
public PemReader(TextReader reader)
|
||||
{
|
||||
if (reader == null)
|
||||
{
|
||||
throw new ArgumentNullException("reader");
|
||||
}
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
public PemObject ReadPemObject()
|
||||
{
|
||||
string text = reader.ReadLine();
|
||||
if (text != null && Platform.StartsWith(text, "-----BEGIN "))
|
||||
{
|
||||
text = text.Substring("-----BEGIN ".Length);
|
||||
int num = text.IndexOf('-');
|
||||
string type = text.Substring(0, num);
|
||||
if (num > 0)
|
||||
{
|
||||
return LoadObject(type);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PemObject LoadObject(string type)
|
||||
{
|
||||
string text = "-----END " + type;
|
||||
IList list = Platform.CreateArrayList();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
string text2;
|
||||
while ((text2 = reader.ReadLine()) != null && Platform.IndexOf(text2, text) == -1)
|
||||
{
|
||||
int num = text2.IndexOf(':');
|
||||
if (num == -1)
|
||||
{
|
||||
stringBuilder.Append(text2.Trim());
|
||||
continue;
|
||||
}
|
||||
string text3 = text2.Substring(0, num).Trim();
|
||||
if (Platform.StartsWith(text3, "X-"))
|
||||
{
|
||||
text3 = text3.Substring(2);
|
||||
}
|
||||
string val = text2.Substring(num + 1).Trim();
|
||||
list.Add(new PemHeader(text3, val));
|
||||
}
|
||||
if (text2 == null)
|
||||
{
|
||||
throw new IOException(text + " not found");
|
||||
}
|
||||
if (stringBuilder.Length % 4 != 0)
|
||||
{
|
||||
throw new IOException("base64 data appears to be truncated");
|
||||
}
|
||||
return new PemObject(type, list, Base64.Decode(stringBuilder.ToString()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Org.BouncyCastle.Utilities.Encoders;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO.Pem;
|
||||
|
||||
public class PemWriter
|
||||
{
|
||||
private const int LineLength = 64;
|
||||
|
||||
private readonly TextWriter writer;
|
||||
|
||||
private readonly int nlLength;
|
||||
|
||||
private char[] buf = new char[64];
|
||||
|
||||
public TextWriter Writer => writer;
|
||||
|
||||
public PemWriter(TextWriter writer)
|
||||
{
|
||||
if (writer == null)
|
||||
{
|
||||
throw new ArgumentNullException("writer");
|
||||
}
|
||||
this.writer = writer;
|
||||
nlLength = Platform.NewLine.Length;
|
||||
}
|
||||
|
||||
public int GetOutputSize(PemObject obj)
|
||||
{
|
||||
int num = 2 * (obj.Type.Length + 10 + nlLength) + 6 + 4;
|
||||
if (obj.Headers.Count > 0)
|
||||
{
|
||||
foreach (PemHeader header in obj.Headers)
|
||||
{
|
||||
num += header.Name.Length + ": ".Length + header.Value.Length + nlLength;
|
||||
}
|
||||
num += nlLength;
|
||||
}
|
||||
int num2 = (obj.Content.Length + 2) / 3 * 4;
|
||||
return num + (num2 + (num2 + 64 - 1) / 64 * nlLength);
|
||||
}
|
||||
|
||||
public void WriteObject(PemObjectGenerator objGen)
|
||||
{
|
||||
PemObject pemObject = objGen.Generate();
|
||||
WritePreEncapsulationBoundary(pemObject.Type);
|
||||
if (pemObject.Headers.Count > 0)
|
||||
{
|
||||
foreach (PemHeader header in pemObject.Headers)
|
||||
{
|
||||
writer.Write(header.Name);
|
||||
writer.Write(": ");
|
||||
writer.WriteLine(header.Value);
|
||||
}
|
||||
writer.WriteLine();
|
||||
}
|
||||
WriteEncoded(pemObject.Content);
|
||||
WritePostEncapsulationBoundary(pemObject.Type);
|
||||
}
|
||||
|
||||
private void WriteEncoded(byte[] bytes)
|
||||
{
|
||||
bytes = Base64.Encode(bytes);
|
||||
for (int i = 0; i < bytes.Length; i += buf.Length)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j != buf.Length && i + j < bytes.Length; j++)
|
||||
{
|
||||
buf[j] = (char)bytes[i + j];
|
||||
}
|
||||
writer.WriteLine(buf, 0, j);
|
||||
}
|
||||
}
|
||||
|
||||
private void WritePreEncapsulationBoundary(string type)
|
||||
{
|
||||
writer.WriteLine("-----BEGIN " + type + "-----");
|
||||
}
|
||||
|
||||
private void WritePostEncapsulationBoundary(string type)
|
||||
{
|
||||
writer.WriteLine("-----END " + type + "-----");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class PushbackStream : FilterStream
|
||||
{
|
||||
private int buf = -1;
|
||||
|
||||
public PushbackStream(Stream s)
|
||||
: base(s)
|
||||
{
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (buf != -1)
|
||||
{
|
||||
int result = buf;
|
||||
buf = -1;
|
||||
return result;
|
||||
}
|
||||
return base.ReadByte();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (buf != -1 && count > 0)
|
||||
{
|
||||
buffer[offset] = (byte)buf;
|
||||
buf = -1;
|
||||
return 1;
|
||||
}
|
||||
return base.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public virtual void Unread(int b)
|
||||
{
|
||||
if (buf != -1)
|
||||
{
|
||||
throw new InvalidOperationException("Can only push back one byte");
|
||||
}
|
||||
buf = b & 0xFF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
[Serializable]
|
||||
public class StreamOverflowException : IOException
|
||||
{
|
||||
public StreamOverflowException()
|
||||
{
|
||||
}
|
||||
|
||||
public StreamOverflowException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public StreamOverflowException(string message, Exception exception)
|
||||
: base(message, exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public sealed class Streams
|
||||
{
|
||||
private const int BufferSize = 512;
|
||||
|
||||
private Streams()
|
||||
{
|
||||
}
|
||||
|
||||
public static void Drain(Stream inStr)
|
||||
{
|
||||
byte[] array = new byte[512];
|
||||
while (inStr.Read(array, 0, array.Length) > 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ReadAll(Stream inStr)
|
||||
{
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
PipeAll(inStr, memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] ReadAllLimited(Stream inStr, int limit)
|
||||
{
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
PipeAllLimited(inStr, limit, memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
public static int ReadFully(Stream inStr, byte[] buf)
|
||||
{
|
||||
return ReadFully(inStr, buf, 0, buf.Length);
|
||||
}
|
||||
|
||||
public static int ReadFully(Stream inStr, byte[] buf, int off, int len)
|
||||
{
|
||||
int i;
|
||||
int num;
|
||||
for (i = 0; i < len; i += num)
|
||||
{
|
||||
num = inStr.Read(buf, off + i, len - i);
|
||||
if (num < 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public static void PipeAll(Stream inStr, Stream outStr)
|
||||
{
|
||||
byte[] array = new byte[512];
|
||||
int count;
|
||||
while ((count = inStr.Read(array, 0, array.Length)) > 0)
|
||||
{
|
||||
outStr.Write(array, 0, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static long PipeAllLimited(Stream inStr, long limit, Stream outStr)
|
||||
{
|
||||
byte[] array = new byte[512];
|
||||
long num = 0L;
|
||||
int num2;
|
||||
while ((num2 = inStr.Read(array, 0, array.Length)) > 0)
|
||||
{
|
||||
if (limit - num < num2)
|
||||
{
|
||||
throw new StreamOverflowException("Data Overflow");
|
||||
}
|
||||
num += num2;
|
||||
outStr.Write(array, 0, num2);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
public static void WriteBufTo(MemoryStream buf, Stream output)
|
||||
{
|
||||
buf.WriteTo(output);
|
||||
}
|
||||
|
||||
public static int WriteBufTo(MemoryStream buf, byte[] output, int offset)
|
||||
{
|
||||
int num = (int)buf.Length;
|
||||
buf.WriteTo(new MemoryStream(output, offset, num, writable: true));
|
||||
return num;
|
||||
}
|
||||
|
||||
public static void WriteZeroes(Stream outStr, long count)
|
||||
{
|
||||
byte[] buffer = new byte[512];
|
||||
while (count > 512)
|
||||
{
|
||||
outStr.Write(buffer, 0, 512);
|
||||
count -= 512;
|
||||
}
|
||||
outStr.Write(buffer, 0, (int)count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class TeeInputStream : BaseInputStream
|
||||
{
|
||||
private readonly Stream input;
|
||||
|
||||
private readonly Stream tee;
|
||||
|
||||
public TeeInputStream(Stream input, Stream tee)
|
||||
{
|
||||
this.input = input;
|
||||
this.tee = tee;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Platform.Dispose(input);
|
||||
Platform.Dispose(tee);
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buf, int off, int len)
|
||||
{
|
||||
int num = input.Read(buf, off, len);
|
||||
if (num > 0)
|
||||
{
|
||||
tee.Write(buf, off, num);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
int num = input.ReadByte();
|
||||
if (num >= 0)
|
||||
{
|
||||
tee.WriteByte((byte)num);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Org.BouncyCastle.Utilities.IO;
|
||||
|
||||
public class TeeOutputStream : BaseOutputStream
|
||||
{
|
||||
private readonly Stream output;
|
||||
|
||||
private readonly Stream tee;
|
||||
|
||||
public TeeOutputStream(Stream output, Stream tee)
|
||||
{
|
||||
this.output = output;
|
||||
this.tee = tee;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
Platform.Dispose(output);
|
||||
Platform.Dispose(tee);
|
||||
base.Close();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
output.Write(buffer, offset, count);
|
||||
tee.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void WriteByte(byte b)
|
||||
{
|
||||
output.WriteByte(b);
|
||||
tee.WriteByte(b);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user