Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
asn1c
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
asn1c
Commits
92302255
Commit
92302255
authored
Oct 23, 2004
by
Lev Walkin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
OBJECT IDENTIFIER and RELATIVE-OID XER decoding
parent
ed90f0ae
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
292 additions
and
15 deletions
+292
-15
skeletons/OBJECT_IDENTIFIER.c
skeletons/OBJECT_IDENTIFIER.c
+128
-1
skeletons/OBJECT_IDENTIFIER.h
skeletons/OBJECT_IDENTIFIER.h
+38
-13
skeletons/RELATIVE-OID.c
skeletons/RELATIVE-OID.c
+45
-1
skeletons/RELATIVE-OID.h
skeletons/RELATIVE-OID.h
+1
-0
skeletons/tests/check-OIDs.c
skeletons/tests/check-OIDs.c
+80
-0
No files found.
skeletons/OBJECT_IDENTIFIER.c
View file @
92302255
...
@@ -22,7 +22,7 @@ asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = {
...
@@ -22,7 +22,7 @@ asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = {
OBJECT_IDENTIFIER_constraint
,
OBJECT_IDENTIFIER_constraint
,
ber_decode_primitive
,
ber_decode_primitive
,
der_encode_primitive
,
der_encode_primitive
,
0
,
/* Not implemented yet */
OBJECT_IDENTIFIER_decode_xer
,
OBJECT_IDENTIFIER_encode_xer
,
OBJECT_IDENTIFIER_encode_xer
,
0
,
/* Use generic outmost tag fetcher */
0
,
/* Use generic outmost tag fetcher */
asn_DEF_OBJECT_IDENTIFIER_tags
,
asn_DEF_OBJECT_IDENTIFIER_tags
,
...
@@ -263,6 +263,49 @@ OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_byte
...
@@ -263,6 +263,49 @@ OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_byte
return
wrote_len
;
return
wrote_len
;
}
}
static
ssize_t
OBJECT_IDENTIFIER__xer_body_decode
(
void
*
sptr
,
void
*
chunk_buf
,
size_t
chunk_size
)
{
OBJECT_IDENTIFIER_t
*
st
=
(
OBJECT_IDENTIFIER_t
*
)
sptr
;
char
*
endptr
;
long
s_arcs
[
10
];
long
*
arcs
=
s_arcs
;
int
arcs_count
;
int
ret
;
arcs_count
=
OBJECT_IDENTIFIER_parse_arcs
(
(
const
char
*
)
chunk_buf
,
chunk_size
,
arcs
,
10
,
&
endptr
);
if
(
arcs_count
<=
0
)
return
-
1
;
/* Expecting more than zero arcs */
if
(
arcs_count
>
10
)
{
arcs
=
(
long
*
)
MALLOC
(
arcs_count
*
sizeof
(
long
));
if
(
!
arcs
)
return
-
1
;
ret
=
OBJECT_IDENTIFIER_parse_arcs
(
(
const
char
*
)
chunk_buf
,
chunk_size
,
arcs
,
arcs_count
,
&
endptr
);
if
(
ret
!=
arcs_count
)
return
-
1
;
/* assert?.. */
}
/*
* Convert arcs into BER representation.
*/
ret
=
OBJECT_IDENTIFIER_set_arcs
(
st
,
arcs
,
sizeof
(
*
arcs
),
arcs_count
);
if
(
ret
)
return
-
1
;
if
(
arcs
!=
s_arcs
)
FREEMEM
(
arcs
);
return
endptr
-
(
char
*
)
chunk_buf
;
}
asn_dec_rval_t
OBJECT_IDENTIFIER_decode_xer
(
asn_codec_ctx_t
*
opt_codec_ctx
,
asn_TYPE_descriptor_t
*
td
,
void
**
sptr
,
const
char
*
opt_mname
,
void
*
buf_ptr
,
size_t
size
)
{
return
xer_decode_primitive
(
opt_codec_ctx
,
td
,
sptr
,
sizeof
(
OBJECT_IDENTIFIER_t
),
opt_mname
,
buf_ptr
,
size
,
OBJECT_IDENTIFIER__xer_body_decode
);
}
asn_enc_rval_t
asn_enc_rval_t
OBJECT_IDENTIFIER_encode_xer
(
asn_TYPE_descriptor_t
*
td
,
void
*
sptr
,
OBJECT_IDENTIFIER_encode_xer
(
asn_TYPE_descriptor_t
*
td
,
void
*
sptr
,
int
ilevel
,
enum
xer_encoder_flags_e
flags
,
int
ilevel
,
enum
xer_encoder_flags_e
flags
,
...
@@ -596,3 +639,87 @@ OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, void *arcs, unsigned int ar
...
@@ -596,3 +639,87 @@ OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *oid, void *arcs, unsigned int ar
return
0
;
return
0
;
}
}
int
OBJECT_IDENTIFIER_parse_arcs
(
const
char
*
oid_text
,
ssize_t
oid_txt_length
,
long
*
arcs
,
unsigned
int
arcs_slots
,
char
**
oid_text_end
)
{
unsigned
int
arcs_count
=
0
;
const
char
*
oid_end
;
long
value
=
0
;
enum
{
ST_SKIPSPACE
,
ST_WAITDIGITS
,
/* Next character is expected to be a digit */
ST_DIGITS
,
}
state
=
ST_SKIPSPACE
;
if
(
!
oid_text
||
oid_txt_length
<
-
1
||
(
arcs_slots
&&
!
arcs
))
{
if
(
oid_text_end
)
(
const
char
*
)
*
oid_text_end
=
oid_text
;
errno
=
EINVAL
;
return
-
1
;
}
if
(
oid_txt_length
==
-
1
)
oid_txt_length
=
strlen
(
oid_text
);
for
(
oid_end
=
oid_text
+
oid_txt_length
;
oid_text
<
oid_end
;
oid_text
++
)
{
switch
(
*
oid_text
)
{
case
0x09
:
case
0x0a
:
case
0x0d
:
case
0x20
:
/* whitespace */
if
(
state
==
ST_SKIPSPACE
)
{
continue
;
}
else
{
break
;
/* Finish */
}
case
0x2e
:
/* '.' */
if
(
state
!=
ST_DIGITS
||
(
oid_text
+
1
)
==
oid_end
)
{
state
=
ST_WAITDIGITS
;
break
;
}
if
(
arcs_count
<
arcs_slots
)
arcs
[
arcs_count
]
=
value
;
arcs_count
++
;
state
=
ST_WAITDIGITS
;
continue
;
case
0x30
:
case
0x31
:
case
0x32
:
case
0x33
:
case
0x34
:
case
0x35
:
case
0x36
:
case
0x37
:
case
0x38
:
case
0x39
:
if
(
state
!=
ST_DIGITS
)
{
state
=
ST_DIGITS
;
value
=
0
;
}
if
(
1
)
{
long
new_value
=
value
*
10
;
if
(
new_value
/
10
!=
value
||
(
value
=
new_value
+
(
*
oid_text
-
0x30
))
<
0
)
{
/* Overflow */
state
=
ST_WAITDIGITS
;
break
;
}
continue
;
}
default:
/* Unexpected symbols */
state
=
ST_WAITDIGITS
;
break
;
}
/* switch() */
break
;
}
/* for() */
if
(
oid_text_end
)
(
const
char
*
)
*
oid_text_end
=
oid_text
;
/* Finalize last arc */
switch
(
state
)
{
case
ST_WAITDIGITS
:
errno
=
EINVAL
;
return
-
1
;
case
ST_DIGITS
:
if
(
arcs_count
<
arcs_slots
)
arcs
[
arcs_count
]
=
value
;
arcs_count
++
;
/* Fall through */
default:
return
arcs_count
;
}
}
skeletons/OBJECT_IDENTIFIER.h
View file @
92302255
...
@@ -15,28 +15,16 @@ extern asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER;
...
@@ -15,28 +15,16 @@ extern asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER;
asn_struct_print_f
OBJECT_IDENTIFIER_print
;
asn_struct_print_f
OBJECT_IDENTIFIER_print
;
asn_constr_check_f
OBJECT_IDENTIFIER_constraint
;
asn_constr_check_f
OBJECT_IDENTIFIER_constraint
;
der_type_encoder_f
OBJECT_IDENTIFIER_encode_der
;
der_type_encoder_f
OBJECT_IDENTIFIER_encode_der
;
xer_type_decoder_f
OBJECT_IDENTIFIER_decode_xer
;
xer_type_encoder_f
OBJECT_IDENTIFIER_encode_xer
;
xer_type_encoder_f
OBJECT_IDENTIFIER_encode_xer
;
/**********************************
/**********************************
* Some handy conversion routines *
* Some handy conversion routines *
**********************************/
**********************************/
/*
* Print the specified OBJECT IDENTIFIER arc.
*/
int
OBJECT_IDENTIFIER_print_arc
(
uint8_t
*
arcbuf
,
int
arclen
,
int
add
,
/* Arbitrary offset, required to process the first two arcs */
asn_app_consume_bytes_f
*
cb
,
void
*
app_key
);
/* Same as above, but returns the number of written digits, instead of 0 */
ssize_t
OBJECT_IDENTIFIER__dump_arc
(
uint8_t
*
arcbuf
,
int
arclen
,
int
add
,
asn_app_consume_bytes_f
*
cb
,
void
*
app_key
);
/*
/*
* This function fills an (_arcs) array with OBJECT IDENTIFIER arcs
* This function fills an (_arcs) array with OBJECT IDENTIFIER arcs
* up to specified (_arc_slots) elements.
* up to specified (_arc_slots) elements.
* The function always returns the real number of arcs, even if there is no
* sufficient (_arc_slots) provided.
*
*
* EXAMPLE:
* EXAMPLE:
* void print_arcs(OBJECT_IDENTIFIER_t *oid) {
* void print_arcs(OBJECT_IDENTIFIER_t *oid) {
...
@@ -71,6 +59,9 @@ ssize_t OBJECT_IDENTIFIER__dump_arc(uint8_t *arcbuf, int arclen, int add,
...
@@ -71,6 +59,9 @@ ssize_t OBJECT_IDENTIFIER__dump_arc(uint8_t *arcbuf, int arclen, int add,
* -1/EINVAL: Invalid arguments (oid is missing)
* -1/EINVAL: Invalid arguments (oid is missing)
* -1/ERANGE: One or more arcs have value out of array cell type range.
* -1/ERANGE: One or more arcs have value out of array cell type range.
* >=0: Number of arcs contained in the OBJECT IDENTIFIER
* >=0: Number of arcs contained in the OBJECT IDENTIFIER
*
* WARNING: The function always returns the real number of arcs,
* even if there is no sufficient (_arc_slots) provided.
*/
*/
int
OBJECT_IDENTIFIER_get_arcs
(
OBJECT_IDENTIFIER_t
*
_oid
,
int
OBJECT_IDENTIFIER_get_arcs
(
OBJECT_IDENTIFIER_t
*
_oid
,
void
*
_arcs
,
/* i.e., unsigned int arcs[N] */
void
*
_arcs
,
/* i.e., unsigned int arcs[N] */
...
@@ -92,6 +83,40 @@ int OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *_oid,
...
@@ -92,6 +83,40 @@ int OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *_oid,
unsigned
int
_arc_type_size
,
/* i.e., sizeof(arcs[0]) */
unsigned
int
_arc_type_size
,
/* i.e., sizeof(arcs[0]) */
unsigned
int
_arc_slots
/* i.e., N */
);
unsigned
int
_arc_slots
/* i.e., N */
);
/*
* Print the specified OBJECT IDENTIFIER arc.
*/
int
OBJECT_IDENTIFIER_print_arc
(
uint8_t
*
arcbuf
,
int
arclen
,
int
add
,
/* Arbitrary offset, required to process the first two arcs */
asn_app_consume_bytes_f
*
cb
,
void
*
app_key
);
/* Same as above, but returns the number of written digits, instead of 0 */
ssize_t
OBJECT_IDENTIFIER__dump_arc
(
uint8_t
*
arcbuf
,
int
arclen
,
int
add
,
asn_app_consume_bytes_f
*
cb
,
void
*
app_key
);
/*
* Parse the OBJECT IDENTIFIER textual representation ("1.3.6.1.4.1.9363").
* No arc can exceed the (0..signed_long_max) range (typically, 0..2G if L32).
* This function is not specific to OBJECT IDENTIFIER, it may be used to parse
* the RELATIVE-OID data, or any other data consisting of dot-separated
* series of numeric values.
*
* If (oid_txt_length == -1), the strlen() will be invoked to determine the
* size of the (oid_text) string.
*
* After return, the optional (oid_text_end) is set to the character after
* the last parsed one. (oid_text_end) is never less than (oid_text).
*
* RETURN VALUES:
* -1: Parse error.
* >= 0: Number of arcs contained in the OBJECT IDENTIFIER.
*
* WARNING: The function always returns the real number of arcs,
* even if there is no sufficient (_arc_slots) provided.
*/
int
OBJECT_IDENTIFIER_parse_arcs
(
const
char
*
oid_text
,
ssize_t
oid_txt_length
,
long
arcs
[],
unsigned
int
arcs_slots
,
char
**
oid_text_end
);
/*
/*
* Internal functions.
* Internal functions.
* Used by RELATIVE-OID implementation in particular.
* Used by RELATIVE-OID implementation in particular.
...
...
skeletons/RELATIVE-OID.c
View file @
92302255
...
@@ -23,7 +23,7 @@ asn_TYPE_descriptor_t asn_DEF_RELATIVE_OID = {
...
@@ -23,7 +23,7 @@ asn_TYPE_descriptor_t asn_DEF_RELATIVE_OID = {
asn_generic_no_constraint
,
asn_generic_no_constraint
,
ber_decode_primitive
,
ber_decode_primitive
,
der_encode_primitive
,
der_encode_primitive
,
0
,
/* Not implemented yet */
RELATIVE_OID_decode_xer
,
RELATIVE_OID_encode_xer
,
RELATIVE_OID_encode_xer
,
0
,
/* Use generic outmost tag fetcher */
0
,
/* Use generic outmost tag fetcher */
asn_DEF_RELATIVE_OID_tags
,
asn_DEF_RELATIVE_OID_tags
,
...
@@ -86,6 +86,50 @@ RELATIVE_OID_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
...
@@ -86,6 +86,50 @@ RELATIVE_OID_print(asn_TYPE_descriptor_t *td, const void *sptr, int ilevel,
return
(
cb
(
" }"
,
2
,
app_key
)
<
0
)
?
-
1
:
0
;
return
(
cb
(
" }"
,
2
,
app_key
)
<
0
)
?
-
1
:
0
;
}
}
static
ssize_t
RELATIVE_OID__xer_body_decode
(
void
*
sptr
,
void
*
chunk_buf
,
size_t
chunk_size
)
{
RELATIVE_OID_t
*
st
=
(
RELATIVE_OID_t
*
)
sptr
;
char
*
endptr
;
long
s_arcs
[
6
];
long
*
arcs
=
s_arcs
;
int
arcs_count
;
int
ret
;
arcs_count
=
OBJECT_IDENTIFIER_parse_arcs
(
(
const
char
*
)
chunk_buf
,
chunk_size
,
arcs
,
6
,
&
endptr
);
if
(
arcs_count
<
0
)
return
-
1
;
/* Expecting at least zero arcs */
if
(
arcs_count
>
6
)
{
arcs
=
(
long
*
)
MALLOC
(
arcs_count
*
sizeof
(
long
));
if
(
!
arcs
)
return
-
1
;
ret
=
OBJECT_IDENTIFIER_parse_arcs
(
(
const
char
*
)
chunk_buf
,
chunk_size
,
arcs
,
arcs_count
,
&
endptr
);
if
(
ret
!=
arcs_count
)
return
-
1
;
/* assert?.. */
}
/*
* Convert arcs into BER representation.
*/
ret
=
RELATIVE_OID_set_arcs
(
st
,
arcs
,
sizeof
(
*
arcs
),
arcs_count
);
if
(
ret
)
return
-
1
;
if
(
arcs
!=
s_arcs
)
FREEMEM
(
arcs
);
return
endptr
-
(
char
*
)
chunk_buf
;
}
asn_dec_rval_t
RELATIVE_OID_decode_xer
(
asn_codec_ctx_t
*
opt_codec_ctx
,
asn_TYPE_descriptor_t
*
td
,
void
**
sptr
,
const
char
*
opt_mname
,
void
*
buf_ptr
,
size_t
size
)
{
return
xer_decode_primitive
(
opt_codec_ctx
,
td
,
sptr
,
sizeof
(
RELATIVE_OID_t
),
opt_mname
,
buf_ptr
,
size
,
RELATIVE_OID__xer_body_decode
);
}
asn_enc_rval_t
asn_enc_rval_t
RELATIVE_OID_encode_xer
(
asn_TYPE_descriptor_t
*
td
,
void
*
sptr
,
RELATIVE_OID_encode_xer
(
asn_TYPE_descriptor_t
*
td
,
void
*
sptr
,
int
ilevel
,
enum
xer_encoder_flags_e
flags
,
int
ilevel
,
enum
xer_encoder_flags_e
flags
,
...
...
skeletons/RELATIVE-OID.h
View file @
92302255
...
@@ -13,6 +13,7 @@ typedef OBJECT_IDENTIFIER_t RELATIVE_OID_t;
...
@@ -13,6 +13,7 @@ typedef OBJECT_IDENTIFIER_t RELATIVE_OID_t;
extern
asn_TYPE_descriptor_t
asn_DEF_RELATIVE_OID
;
extern
asn_TYPE_descriptor_t
asn_DEF_RELATIVE_OID
;
asn_struct_print_f
RELATIVE_OID_print
;
asn_struct_print_f
RELATIVE_OID_print
;
xer_type_decoder_f
RELATIVE_OID_decode_xer
;
xer_type_encoder_f
RELATIVE_OID_encode_xer
;
xer_type_encoder_f
RELATIVE_OID_encode_xer
;
/**********************************
/**********************************
...
...
skeletons/tests/check-OIDs.c
View file @
92302255
...
@@ -223,6 +223,50 @@ check_speed() {
...
@@ -223,6 +223,50 @@ check_speed() {
return
0
;
return
0
;
}
}
static
void
check_parse
(
const
char
*
oid_txt
,
int
retval
)
{
int
ret
;
long
l
[
2
];
char
*
p
;
ret
=
OBJECT_IDENTIFIER_parse_arcs
(
oid_txt
,
-
1
,
l
,
2
,
&
p
);
printf
(
"[%s] => %d == %d
\n
"
,
oid_txt
,
ret
,
retval
);
assert
(
ret
==
retval
);
assert
(
p
>=
oid_txt
);
}
static
void
check_xer
(
int
expect_arcs
,
char
*
xer
)
{
asn_dec_rval_t
rc
;
RELATIVE_OID_t
*
st
=
0
;
long
arcs
[
10
];
int
ret
;
int
i
;
printf
(
"[%s] => "
,
xer
);
fflush
(
stdout
);
rc
=
asn_DEF_RELATIVE_OID
.
xer_decoder
(
0
,
&
asn_DEF_RELATIVE_OID
,
(
void
**
)
&
st
,
"t"
,
xer
,
strlen
(
xer
));
if
(
expect_arcs
==
-
1
)
{
if
(
rc
.
code
!=
RC_OK
)
return
;
}
assert
(
rc
.
code
==
RC_OK
);
ret
=
RELATIVE_OID_get_arcs
(
st
,
arcs
,
sizeof
(
arcs
[
0
]),
sizeof
(
arcs
)
/
sizeof
(
arcs
[
0
]));
assert
(
ret
<
10
);
if
(
expect_arcs
==
-
1
)
{
assert
(
ret
==
-
1
);
return
;
}
for
(
i
=
0
;
i
<
ret
;
i
++
)
{
if
(
i
)
printf
(
"."
);
printf
(
"%ld"
,
arcs
[
i
]);
assert
(
arcs
[
i
]
==
i
+
1
);
}
printf
(
": %d == %d
\n
"
,
ret
,
expect_arcs
);
assert
(
ret
==
expect_arcs
);
}
#define CHECK_OID(n) check_OID(buf ## n, sizeof(buf ## n), \
#define CHECK_OID(n) check_OID(buf ## n, sizeof(buf ## n), \
buf ## n ## _check, \
buf ## n ## _check, \
sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0]))
sizeof(buf ## n ## _check)/sizeof(buf ## n ## _check[0]))
...
@@ -334,6 +378,42 @@ main() {
...
@@ -334,6 +378,42 @@ main() {
CHECK_REGEN_OID
(
19
);
CHECK_REGEN_OID
(
19
);
CHECK_REGEN_OID
(
20
);
CHECK_REGEN_OID
(
20
);
check_parse
(
""
,
0
);
check_parse
(
" "
,
0
);
check_parse
(
" "
,
0
);
check_parse
(
"."
,
-
1
);
check_parse
(
" ."
,
-
1
);
check_parse
(
" 1"
,
1
);
check_parse
(
" 1.2"
,
2
);
check_parse
(
" 1."
,
-
1
);
check_parse
(
" 1. "
,
-
1
);
check_parse
(
"1. "
,
-
1
);
check_parse
(
"1.2"
,
2
);
check_parse
(
"10.30.234.234"
,
4
);
check_parse
(
"10.30.234.234 "
,
4
);
check_parse
(
"10.30.234. 234 "
,
-
1
);
check_parse
(
"10.30.234.234."
,
-
1
);
check_parse
(
"1.2000000000.3"
,
3
);
check_parse
(
"1.2147483647.3"
,
3
);
if
(
sizeof
(
long
)
==
4
)
{
check_parse
(
"1.2147483648.3"
,
-
1
);
/* overflow on ILP32 */
check_parse
(
"1.3000000000.3"
,
-
1
);
check_parse
(
"1.4000000000.3"
,
-
1
);
check_parse
(
"1.5000000000.3"
,
-
1
);
check_parse
(
"1.6000000000.3"
,
-
1
);
check_parse
(
"1.9000000000.3"
,
-
1
);
}
else
{
check_parse
(
"1.2147483648.3"
,
3
);
}
check_parse
(
"1.900a0000000.3"
,
-
1
);
check_parse
(
"1.900a.3"
,
-
1
);
check_xer
(
0
,
"<t></t>"
);
check_xer
(
2
,
"<t>1.2</t>"
);
check_xer
(
3
,
"<t>1.2.3</t>"
);
check_xer
(
3
,
"<t> 1.2.3 </t>"
);
check_xer
(
-
1
,
"<t>1.2.3 1</t>"
);
for
(
i
=
0
;
i
<
100000
;
i
++
)
{
for
(
i
=
0
;
i
<
100000
;
i
++
)
{
int
bufA_check
[
3
]
=
{
2
,
i
,
rand
()
};
int
bufA_check
[
3
]
=
{
2
,
i
,
rand
()
};
int
bufB_check
[
2
]
=
{
rand
(),
i
*
121
};
int
bufB_check
[
2
]
=
{
rand
(),
i
*
121
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment