mirror of
https://github.com/vxcontrol/lualibs-libb64.git
synced 2026-07-01 11:55:38 -04:00
init
This commit is contained in:
BIN
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
Copyright-Only Dedication (based on United States law)
|
||||
or Public Domain Certification
|
||||
|
||||
The person or persons who have associated work with this document (the
|
||||
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||
his knowledge, the work of authorship identified is in the public domain of the
|
||||
country from which the work is published, or (b) hereby dedicates whatever
|
||||
copyright the dedicators holds in the work of authorship identified below (the
|
||||
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||
interest he may have in the associated work, and for these purposes, is
|
||||
described as a "dedicator" below.
|
||||
|
||||
A certifier has taken reasonable steps to verify the copyright status of this
|
||||
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||
liability if in fact the work certified is not in the public domain.
|
||||
|
||||
Dedicator makes this dedication for the benefit of the public at large and to
|
||||
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||
and future rights under copyright law, whether vested or contingent, in the
|
||||
Work. Dedicator understands that such relinquishment of all rights includes
|
||||
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||
copyrights in the Work.
|
||||
|
||||
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||
and in any way, including by methods that have not yet been invented or
|
||||
conceived.
|
||||
@@ -0,0 +1,138 @@
|
||||
b64: Base64 Encoding/Decoding Routines
|
||||
======================================
|
||||
|
||||
Overview:
|
||||
--------
|
||||
libb64 is a library of ANSI C routines for fast encoding/decoding data into and
|
||||
from a base64-encoded format. C++ wrappers are included, as well as the source
|
||||
code for standalone encoding and decoding executables.
|
||||
|
||||
base64 consists of ASCII text, and is therefore a useful encoding for storing
|
||||
binary data in a text file, such as xml, or sending binary data over text-only
|
||||
email.
|
||||
|
||||
References:
|
||||
----------
|
||||
* Wikipedia article:
|
||||
http://en.wikipedia.org/wiki/Base64
|
||||
* base64, another implementation of a commandline en/decoder:
|
||||
http://www.fourmilab.ch/webtools/base64/
|
||||
|
||||
Why?
|
||||
---
|
||||
I did this because I need an implementation of base64 encoding and decoding,
|
||||
without any licensing problems. Most OS implementations are released under
|
||||
either the GNU/GPL, or a BSD-variant, which is not what I require.
|
||||
|
||||
Also, the chance to actually use the co-routine implementation in code is rare,
|
||||
and its use here is fitting. I couldn't pass up the chance.
|
||||
For more information on this technique, see "Coroutines in C", by Simon Tatham,
|
||||
which can be found online here:
|
||||
http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
|
||||
|
||||
So then, under which license do I release this code? On to the next section...
|
||||
|
||||
License:
|
||||
-------
|
||||
This work is released under into the Public Domain.
|
||||
It basically boils down to this: I put this work in the public domain, and you
|
||||
can take it and do whatever you want with it.
|
||||
|
||||
An example of this "license" is the Creative Commons Public Domain License, a
|
||||
copy of which can be found in the LICENSE file, and also online at
|
||||
http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
Commandline Use:
|
||||
---------------
|
||||
There is a new executable available, it is simply called base64.
|
||||
It can encode and decode files, as instructed by the user.
|
||||
|
||||
To encode a file:
|
||||
$ ./base64 -e filea fileb
|
||||
fileb will now be the base64-encoded version of filea.
|
||||
|
||||
To decode a file:
|
||||
$ ./base64 -d fileb filec
|
||||
filec will now be identical to filea.
|
||||
|
||||
Programming:
|
||||
-----------
|
||||
Some C++ wrappers are provided as well, so you don't have to get your hands
|
||||
dirty. Encoding from standard input to standard output is as simple as
|
||||
|
||||
#include <b64/encode.h>
|
||||
#include <iostream>
|
||||
int main()
|
||||
{
|
||||
base64::encoder E;
|
||||
E.encode(std::cin, std::cout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Both standalone executables and a static library is provided in the package,
|
||||
|
||||
Implementation:
|
||||
--------------
|
||||
It is DAMN fast, if I may say so myself. The C code uses a little trick which
|
||||
has been used to implement coroutines, of which one can say that this
|
||||
implementation is an example.
|
||||
|
||||
(To see how the libb64 codebase compares with some other BASE64 implementations
|
||||
available, see the BENCHMARKS file)
|
||||
|
||||
The trick involves the fact that a switch-statement may legally cross into
|
||||
sub-blocks. A very thorough and enlightening essay on co-routines in C, using
|
||||
this method, can be found in the above mentioned "Coroutines in C", by Simon
|
||||
Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
|
||||
|
||||
For example, an RLE decompressing routine, adapted from the article:
|
||||
1 static int STATE = 0;
|
||||
2 static int len, c;
|
||||
3 switch (STATE)
|
||||
4 {
|
||||
5 while (1)
|
||||
6 {
|
||||
7 c = getchar();
|
||||
8 if (c == EOF) return EOF;
|
||||
9 if (c == 0xFF) {
|
||||
10 len = getchar();
|
||||
11 c = getchar();
|
||||
12 while (len--)
|
||||
13 {
|
||||
14 STATE = 0;
|
||||
15 return c;
|
||||
16 case 0:
|
||||
17 }
|
||||
18 } else
|
||||
19 STATE = 1;
|
||||
20 return c;
|
||||
21 case 1:
|
||||
22 }
|
||||
23 }
|
||||
24 }
|
||||
|
||||
As can be seen from this example, a coroutine depends on a state variable,
|
||||
which it sets directly before exiting (lines 14 and 119). The next time the
|
||||
routine is entered, the switch moves control to the specific point directly
|
||||
after the previous exit (lines 16 and 21).hands
|
||||
|
||||
(As an aside, in the mentioned article the combination of the top-level switch,
|
||||
the various setting of the state, the return of a value, and the labelling of
|
||||
the exit point is wrapped in #define macros, making the structure of the
|
||||
routine even clearer.)
|
||||
|
||||
The obvious problem with any such routine is the static keyword.
|
||||
Any static variables in a function spell doom for multithreaded applications.
|
||||
Also, in situations where this coroutine is used by more than one other
|
||||
coroutines, the consistency is disturbed.
|
||||
|
||||
What is needed is a structure for storing these variabled, which is passed to
|
||||
the routine seperately. This obviously breaks the modularity of the function,
|
||||
since now the caller has to worry about and care for the internal state of the
|
||||
routine (the callee). This allows for a fast, multithreading-enabled
|
||||
implementation, which may (obviously) be wrapped in a C++ object for ease of
|
||||
use.
|
||||
|
||||
The base64 encoding and decoding functionality in this package is implemented
|
||||
in exactly this way, providing both a high-speed high-maintanence C interface,
|
||||
and a wrapped C++ which is low-maintanence and only slightly less performant.
|
||||
@@ -0,0 +1 @@
|
||||
libb64 1.2 from http://libb64.sourceforge.net/ (public domain)
|
||||
@@ -0,0 +1,4 @@
|
||||
gcc *.c -O3 -s -I. -shared -o ../../linux/bin/libb64.so -Wall
|
||||
|
||||
cd ../..
|
||||
linux/bin/luajit libb64_test.lua
|
||||
@@ -0,0 +1,5 @@
|
||||
gcc *.c -O3 -s -I. -shared -o ../../bin/b64.dll -Wall
|
||||
|
||||
cd ../..
|
||||
bin/luajit libb64_test.lua
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cdecode.h"
|
||||
|
||||
int base64_decode_value(char value_in)
|
||||
{
|
||||
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,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,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||
static const char decoding_size = sizeof(decoding);
|
||||
value_in -= 43;
|
||||
if (value_in < 0 || value_in > decoding_size) return -1;
|
||||
return decoding[(int)value_in];
|
||||
}
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = 0;
|
||||
}
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||
{
|
||||
const char* codechar = code_in;
|
||||
char* plainchar = plaintext_out;
|
||||
char fragment;
|
||||
|
||||
*plainchar = state_in->plainchar;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_a:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_a;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar = (fragment & 0x03f) << 2;
|
||||
case step_b:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_b;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||
*plainchar = (fragment & 0x00f) << 4;
|
||||
case step_c:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_c;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||
*plainchar = (fragment & 0x003) << 6;
|
||||
case step_d:
|
||||
do {
|
||||
if (codechar == code_in+length_in)
|
||||
{
|
||||
state_in->step = step_d;
|
||||
state_in->plainchar = *plainchar;
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
fragment = (char)base64_decode_value(*codechar++);
|
||||
} while (fragment < 0);
|
||||
*plainchar++ |= (fragment & 0x03f);
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return plainchar - plaintext_out;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
cdecode.h - c header for a base64 decoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CDECODE_H
|
||||
#define BASE64_CDECODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_a, step_b, step_c, step_d
|
||||
} base64_decodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_decodestep step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
|
||||
int base64_decode_value(char value_in);
|
||||
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CDECODE_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#include "cencode.h"
|
||||
|
||||
const int CHARS_PER_LINE = 72;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in)
|
||||
{
|
||||
state_in->step = step_A;
|
||||
state_in->result = 0;
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
|
||||
char base64_encode_value(char value_in)
|
||||
{
|
||||
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (value_in > 63) return '=';
|
||||
return encoding[(int)value_in];
|
||||
}
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
const char* plainchar = plaintext_in;
|
||||
const char* const plaintextend = plaintext_in + length_in;
|
||||
char* codechar = code_out;
|
||||
char result;
|
||||
char fragment;
|
||||
|
||||
result = state_in->result;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
case step_A:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_A;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result = (fragment & 0x0fc) >> 2;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x003) << 4;
|
||||
case step_B:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_B;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0f0) >> 4;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x00f) << 2;
|
||||
case step_C:
|
||||
if (plainchar == plaintextend)
|
||||
{
|
||||
state_in->result = result;
|
||||
state_in->step = step_C;
|
||||
return codechar - code_out;
|
||||
}
|
||||
fragment = *plainchar++;
|
||||
result |= (fragment & 0x0c0) >> 6;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
result = (fragment & 0x03f) >> 0;
|
||||
*codechar++ = base64_encode_value(result);
|
||||
|
||||
++(state_in->stepcount);
|
||||
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||
{
|
||||
*codechar++ = '\n';
|
||||
state_in->stepcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* control should not reach here */
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||
{
|
||||
char* codechar = code_out;
|
||||
|
||||
switch (state_in->step)
|
||||
{
|
||||
case step_B:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_C:
|
||||
*codechar++ = base64_encode_value(state_in->result);
|
||||
*codechar++ = '=';
|
||||
break;
|
||||
case step_A:
|
||||
break;
|
||||
}
|
||||
*codechar++ = '\n';
|
||||
|
||||
return codechar - code_out;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
cencode.h - c header for a base64 encoding algorithm
|
||||
|
||||
This is part of the libb64 project, and has been placed in the public domain.
|
||||
For details, see http://sourceforge.net/projects/libb64
|
||||
*/
|
||||
|
||||
#ifndef BASE64_CENCODE_H
|
||||
#define BASE64_CENCODE_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
step_A, step_B, step_C
|
||||
} base64_encodestep;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
base64_encodestep step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
|
||||
char base64_encode_value(char value_in);
|
||||
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
|
||||
#endif /* BASE64_CENCODE_H */
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
--libb64 ffi binding
|
||||
local ffi = require'ffi'
|
||||
local C = ffi.load'b64'
|
||||
|
||||
ffi.cdef[[
|
||||
typedef struct
|
||||
{
|
||||
int step;
|
||||
char plainchar;
|
||||
} base64_decodestate;
|
||||
|
||||
void base64_init_decodestate(base64_decodestate* state_in);
|
||||
int base64_decode_value(char value_in);
|
||||
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int step;
|
||||
char result;
|
||||
int stepcount;
|
||||
} base64_encodestate;
|
||||
|
||||
void base64_init_encodestate(base64_encodestate* state_in);
|
||||
char base64_encode_value(char value_in);
|
||||
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||
]]
|
||||
|
||||
local function decode_tobuffer(data, size, buf, sz)
|
||||
size = size or #data
|
||||
if size == 0 then return 0 end
|
||||
assert(sz >= math.floor(size * 3 / 4), 'buffer too small')
|
||||
local state_in = ffi.new'base64_decodestate'
|
||||
C.base64_init_decodestate(state_in)
|
||||
return C.base64_decode_block(data, size, buf, state_in)
|
||||
end
|
||||
|
||||
local function decode(data, size)
|
||||
size = size or #data
|
||||
if size == 0 then return '' end
|
||||
local sz = math.floor(size * 3 / 4)
|
||||
local buf = ffi.new('uint8_t[?]', sz)
|
||||
sz = decode_tobuffer(data, size, buf, sz)
|
||||
return ffi.string(buf, sz)
|
||||
end
|
||||
|
||||
local function encode_tobuffer(data, size, buf, sz)
|
||||
size = size or #data
|
||||
if size == 0 then return 0 end
|
||||
assert(sz >= size * 2 + 3, 'buffer too small')
|
||||
local state_in = ffi.new'base64_encodestate'
|
||||
C.base64_init_encodestate(state_in)
|
||||
local sz = C.base64_encode_block(data, size, buf, state_in)
|
||||
sz = sz + C.base64_encode_blockend(buf + sz, state_in)
|
||||
buf[sz-1] = 0 --replace \n
|
||||
return sz-1
|
||||
end
|
||||
|
||||
local function encode(data, size)
|
||||
size = size or #data
|
||||
if size == 0 then return '' end
|
||||
local sz = size * 2 + 3
|
||||
local buf = ffi.new('uint8_t[?]', sz)
|
||||
sz = encode_tobuffer(data, size, buf, sz)
|
||||
return ffi.string(buf, sz)
|
||||
end
|
||||
|
||||
if not ... then require'libb64_test' end
|
||||
|
||||
return {
|
||||
decode_tobuffer = decode_tobuffer,
|
||||
decode = decode,
|
||||
encode_tobuffer = encode_tobuffer,
|
||||
encode = encode,
|
||||
C = C,
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
project: libb64
|
||||
tagline: fast base64 encoding and decoding
|
||||
---
|
||||
|
||||
v1.0 | libb64 1.2 | LuaJIT 2
|
||||
|
||||
## `local b64 = require'libb64'`
|
||||
|
||||
FFI binding of [libb64](http://libb64.sourceforge.net/), a fast base64 encoder and decoder by Chris Venter.
|
||||
|
||||
b64.encode(s[, size]) -> s
|
||||
b64.decode(s[, size]) -> s
|
||||
b64.encode(cdata, size) -> s
|
||||
b64.decode(cdata, size) -> s
|
||||
|
||||
Encode/decode a string or cdata to a string.
|
||||
|
||||
b64.encode_tobuffer(s, [size], out_buf, out_size) -> bytes_written
|
||||
b64.decode_tobuffer(s, [size], out_buf, out_size) -> bytes_written
|
||||
b64.encode_tobuffer(cdata, size, out_buf, out_size) -> bytes_written
|
||||
b64.decode_tobuffer(cdata, size, out_buf, out_size) -> bytes_written
|
||||
|
||||
Encode/decode a string or cdata to a buffer.
|
||||
|
||||
* encoding needs a buffer of at least `size * 2 + 3` bytes
|
||||
* decoding needs a buffer of at least `math.floor(size * 3 / 4)` bytes
|
||||
|
||||
## TODO
|
||||
|
||||
Stream-like interface similar to zlib's inflate/deflate.
|
||||
@@ -0,0 +1,22 @@
|
||||
local b64 = require'libb64'
|
||||
local decode = b64.decode
|
||||
local encode = b64.encode
|
||||
|
||||
assert(decode'YW55IGNhcm5hbCBwbGVhc3VyZS4=' == 'any carnal pleasure.')
|
||||
assert(decode'YW55IGNhcm5hbCBwbGVhc3VyZQ==' == 'any carnal pleasure')
|
||||
assert(decode'YW55IGNhcm5hbCBwbGVhc3Vy' == 'any carnal pleasur')
|
||||
assert(decode'YW55IGNhcm5hbCBwbGVhc3U=' == 'any carnal pleasu')
|
||||
assert(decode'YW55IGNhcm5hbCBwbGVhcw==' == 'any carnal pleas')
|
||||
assert(decode'., ? !@#$%^& \n\r\n\r YW55IGNhcm5hbCBwbGVhcw== \n\r' == 'any carnal pleas')
|
||||
|
||||
assert(encode'any carnal pleasure.' == 'YW55IGNhcm5hbCBwbGVhc3VyZS4=')
|
||||
assert(encode'any carnal pleasure' == 'YW55IGNhcm5hbCBwbGVhc3VyZQ==')
|
||||
assert(encode'any carnal pleasur' == 'YW55IGNhcm5hbCBwbGVhc3Vy')
|
||||
assert(encode'any carnal pleasu' == 'YW55IGNhcm5hbCBwbGVhc3U=')
|
||||
assert(encode'any carnal pleas' == 'YW55IGNhcm5hbCBwbGVhcw==')
|
||||
|
||||
assert(decode(encode'') == '')
|
||||
assert(decode(encode'x') == 'x')
|
||||
assert(decode(encode'xx') == 'xx')
|
||||
assert(decode'.!@#$%^&*( \n\r\t' == '')
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user