Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
T
tg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
tg
Commits
0819dcf0
Commit
0819dcf0
authored
Nov 01, 2013
by
vysheng
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Somthing fixed. Don't know what
parent
b70f868e
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
143 additions
and
7 deletions
+143
-7
constants.h
constants.h
+5
-1
loop.c
loop.c
+2
-2
mtproto-client.c
mtproto-client.c
+35
-0
mtproto-common.h
mtproto-common.h
+4
-0
queries.c
queries.c
+43
-3
queries.h
queries.h
+1
-0
structures.c
structures.c
+42
-1
structures.h
structures.h
+11
-0
No files found.
constants.h
View file @
0819dcf0
...
@@ -252,10 +252,11 @@
...
@@ -252,10 +252,11 @@
#define CODE_messages_dh_config 0x2c221edd
#define CODE_messages_dh_config 0x2c221edd
#define CODE_messages_sent_encrypted_message 0x560f8935
#define CODE_messages_sent_encrypted_message 0x560f8935
#define CODE_messages_sent_encrypted_file 0x9493ff32
#define CODE_messages_sent_encrypted_file 0x9493ff32
#define CODE_input_file_big 0xfa4f0bb5
#define CODE_input_encrypted_file_big_uploaded 0x2dc173c8
#define CODE_invoke_after_msg 0xcb9f372d
#define CODE_invoke_after_msg 0xcb9f372d
#define CODE_invoke_after_msgs 0x3dc4b4f0
#define CODE_invoke_after_msgs 0x3dc4b4f0
#define CODE_invoke_with_layer1 0x53835315
#define CODE_invoke_with_layer1 0x53835315
#define CODE_init_connection 0x3fc12e08
#define CODE_auth_check_phone 0x6fe51dfb
#define CODE_auth_check_phone 0x6fe51dfb
#define CODE_auth_send_code 0x768d5f4d
#define CODE_auth_send_code 0x768d5f4d
#define CODE_auth_send_call 0x3c51564
#define CODE_auth_send_call 0x3c51564
...
@@ -348,4 +349,7 @@
...
@@ -348,4 +349,7 @@
#define CODE_messages_send_encrypted_service 0x32d439a4
#define CODE_messages_send_encrypted_service 0x32d439a4
#define CODE_messages_received_queue 0x55a5bb66
#define CODE_messages_received_queue 0x55a5bb66
#define CODE_invoke_with_layer8 0xe9abd9fd
#define CODE_invoke_with_layer8 0xe9abd9fd
#define CODE_upload_save_big_file_part 0xde7b673d
#define CODE_init_connection 0x69796de9
#define CODE_invoke_with_layer9 0x76715a63
#endif
#endif
loop.c
View file @
0819dcf0
...
@@ -311,7 +311,7 @@ int loop (void) {
...
@@ -311,7 +311,7 @@ int loop (void) {
perror
(
"getline()"
);
perror
(
"getline()"
);
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
}
}
if
(
!*
code
||
*
code
==
'y'
)
{
if
(
!*
code
||
*
code
==
'y'
||
*
code
==
'Y'
)
{
printf
(
"Ok, starting registartion.
\n
"
);
printf
(
"Ok, starting registartion.
\n
"
);
}
else
{
}
else
{
printf
(
"Then try again
\n
"
);
printf
(
"Then try again
\n
"
);
...
@@ -368,7 +368,7 @@ int loop (void) {
...
@@ -368,7 +368,7 @@ int loop (void) {
rl_attempted_completion_function
=
(
CPPFunction
*
)
complete_text
;
rl_attempted_completion_function
=
(
CPPFunction
*
)
complete_text
;
rl_completion_entry_function
=
complete_none
;
rl_completion_entry_function
=
complete_none
;
do_get_dialog_list
();
do_get_dialog_list
_ex
();
return
main_loop
();
return
main_loop
();
}
}
...
...
mtproto-client.c
View file @
0819dcf0
...
@@ -946,6 +946,41 @@ void work_update (struct connection *c UU, long long msg_id UU) {
...
@@ -946,6 +946,41 @@ void work_update (struct connection *c UU, long long msg_id UU) {
free
(
location
);
free
(
location
);
}
}
break
;
break
;
case
CODE_update_new_geo_chat_message
:
{
struct
message
*
M
=
fetch_alloc_geo_message
();
unread_messages
++
;
print_message
(
M
);
update_prompt
();
}
break
;
case
CODE_update_new_encrypted_message
:
{
logprintf
(
"New encrypted message. Unsupported yet
\n
"
);
}
break
;
case
CODE_update_encryption
:
{
logprintf
(
"New encrypted chat. Unsupported yet
\n
"
);
}
break
;
case
CODE_update_encrypted_chat_typing
:
{
logprintf
(
"Typing in encrypted chat. Unsupported yet
\n
"
);
}
break
;
case
CODE_update_encrypted_messages_read
:
{
fetch_int
();
// chat_id
fetch_int
();
// max_date
fetch_int
();
// date
print_start
();
push_color
(
COLOR_YELLOW
);
printf
(
"Messages in encrypted chat mark read
\n
"
);
pop_color
();
print_end
();
}
break
;
default:
default:
logprintf
(
"Unknown update type %08x
\n
"
,
op
);
logprintf
(
"Unknown update type %08x
\n
"
,
op
);
}
}
...
...
mtproto-common.h
View file @
0819dcf0
...
@@ -377,4 +377,8 @@ int pad_aes_decrypt (char *from, int from_len, char *to, int size);
...
@@ -377,4 +377,8 @@ int pad_aes_decrypt (char *from, int from_len, char *to, int size);
static
inline
void
hexdump_in
(
void
)
{
static
inline
void
hexdump_in
(
void
)
{
hexdump
(
in_ptr
,
in_end
);
hexdump
(
in_ptr
,
in_end
);
}
}
static
inline
void
hexdump_out
(
void
)
{
hexdump
(
packet_buffer
,
packet_ptr
);
}
#endif
#endif
queries.c
View file @
0819dcf0
...
@@ -25,6 +25,7 @@
...
@@ -25,6 +25,7 @@
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include "include.h"
#include "include.h"
#include "mtproto-client.h"
#include "mtproto-client.h"
...
@@ -35,6 +36,7 @@
...
@@ -35,6 +36,7 @@
#include "loop.h"
#include "loop.h"
#include "structures.h"
#include "structures.h"
#include "interface.h"
#include "interface.h"
#include "net.h"
char
*
get_downloads_directory
(
void
);
char
*
get_downloads_directory
(
void
);
int
verbosity
;
int
verbosity
;
...
@@ -280,7 +282,7 @@ int help_get_config_on_answer (struct query *q UU) {
...
@@ -280,7 +282,7 @@ int help_get_config_on_answer (struct query *q UU) {
unsigned
test_mode
=
fetch_int
();
unsigned
test_mode
=
fetch_int
();
assert
(
test_mode
==
CODE_bool_true
||
test_mode
==
CODE_bool_false
);
assert
(
test_mode
==
CODE_bool_true
||
test_mode
==
CODE_bool_false
);
assert
(
test_mode
==
CODE_bool_false
);
assert
(
test_mode
==
CODE_bool_false
||
test_mode
==
CODE_bool_true
);
int
this_dc
=
fetch_int
();
int
this_dc
=
fetch_int
();
if
(
verbosity
)
{
if
(
verbosity
)
{
logprintf
(
"this_dc = %d
\n
"
,
this_dc
);
logprintf
(
"this_dc = %d
\n
"
,
this_dc
);
...
@@ -325,7 +327,7 @@ void do_help_get_config (void) {
...
@@ -325,7 +327,7 @@ void do_help_get_config (void) {
char
*
phone_code_hash
;
char
*
phone_code_hash
;
int
send_code_on_answer
(
struct
query
*
q
UU
)
{
int
send_code_on_answer
(
struct
query
*
q
UU
)
{
assert
(
fetch_int
()
==
CODE_auth_sent_code
);
assert
(
fetch_int
()
==
CODE_auth_sent_code
);
assert
(
fetch_bool
()
);
fetch_bool
(
);
int
l
=
prefetch_strlen
();
int
l
=
prefetch_strlen
();
char
*
s
=
fetch_str
(
l
);
char
*
s
=
fetch_str
(
l
);
if
(
phone_code_hash
)
{
if
(
phone_code_hash
)
{
...
@@ -338,9 +340,13 @@ int send_code_on_answer (struct query *q UU) {
...
@@ -338,9 +340,13 @@ int send_code_on_answer (struct query *q UU) {
int
send_code_on_error
(
struct
query
*
q
UU
,
int
error_code
,
int
l
,
char
*
error
)
{
int
send_code_on_error
(
struct
query
*
q
UU
,
int
error_code
,
int
l
,
char
*
error
)
{
int
s
=
strlen
(
"PHONE_MIGRATE_"
);
int
s
=
strlen
(
"PHONE_MIGRATE_"
);
int
s2
=
strlen
(
"NETWORK_MIGRATE_"
);
if
(
l
>=
s
&&
!
memcmp
(
error
,
"PHONE_MIGRATE_"
,
s
))
{
if
(
l
>=
s
&&
!
memcmp
(
error
,
"PHONE_MIGRATE_"
,
s
))
{
int
i
=
error
[
s
]
-
'0'
;
int
i
=
error
[
s
]
-
'0'
;
want_dc_num
=
i
;
want_dc_num
=
i
;
}
else
if
(
l
>=
s2
&&
!
memcmp
(
error
,
"NETWORK_MIGRATE_"
,
s2
))
{
int
i
=
error
[
s2
]
-
'0'
;
want_dc_num
=
i
;
}
else
{
}
else
{
logprintf
(
"error_code = %d, error = %.*s
\n
"
,
error_code
,
l
,
error
);
logprintf
(
"error_code = %d, error = %.*s
\n
"
,
error_code
,
l
,
error
);
assert
(
0
);
assert
(
0
);
...
@@ -364,9 +370,11 @@ int config_got (void) {
...
@@ -364,9 +370,11 @@ int config_got (void) {
char
*
suser
;
char
*
suser
;
extern
int
dc_working_num
;
extern
int
dc_working_num
;
void
do_send_code
(
const
char
*
user
)
{
void
do_send_code
(
const
char
*
user
)
{
logprintf
(
"sending code
\n
"
);
suser
=
strdup
(
user
);
suser
=
strdup
(
user
);
want_dc_num
=
0
;
want_dc_num
=
0
;
clear_packet
();
clear_packet
();
out_int
(
CODE_invoke_with_layer6
);
out_int
(
CODE_auth_send_code
);
out_int
(
CODE_auth_send_code
);
out_string
(
user
);
out_string
(
user
);
out_int
(
0
);
out_int
(
0
);
...
@@ -374,6 +382,7 @@ void do_send_code (const char *user) {
...
@@ -374,6 +382,7 @@ void do_send_code (const char *user) {
out_string
(
TG_APP_HASH
);
out_string
(
TG_APP_HASH
);
out_string
(
"en"
);
out_string
(
"en"
);
logprintf
(
"send_code: dc_num = %d
\n
"
,
dc_working_num
);
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
send_code_methods
,
0
);
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
send_code_methods
,
0
);
net_loop
(
0
,
code_is_sent
);
net_loop
(
0
,
code_is_sent
);
if
(
want_dc_num
==
-
1
)
{
return
;
}
if
(
want_dc_num
==
-
1
)
{
return
;
}
...
@@ -383,8 +392,10 @@ void do_send_code (const char *user) {
...
@@ -383,8 +392,10 @@ void do_send_code (const char *user) {
dc_create_session
(
DC_working
);
dc_create_session
(
DC_working
);
}
}
dc_working_num
=
want_dc_num
;
dc_working_num
=
want_dc_num
;
logprintf
(
"send_code: dc_num = %d
\n
"
,
dc_working_num
);
want_dc_num
=
0
;
want_dc_num
=
0
;
clear_packet
();
clear_packet
();
out_int
(
CODE_invoke_with_layer6
);
out_int
(
CODE_auth_send_code
);
out_int
(
CODE_auth_send_code
);
out_string
(
user
);
out_string
(
user
);
out_int
(
0
);
out_int
(
0
);
...
@@ -420,7 +431,7 @@ int check_phone_on_error (struct query *q UU, int error_code, int l, char *error
...
@@ -420,7 +431,7 @@ int check_phone_on_error (struct query *q UU, int error_code, int l, char *error
DC_working
=
DC_list
[
i
];
DC_working
=
DC_list
[
i
];
write_auth_file
();
write_auth_file
();
check_phone_result
=
1
;
check_phone_result
=
1
;
}
else
if
(
l
>=
s2
&&
!
memcmp
(
error
,
"NETWORK_MIGRATE_"
,
s
))
{
}
else
if
(
l
>=
s2
&&
!
memcmp
(
error
,
"NETWORK_MIGRATE_"
,
s
2
))
{
int
i
=
error
[
s2
]
-
'0'
;
int
i
=
error
[
s2
]
-
'0'
;
assert
(
DC_list
[
i
]);
assert
(
DC_list
[
i
]);
dc_working_num
=
i
;
dc_working_num
=
i
;
...
@@ -746,6 +757,7 @@ void do_get_history (peer_id_t id, int limit) {
...
@@ -746,6 +757,7 @@ void do_get_history (peer_id_t id, int limit) {
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
get_history_methods
,
(
void
*
)
*
(
long
*
)
&
id
);
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
get_history_methods
,
(
void
*
)
*
(
long
*
)
&
id
);
}
}
int
get_dialogs_on_answer
(
struct
query
*
q
UU
)
{
int
get_dialogs_on_answer
(
struct
query
*
q
UU
)
{
unsigned
x
=
fetch_int
();
unsigned
x
=
fetch_int
();
assert
(
x
==
CODE_messages_dialogs
||
x
==
CODE_messages_dialogs_slice
);
assert
(
x
==
CODE_messages_dialogs
||
x
==
CODE_messages_dialogs_slice
);
...
@@ -823,6 +835,34 @@ void do_get_dialog_list (void) {
...
@@ -823,6 +835,34 @@ void do_get_dialog_list (void) {
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
get_dialogs_methods
,
0
);
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
get_dialogs_methods
,
0
);
}
}
int
allow_send_linux_version
=
1
;
void
do_get_dialog_list_ex
(
void
)
{
clear_packet
();
out_int
(
CODE_invoke_with_layer9
);
out_int
(
CODE_init_connection
);
out_int
(
TG_APP_ID
);
if
(
allow_send_linux_version
)
{
struct
utsname
st
;
uname
(
&
st
);
out_string
(
st
.
machine
);
static
char
buf
[
1000000
];
sprintf
(
buf
,
"%s %s %s"
,
st
.
sysname
,
st
.
release
,
st
.
version
);
out_string
(
buf
);
out_string
(
TG_VERSION
" (build "
TG_BUILD
")"
);
out_string
(
"En"
);
}
else
{
out_string
(
"A"
);
out_string
(
"L"
);
out_string
(
"T"
);
out_string
(
"en"
);
}
out_int
(
CODE_messages_get_dialogs
);
out_int
(
0
);
out_int
(
0
);
out_int
(
100
);
send_query
(
DC_working
,
packet_ptr
-
packet_buffer
,
packet_buffer
,
&
get_dialogs_methods
,
0
);
}
struct
send_file
{
struct
send_file
{
int
fd
;
int
fd
;
long
long
size
;
long
long
size
;
...
...
queries.h
View file @
0819dcf0
...
@@ -73,6 +73,7 @@ void do_send_message (peer_id_t id, const char *msg, int len);
...
@@ -73,6 +73,7 @@ void do_send_message (peer_id_t id, const char *msg, int len);
void
do_send_text
(
peer_id_t
id
,
char
*
file
);
void
do_send_text
(
peer_id_t
id
,
char
*
file
);
void
do_get_history
(
peer_id_t
id
,
int
limit
);
void
do_get_history
(
peer_id_t
id
,
int
limit
);
void
do_get_dialog_list
(
void
);
void
do_get_dialog_list
(
void
);
void
do_get_dialog_list_ex
(
void
);
void
do_send_photo
(
int
type
,
peer_id_t
to_id
,
char
*
file_name
);
void
do_send_photo
(
int
type
,
peer_id_t
to_id
,
char
*
file_name
);
void
do_get_chat_info
(
peer_id_t
id
);
void
do_get_chat_info
(
peer_id_t
id
);
void
do_get_user_list_info_silent
(
int
num
,
int
*
list
);
void
do_get_user_list_info_silent
(
int
num
,
int
*
list
);
...
...
structures.c
View file @
0819dcf0
...
@@ -179,7 +179,7 @@ void fetch_user (struct user *U) {
...
@@ -179,7 +179,7 @@ void fetch_user (struct user *U) {
}
}
fetch_user_status
(
&
U
->
status
);
fetch_user_status
(
&
U
->
status
);
if
(
x
==
CODE_user_self
)
{
if
(
x
==
CODE_user_self
)
{
assert
(
fetch_int
()
==
(
int
)
CODE_bool_false
);
fetch_bool
(
);
}
}
if
(
x
==
CODE_user_contact
)
{
if
(
x
==
CODE_user_contact
)
{
U
->
flags
|=
FLAG_USER_CONTACT
;
U
->
flags
|=
FLAG_USER_CONTACT
;
...
@@ -532,6 +532,27 @@ void fetch_message (struct message *M) {
...
@@ -532,6 +532,27 @@ void fetch_message (struct message *M) {
}
}
}
}
void
fetch_geo_message
(
struct
message
*
M
)
{
memset
(
M
,
0
,
sizeof
(
*
M
));
unsigned
x
=
fetch_int
();
assert
(
x
==
CODE_geo_chat_message_empty
||
x
==
CODE_geo_chat_message
||
x
==
CODE_geo_chat_message_service
);
M
->
to_id
=
MK_GEO_CHAT
(
fetch_int
());
M
->
id
=
fetch_int
();
if
(
x
==
CODE_geo_chat_message_empty
)
{
M
->
flags
|=
1
;
return
;
}
M
->
from_id
=
MK_USER
(
fetch_int
());
M
->
date
=
fetch_int
();
if
(
x
==
CODE_geo_chat_message_service
)
{
M
->
service
=
1
;
fetch_message_action
(
&
M
->
action
);
}
else
{
M
->
message
=
fetch_str_dup
();
fetch_message_media
(
&
M
->
media
);
}
}
#define id_cmp(a,b) ((a)->id - (b)->id)
#define id_cmp(a,b) ((a)->id - (b)->id)
#define peer_cmp(a,b) (cmp_peer_id (a->id, b->id))
#define peer_cmp(a,b) (cmp_peer_id (a->id, b->id))
...
@@ -711,6 +732,26 @@ struct message *fetch_alloc_message (void) {
...
@@ -711,6 +732,26 @@ struct message *fetch_alloc_message (void) {
}
}
}
}
struct
message
*
fetch_alloc_geo_message
(
void
)
{
struct
message
*
M
=
malloc
(
sizeof
(
*
M
));
fetch_geo_message
(
M
);
struct
message
*
M1
=
tree_lookup_message
(
message_tree
,
M
);
messages_allocated
++
;
if
(
M1
)
{
message_del_use
(
M1
);
free_message
(
M1
);
memcpy
(
M1
,
M
,
sizeof
(
*
M
));
free
(
M
);
message_add_use
(
M1
);
messages_allocated
--
;
return
M1
;
}
else
{
message_add_use
(
M
);
message_tree
=
tree_insert_message
(
message_tree
,
M
,
lrand48
());
return
M
;
}
}
struct
message
*
fetch_alloc_message_short
(
void
)
{
struct
message
*
fetch_alloc_message_short
(
void
)
{
struct
message
*
M
=
malloc
(
sizeof
(
*
M
));
struct
message
*
M
=
malloc
(
sizeof
(
*
M
));
fetch_message_short
(
M
);
fetch_message_short
(
M
);
...
...
structures.h
View file @
0819dcf0
...
@@ -198,6 +198,7 @@ struct user *fetch_alloc_user_full (void);
...
@@ -198,6 +198,7 @@ struct user *fetch_alloc_user_full (void);
struct
chat
*
fetch_alloc_chat
(
void
);
struct
chat
*
fetch_alloc_chat
(
void
);
struct
chat
*
fetch_alloc_chat_full
(
void
);
struct
chat
*
fetch_alloc_chat_full
(
void
);
struct
message
*
fetch_alloc_message
(
void
);
struct
message
*
fetch_alloc_message
(
void
);
struct
message
*
fetch_alloc_geo_message
(
void
);
struct
message
*
fetch_alloc_message_short
(
void
);
struct
message
*
fetch_alloc_message_short
(
void
);
struct
message
*
fetch_alloc_message_short_chat
(
void
);
struct
message
*
fetch_alloc_message_short_chat
(
void
);
peer_id_t
fetch_peer_id
(
void
);
peer_id_t
fetch_peer_id
(
void
);
...
@@ -215,15 +216,20 @@ void fetch_photo (struct photo *P);
...
@@ -215,15 +216,20 @@ void fetch_photo (struct photo *P);
#define PEER_USER 1
#define PEER_USER 1
#define PEER_CHAT 2
#define PEER_CHAT 2
#define PEER_GEO_CHAT 3
#define PEER_UNKNOWN 0
#define PEER_UNKNOWN 0
#define MK_USER(id) set_peer_id (PEER_USER,id)
#define MK_USER(id) set_peer_id (PEER_USER,id)
#define MK_CHAT(id) set_peer_id (PEER_CHAT,id)
#define MK_CHAT(id) set_peer_id (PEER_CHAT,id)
#define MK_GEO_CHAT(id) set_peer_id (PEER_GEO_CHAT,id)
static
inline
int
get_peer_type
(
peer_id_t
id
)
{
static
inline
int
get_peer_type
(
peer_id_t
id
)
{
if
(
id
.
id
>
0
)
{
if
(
id
.
id
>
0
)
{
return
PEER_USER
;
return
PEER_USER
;
}
}
if
(
id
.
id
<
-
1000000000
)
{
return
PEER_GEO_CHAT
;
}
if
(
id
.
id
<
0
)
{
if
(
id
.
id
<
0
)
{
return
PEER_CHAT
;
return
PEER_CHAT
;
}
}
...
@@ -236,6 +242,8 @@ static inline int get_peer_id (peer_id_t id) {
...
@@ -236,6 +242,8 @@ static inline int get_peer_id (peer_id_t id) {
return
id
.
id
;
return
id
.
id
;
case
PEER_CHAT
:
case
PEER_CHAT
:
return
-
id
.
id
;
return
-
id
.
id
;
case
PEER_GEO_CHAT
:
return
-
id
.
id
-
1000000000
;
default:
default:
return
0
;
return
0
;
}
}
...
@@ -250,6 +258,9 @@ static inline peer_id_t set_peer_id (int type, int id) {
...
@@ -250,6 +258,9 @@ static inline peer_id_t set_peer_id (int type, int id) {
case
PEER_CHAT
:
case
PEER_CHAT
:
ID
.
id
=
-
id
;
ID
.
id
=
-
id
;
return
ID
;
return
ID
;
case
PEER_GEO_CHAT
:
ID
.
id
=
-
id
-
1000000000
;
return
ID
;
default:
default:
assert
(
0
);
assert
(
0
);
return
ID
;
return
ID
;
...
...
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