You've already forked lazarus-ccr
Base32 encoding + tests
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1357 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -21,6 +21,7 @@ type
|
|||||||
|
|
||||||
EBaseXException = class(Exception);
|
EBaseXException = class(Exception);
|
||||||
EBase16Exception = class(EBaseXException);
|
EBase16Exception = class(EBaseXException);
|
||||||
|
EBase32Exception = class(EBaseXException);
|
||||||
EBase64Exception = class(EBaseXException);
|
EBase64Exception = class(EBaseXException);
|
||||||
|
|
||||||
TBaseXOption = ( xoDecodeIgnoreIllegalChar );
|
TBaseXOption = ( xoDecodeIgnoreIllegalChar );
|
||||||
@ -30,6 +31,10 @@ type
|
|||||||
function Base64Encode(const AInBuffer : TBinaryString) : string;overload;
|
function Base64Encode(const AInBuffer : TBinaryString) : string;overload;
|
||||||
function Base64Decode(const AInBuffer : string; const AOptions : TBaseXOptions) : TByteDynArray;overload;
|
function Base64Decode(const AInBuffer : string; const AOptions : TBaseXOptions) : TByteDynArray;overload;
|
||||||
|
|
||||||
|
function Base32Encode(const ALength : PtrInt; const AInBuffer) : string; overload;
|
||||||
|
function Base32Encode(const AInBuffer : TBinaryString) : string; overload;
|
||||||
|
function Base32Decode(const AInBuffer : string; const AOptions : TBaseXOptions) : TByteDynArray;
|
||||||
|
|
||||||
procedure Base16Encode(const ABin; const ALen : Integer; AOutBuffer : PChar); overload;
|
procedure Base16Encode(const ABin; const ALen : Integer; AOutBuffer : PChar); overload;
|
||||||
function Base16Encode(const ABin; const ALen : Integer) : string; overload;
|
function Base16Encode(const ABin; const ALen : Integer) : string; overload;
|
||||||
function Base16Encode(const AInBuffer : TBinaryString) : string;overload;
|
function Base16Encode(const AInBuffer : TBinaryString) : string;overload;
|
||||||
@ -203,6 +208,195 @@ begin
|
|||||||
Move(locRes[0],Result[1],Length(Result));
|
Move(locRes[0],Result[1],Length(Result));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
const
|
||||||
|
Base32_CHAR_TABLE : array[0..31] of char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||||
|
|
||||||
|
function Base32Encode(const ALength : PtrInt; const AInBuffer) : string;
|
||||||
|
var
|
||||||
|
locBuffer : PByte;
|
||||||
|
locCopied, locBlockCount, i, locAtualLen : PtrInt;
|
||||||
|
locInQuantom : array[0..4] of Byte;
|
||||||
|
locOutQuantom : array[0..7] of Char;
|
||||||
|
begin
|
||||||
|
Result := '';
|
||||||
|
if ( ALength > 0 ) then begin
|
||||||
|
locBuffer := @AInBuffer;
|
||||||
|
locBlockCount := ALength div 5;
|
||||||
|
SetLength(Result,(locBlockCount + 1 ) * 8);
|
||||||
|
locAtualLen := 0;
|
||||||
|
for i := 1 to locBlockCount do begin
|
||||||
|
Move(locBuffer^,locInQuantom[0],5);
|
||||||
|
Inc(locBuffer,5);
|
||||||
|
locOutQuantom[0] := Base32_CHAR_TABLE[(locInQuantom[0] shr 3)];
|
||||||
|
locOutQuantom[1] := Base32_CHAR_TABLE[( (locInQuantom[0] and 7) shl 2 ) or ( locInQuantom[1] shr 6 )];
|
||||||
|
locOutQuantom[2] := Base32_CHAR_TABLE[( (locInQuantom[1] and 62) shr 1 )];
|
||||||
|
locOutQuantom[3] := Base32_CHAR_TABLE[( (locInQuantom[1] and 1) shl 4 ) or ( locInQuantom[2] shr 4 )];
|
||||||
|
locOutQuantom[4] := Base32_CHAR_TABLE[( (locInQuantom[2] and 15) shl 1 ) or ( locInQuantom[3] shr 7 )];
|
||||||
|
locOutQuantom[5] := Base32_CHAR_TABLE[( (locInQuantom[3] and 124) shr 2 )];
|
||||||
|
locOutQuantom[6] := Base32_CHAR_TABLE[( (locInQuantom[3] and 3) shl 3 ) or ( locInQuantom[4] shr 5 )];
|
||||||
|
locOutQuantom[7] := Base32_CHAR_TABLE[(locInQuantom[4] and 31)];
|
||||||
|
Move(locOutQuantom[0],Result[locAtualLen + 1],( 8 * SizeOf(Char) ));
|
||||||
|
Inc(locAtualLen,8);
|
||||||
|
end;
|
||||||
|
locCopied := ALength mod 5;
|
||||||
|
if ( locCopied > 0 ) then begin
|
||||||
|
case locCopied of
|
||||||
|
1 :
|
||||||
|
begin
|
||||||
|
Move(locBuffer^,locInQuantom[0],1);
|
||||||
|
locInQuantom[1] := 0;
|
||||||
|
locOutQuantom[0] := Base32_CHAR_TABLE[(locInQuantom[0] shr 3)];
|
||||||
|
locOutQuantom[1] := Base32_CHAR_TABLE[( (locInQuantom[0] and 7) shl 2 ) or ( locInQuantom[1] shr 6 )];
|
||||||
|
locOutQuantom[2] := '=';
|
||||||
|
locOutQuantom[3] := '=';
|
||||||
|
locOutQuantom[4] := '=';
|
||||||
|
locOutQuantom[5] := '=';
|
||||||
|
locOutQuantom[6] := '=';
|
||||||
|
locOutQuantom[7] := '=';
|
||||||
|
end;
|
||||||
|
2 :
|
||||||
|
begin
|
||||||
|
Move(locBuffer^,locInQuantom[0],2);
|
||||||
|
locInQuantom[2] := 0;
|
||||||
|
locOutQuantom[0] := Base32_CHAR_TABLE[(locInQuantom[0] shr 3)];
|
||||||
|
locOutQuantom[1] := Base32_CHAR_TABLE[( (locInQuantom[0] and 7) shl 2 ) or ( locInQuantom[1] shr 6 )];
|
||||||
|
locOutQuantom[2] := Base32_CHAR_TABLE[( (locInQuantom[1] and 62) shr 1 )];
|
||||||
|
locOutQuantom[3] := Base32_CHAR_TABLE[( (locInQuantom[1] and 1) shl 4 ) or ( locInQuantom[2] shr 4 )];
|
||||||
|
locOutQuantom[4] := '=';
|
||||||
|
locOutQuantom[5] := '=';
|
||||||
|
locOutQuantom[6] := '=';
|
||||||
|
locOutQuantom[7] := '=';
|
||||||
|
end;
|
||||||
|
3 :
|
||||||
|
begin
|
||||||
|
Move(locBuffer^,locInQuantom[0],3);
|
||||||
|
locInQuantom[3] := 0;
|
||||||
|
locOutQuantom[0] := Base32_CHAR_TABLE[(locInQuantom[0] shr 3)];
|
||||||
|
locOutQuantom[1] := Base32_CHAR_TABLE[( (locInQuantom[0] and 7) shl 2 ) or ( locInQuantom[1] shr 6 )];
|
||||||
|
locOutQuantom[2] := Base32_CHAR_TABLE[( (locInQuantom[1] and 62) shr 1 )];
|
||||||
|
locOutQuantom[3] := Base32_CHAR_TABLE[( (locInQuantom[1] and 1) shl 4 ) or ( locInQuantom[2] shr 4 )];
|
||||||
|
locOutQuantom[4] := Base32_CHAR_TABLE[( (locInQuantom[2] and 15) shl 1 ) or ( locInQuantom[3] shr 7 )];
|
||||||
|
locOutQuantom[5] := '=';
|
||||||
|
locOutQuantom[6] := '=';
|
||||||
|
locOutQuantom[7] := '=';
|
||||||
|
end;
|
||||||
|
4 :
|
||||||
|
begin
|
||||||
|
Move(locBuffer^,locInQuantom[0],4);
|
||||||
|
locInQuantom[4] := 0;
|
||||||
|
locOutQuantom[0] := Base32_CHAR_TABLE[(locInQuantom[0] shr 3)];
|
||||||
|
locOutQuantom[1] := Base32_CHAR_TABLE[( (locInQuantom[0] and 7) shl 2 ) or ( locInQuantom[1] shr 6 )];
|
||||||
|
locOutQuantom[2] := Base32_CHAR_TABLE[( (locInQuantom[1] and 62) shr 1 )];
|
||||||
|
locOutQuantom[3] := Base32_CHAR_TABLE[( (locInQuantom[1] and 1) shl 4 ) or ( locInQuantom[2] shr 4 )];
|
||||||
|
locOutQuantom[4] := Base32_CHAR_TABLE[( (locInQuantom[2] and 15) shl 1 ) or ( locInQuantom[3] shr 7 )];
|
||||||
|
locOutQuantom[5] := Base32_CHAR_TABLE[( (locInQuantom[3] and 124) shr 2 )];
|
||||||
|
locOutQuantom[6] := Base32_CHAR_TABLE[( (locInQuantom[3] and 3) shl 3 ) or ( locInQuantom[4] shr 5 )];
|
||||||
|
locOutQuantom[7] := '=';
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Move(locOutQuantom[0],Result[locAtualLen + 1],(8 * SizeOf(Char)));
|
||||||
|
Inc(locAtualLen,8);
|
||||||
|
end;
|
||||||
|
SetLength(Result,locAtualLen);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function Base32Encode(const AInBuffer : TBinaryString) : string;
|
||||||
|
begin
|
||||||
|
if ( Length(AInBuffer) = 0 ) then
|
||||||
|
Result := ''
|
||||||
|
else
|
||||||
|
Result := Base32Encode(Length(AInBuffer),AInBuffer[1]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function Base32Decode(const AInBuffer : string; const AOptions : TBaseXOptions) : TByteDynArray;
|
||||||
|
const
|
||||||
|
ALPHA_UP_MAP : array['A'..'Z'] of Byte = (
|
||||||
|
0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25
|
||||||
|
);
|
||||||
|
ALPHA_LOW_MAP : array['a'..'z'] of Byte = (
|
||||||
|
0 , 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25
|
||||||
|
);
|
||||||
|
DIGIT_MAP : array['2'..'7'] of Byte = ( 26, 27, 28, 29, 30, 31);
|
||||||
|
var
|
||||||
|
locBuffer : PChar;
|
||||||
|
locInLen, locInIndex, i, locPadded : PtrInt;
|
||||||
|
locOutQuantom : array[0..4] of Byte;
|
||||||
|
locInQuantom : array[0..7] of Byte;
|
||||||
|
ok : Boolean;
|
||||||
|
locAtualLen : PtrInt;
|
||||||
|
locInValue, locReadedValidChars : Byte;
|
||||||
|
locFailOnIllegalChar : Boolean;
|
||||||
|
locTrueData : Integer;
|
||||||
|
begin
|
||||||
|
if ( AInBuffer = '' ) then begin
|
||||||
|
Result := nil;
|
||||||
|
end else begin
|
||||||
|
locInIndex := 0;
|
||||||
|
locAtualLen := 0;
|
||||||
|
locPadded := 0;
|
||||||
|
locInLen := Length(AInBuffer);
|
||||||
|
SetLength(Result,locInLen);
|
||||||
|
locBuffer := @(AInBuffer[1]);
|
||||||
|
locFailOnIllegalChar := not ( xoDecodeIgnoreIllegalChar in AOptions );
|
||||||
|
while ( locInIndex < locInLen ) do begin
|
||||||
|
locReadedValidChars := 0;
|
||||||
|
for i := 0 to 7 do begin
|
||||||
|
ok := False;
|
||||||
|
while ( locInIndex <= locInLen ) do begin
|
||||||
|
case locBuffer^ of
|
||||||
|
'A'..'Z' : locInValue := ALPHA_UP_MAP[locBuffer^];
|
||||||
|
'a'..'z' : locInValue := ALPHA_LOW_MAP[locBuffer^];
|
||||||
|
'2'..'7' : locInValue := DIGIT_MAP[locBuffer^];
|
||||||
|
'=' : locInValue := 0;
|
||||||
|
else begin
|
||||||
|
if locFailOnIllegalChar then
|
||||||
|
raise EBase32Exception.CreateFmt(SERR_IllegalChar,[Char(locBuffer^)]);
|
||||||
|
Inc(locBuffer);
|
||||||
|
Inc(locInIndex);
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Inc(locBuffer);
|
||||||
|
Inc(locInIndex);
|
||||||
|
locInQuantom[i] := locInValue;
|
||||||
|
if ( locBuffer^ = '=' ) then
|
||||||
|
Inc(locPadded);
|
||||||
|
ok := True;
|
||||||
|
Inc(locReadedValidChars);
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
if ( not ok ) and locFailOnIllegalChar then
|
||||||
|
raise EBase64Exception.CreateFmt(SERR_IllegalChar,[Char(locBuffer^)]);
|
||||||
|
end;
|
||||||
|
if ( locReadedValidChars > 0 ) then begin
|
||||||
|
locOutQuantom[0] := ( locInQuantom[0] shl 3 ) or ( locInQuantom[1] shr 2 );
|
||||||
|
locOutQuantom[1] := ( (locInQuantom[1] shl 6) and 192 ) or
|
||||||
|
( (locInQuantom[2] shl 1) and 62) or
|
||||||
|
(locInQuantom[3] shr 4);
|
||||||
|
locOutQuantom[2] := ((locInQuantom[3] shl 4) and 240) or ( locInQuantom[4] shr 1);
|
||||||
|
locOutQuantom[3] := ((locInQuantom[4] shl 7) and 128) or
|
||||||
|
((locInQuantom[5] shl 2) and 124) or
|
||||||
|
(locInQuantom[6] shr 3);
|
||||||
|
locOutQuantom[4] := ((locInQuantom[6] shl 5) and 224) or locInQuantom[7];
|
||||||
|
case locPadded of
|
||||||
|
6 : locTrueData := 1;
|
||||||
|
4 : locTrueData := 2;
|
||||||
|
3 : locTrueData := 3;
|
||||||
|
1 : locTrueData := 4;
|
||||||
|
else
|
||||||
|
locTrueData := 5;
|
||||||
|
end;
|
||||||
|
Move(locOutQuantom[0],Result[locAtualLen],locTrueData);
|
||||||
|
Inc(locAtualLen,locTrueData);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
SetLength(Result,locAtualLen);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure Base16Encode(const ABin; const ALen : Integer; AOutBuffer : PChar);
|
procedure Base16Encode(const ABin; const ALen : Integer; AOutBuffer : PChar);
|
||||||
const
|
const
|
||||||
HEX_MAP : array[0..15] of Char = '0123456789ABCDEF';
|
HEX_MAP : array[0..15] of Char = '0123456789ABCDEF';
|
||||||
|
@ -49,6 +49,31 @@ type
|
|||||||
procedure Decode_illegal_char();
|
procedure Decode_illegal_char();
|
||||||
procedure Decode_empty();
|
procedure Decode_empty();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TTest_Base32 }
|
||||||
|
|
||||||
|
TTest_Base32 = class(TWstBaseTest)
|
||||||
|
protected
|
||||||
|
procedure Check_Encode(const AIn, AExpect : string);
|
||||||
|
procedure Check_Decode(const AIn, AExpect : string; const AOptions : TBaseXOptions = [xoDecodeIgnoreIllegalChar]);
|
||||||
|
published
|
||||||
|
procedure Encode_empty();
|
||||||
|
procedure Encode_f();
|
||||||
|
procedure Encode_fo();
|
||||||
|
procedure Encode_foo();
|
||||||
|
procedure Encode_foob();
|
||||||
|
procedure Encode_fooba();
|
||||||
|
procedure Encode_foobar();
|
||||||
|
|
||||||
|
procedure Decode_f();
|
||||||
|
procedure Decode_fo();
|
||||||
|
procedure Decode_foo();
|
||||||
|
procedure Decode_foob();
|
||||||
|
procedure Decode_fooba();
|
||||||
|
procedure Decode_foobar();
|
||||||
|
procedure Decode_illegal_char();
|
||||||
|
procedure Decode_empty();
|
||||||
|
end;
|
||||||
|
|
||||||
TTest_Base16 = class(TWstBaseTest)
|
TTest_Base16 = class(TWstBaseTest)
|
||||||
protected
|
protected
|
||||||
@ -200,6 +225,132 @@ begin
|
|||||||
//Check_Encode('foobar','Zm9vYmFy');
|
//Check_Encode('foobar','Zm9vYmFy');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TTest_Base32 }
|
||||||
|
|
||||||
|
procedure TTest_Base32.Check_Decode(const AIn, AExpect: string; const AOptions : TBaseXOptions);
|
||||||
|
var
|
||||||
|
locRes : TByteDynArray;
|
||||||
|
begin
|
||||||
|
locRes := Base32Decode(AIn,AOptions);
|
||||||
|
CheckEquals(StringToByteArray(AExpect),locRes);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Check_Encode(const AIn, AExpect: string);
|
||||||
|
var
|
||||||
|
locRes : string;
|
||||||
|
begin
|
||||||
|
locRes := Base32Encode(AIn);
|
||||||
|
CheckEquals(AExpect,locRes);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_f();
|
||||||
|
begin
|
||||||
|
Check_Decode('MY======','f');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_fo();
|
||||||
|
begin
|
||||||
|
Check_Decode('MZXQ====','fo');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_foo();
|
||||||
|
begin
|
||||||
|
Check_Decode('MZXW6===','foo');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_foob();
|
||||||
|
begin
|
||||||
|
Check_Decode('MZXW6YQ=','foob');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_fooba();
|
||||||
|
begin
|
||||||
|
Check_Decode('MZXW6YTB','fooba');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_foobar();
|
||||||
|
begin
|
||||||
|
Check_Decode('MZXW6YTBOI======','foobar');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_illegal_char();
|
||||||
|
var
|
||||||
|
ok : Boolean;
|
||||||
|
begin
|
||||||
|
ok := False;
|
||||||
|
try
|
||||||
|
Check_Decode('MZX'#200'W6' + sLineBreak + 'Y'#0'TB','fooba',[]);
|
||||||
|
except
|
||||||
|
on e : EBase32Exception do
|
||||||
|
ok := True;
|
||||||
|
end;
|
||||||
|
CheckEquals(True,ok);
|
||||||
|
|
||||||
|
Check_Decode('MZX'#200'W6' + sLineBreak + 'Y'#0'TB','fooba',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
Check_Decode('MZX'#200'W6' + sLineBreak + 'Y'#0'TB' + sLineBreak,'fooba',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
Check_Decode('MZX'#200'W6' + sLineBreak + 'Y'#0'TB' + sLineBreak + sLineBreak,'fooba',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Decode_empty();
|
||||||
|
var
|
||||||
|
ok : Boolean;
|
||||||
|
begin
|
||||||
|
ok := False;
|
||||||
|
try
|
||||||
|
Check_Decode(sLineBreak,'',[]);
|
||||||
|
except
|
||||||
|
on e : EBase32Exception do
|
||||||
|
ok := True;
|
||||||
|
end;
|
||||||
|
CheckEquals(True,ok);
|
||||||
|
|
||||||
|
Check_Decode('','',[]);
|
||||||
|
Check_Decode(#0,'',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
Check_Decode(sLineBreak,'',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
Check_Decode(sLineBreak + sLineBreak,'',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
Check_Decode(sLineBreak + sLineBreak + sLineBreak,'',[xoDecodeIgnoreIllegalChar]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_empty();
|
||||||
|
begin
|
||||||
|
Check_Encode('','');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_f();
|
||||||
|
begin
|
||||||
|
Check_Encode('f','MY======');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_fo();
|
||||||
|
begin
|
||||||
|
Check_Encode('fo','MZXQ====');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_foo();
|
||||||
|
begin
|
||||||
|
Check_Encode('foo','MZXW6===');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_foob();
|
||||||
|
begin
|
||||||
|
Check_Encode('foob','MZXW6YQ=');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_fooba();
|
||||||
|
begin
|
||||||
|
Check_Encode('fooba','MZXW6YTB');
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TTest_Base32.Encode_foobar();
|
||||||
|
var
|
||||||
|
a, b : string;
|
||||||
|
begin
|
||||||
|
a := 'foobar';
|
||||||
|
b := 'MZXW6YTBOI======';
|
||||||
|
Check_Encode(a,b);
|
||||||
|
//Check_Encode('foobar','Zm9vYmFy');
|
||||||
|
end;
|
||||||
|
|
||||||
{ TTest_Base16 }
|
{ TTest_Base16 }
|
||||||
|
|
||||||
procedure TTest_Base16.Check_Decode(const AIn, AExpect: string; const AOptions: TBaseXOptions);
|
procedure TTest_Base16.Check_Decode(const AIn, AExpect: string; const AOptions: TBaseXOptions);
|
||||||
@ -301,6 +452,7 @@ end;
|
|||||||
|
|
||||||
initialization
|
initialization
|
||||||
RegisterTest('Encoding',TTest_Base64.Suite);
|
RegisterTest('Encoding',TTest_Base64.Suite);
|
||||||
|
RegisterTest('Encoding',TTest_Base32.Suite);
|
||||||
RegisterTest('Encoding',TTest_Base16.Suite);
|
RegisterTest('Encoding',TTest_Base16.Suite);
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
Reference in New Issue
Block a user