Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
client
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
2
Merge Requests
0
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
8ac7e289
authored
Dec 14, 2018
by
Belákovics Ádám
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
removed NX from code
parent
ed03b793
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
7 additions
and
312 deletions
+7
-312
README
+2
-2
src/nsi/cloud.nsi
+3
-29
src/nsi/installer/cloud.py
+1
-1
src/nsi/installer/cloud_connect_from_windows.py
+1
-47
src/nsi/installer/install.cmd
+0
-1
src/nsi/installer/nx_client_installer.py
+0
-69
src/nsi/installer/nxclient-3.5.0-9.exe
+0
-0
src/nsi/installer/nxkey.py
+0
-158
src/nsi/installer/win_install.py
+0
-5
No files found.
README
View file @
8ac7e289
...
@@ -2,8 +2,8 @@ Desktop client for CIRCLE, providing single click connection to the VMs further
...
@@ -2,8 +2,8 @@ Desktop client for CIRCLE, providing single click connection to the VMs further
Python based application using third party softwares.
Python based application using third party softwares.
Tested and made for:
Tested and made for:
- Debian based Linux systems. [RDP,
NX - Remmina based,
SSH - built in console]
- Debian based Linux systems. [RDP, SSH - built in console]
- Windows Vista 32/64 or newer. [RDP - windows built in,
NX - NoMachine NX Client,
SSH - Putty]
- Windows Vista 32/64 or newer. [RDP - windows built in, SSH - Putty]
To install the client Root rights are necessary.
To install the client Root rights are necessary.
The installation guide can be found at the CIRCLE running site.
The installation guide can be found at the CIRCLE running site.
...
...
src/nsi/cloud.nsi
View file @
8ac7e289
...
@@ -183,17 +183,7 @@
...
@@ -183,17 +183,7 @@
To procede please uninstall Python first."
To procede please uninstall Python first."
LangString STATUS_PythonArchitectNotFound ${LANG_HUNGARIAN} "Nem siker�lt kider�teni a feltelep�tett Python m�dj�t!$\r$\n\
LangString STATUS_PythonArchitectNotFound ${LANG_HUNGARIAN} "Nem siker�lt kider�teni a feltelep�tett Python m�dj�t!$\r$\n\
A sikeres telep�t�shez el kell el�bb t�vol�tania a Python-t."
A sikeres telep�t�shez el kell el�bb t�vol�tania a Python-t."
;NX Client (NXClient)
;NAME
LangString NAME_NXClient ${LANG_ENGLISH} "NX Client"
LangString NAME_NXClient ${LANG_HUNGARIAN} "NX Kliens"
;DESC
LangString DESC_NXClient ${LANG_ENGLISH} "Check whether you want to install a CIRCLE compatible NX Client by NoMachine.$\r$\n\
Used for [NX] type of connections."
LangString DESC_NXClient ${LANG_HUNGARIAN} "V�lassza ki, ha fel szeretn� telep�teni a NoMachine �ltal gy�rtott NX Klienst.$\r$\n\
[NX] f�le kapcsolatokhoz haszn�lt."
;Setup
;Setup
LangString STATUS_ExecutingScript ${LANG_ENGLISH} "Executing setup script. Log files can be found at:"
LangString STATUS_ExecutingScript ${LANG_ENGLISH} "Executing setup script. Log files can be found at:"
...
@@ -204,7 +194,6 @@
...
@@ -204,7 +194,6 @@
Var /GLOBAL python_version
Var /GLOBAL python_version
Var /GLOBAL python_architect
Var /GLOBAL python_architect
Var /GLOBAL found_python
Var /GLOBAL found_python
Var /GLOBAL nx_install
Var /GLOBAL append
Var /GLOBAL append
Var /GLOBAL running_directory
Var /GLOBAL running_directory
;--------------------------------
;--------------------------------
...
@@ -215,7 +204,7 @@ SectionGroup /e '!$(NAME_Install)'
...
@@ -215,7 +204,7 @@ SectionGroup /e '!$(NAME_Install)'
SetOutPath "$INSTDIR"
SetOutPath "$INSTDIR"
;ADD OWN FILES HERE----------------------------------------
;ADD OWN FILES HERE----------------------------------------
File /r
/x *nxclient-3.5.0-9.exe /x *nx_client_installer.py
installer
File /r installer
File /r uninstaller
File /r uninstaller
;Initialize Running Directory
;Initialize Running Directory
...
@@ -242,11 +231,6 @@ SectionGroup /e '!$(NAME_Install)'
...
@@ -242,11 +231,6 @@ SectionGroup /e '!$(NAME_Install)'
CreateShortCut '$SMPROGRAMS\${Company}\${AppName}\Uninstall ${AppName}.lnk' '$INSTDIR\${AppUninstaller}' "" '$INSTDIR\${AppUninstaller}' 0
CreateShortCut '$SMPROGRAMS\${Company}\${AppName}\Uninstall ${AppName}.lnk' '$INSTDIR\${AppUninstaller}' "" '$INSTDIR\${AppUninstaller}' 0
SectionEnd
SectionEnd
Section /o $(NAME_NXClient) NXClient
SetOutPath "$INSTDIR\installer"
File 'installer\nxclient-3.5.0-9.exe'
File 'installer\nx_client_installer.py'
SectionEnd
SectionGroupEnd
SectionGroupEnd
SectionGroup /e $(NAME_Functions)
SectionGroup /e $(NAME_Functions)
Section /o $(NAME_PythonLookup) PythonLookup
Section /o $(NAME_PythonLookup) PythonLookup
...
@@ -357,17 +341,10 @@ Function PythonSearch
...
@@ -357,17 +341,10 @@ Function PythonSearch
Push $6
Push $6
FunctionEnd
FunctionEnd
Function Done
Function Done
SectionGetFlags ${NXClient} $9
IntOp $9 $9 & ${SF_SELECTED}
IntCmp $9 ${SF_SELECTED} equal
StrCpy $nx_install "False"
Goto done
equal:
StrCpy $nx_install "True"
done:
done:
DetailPrint '$(STATUS_ExecutingScript) $INSTDIR\install(_error).log'
DetailPrint '$(STATUS_ExecutingScript) $INSTDIR\install(_error).log'
Sleep ${LogInformationTime}
Sleep ${LogInformationTime}
ExecWait '"$INSTDIR\installer\install.cmd" "$INSTDIR" "$running_directory" ${Show_output}
$nx_install
"${AppUrl}" $append >"$INSTDIR\install.log" 2>"$INSTDIR\install_error.log"'
ExecWait '"$INSTDIR\installer\install.cmd" "$INSTDIR" "$running_directory" ${Show_output}
"false"
"${AppUrl}" $append >"$INSTDIR\install.log" 2>"$INSTDIR\install_error.log"'
RMDir /r "$INSTDIR\installer"
RMDir /r "$INSTDIR\installer"
FunctionEnd
FunctionEnd
Function CallbackFunction
Function CallbackFunction
...
@@ -862,7 +839,6 @@ FunctionEnd
...
@@ -862,7 +839,6 @@ FunctionEnd
;Assign language strings to sections
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)
!insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall)
!insertmacro MUI_DESCRIPTION_TEXT ${NXClient} $(DESC_NXClient)
!insertmacro MUI_DESCRIPTION_TEXT ${PythonLookup} $(DESC_PythonLookup)
!insertmacro MUI_DESCRIPTION_TEXT ${PythonLookup} $(DESC_PythonLookup)
!insertmacro MUI_FUNCTION_DESCRIPTION_END
!insertmacro MUI_FUNCTION_DESCRIPTION_END
...
@@ -902,8 +878,6 @@ Section "Uninstall"
...
@@ -902,8 +878,6 @@ Section "Uninstall"
Delete "$INSTDIR\cloud.py"
Delete "$INSTDIR\cloud.py"
IfFileExists "$INSTDIR\cloud_connect_from_windows.py" 0 +2
IfFileExists "$INSTDIR\cloud_connect_from_windows.py" 0 +2
Delete "$INSTDIR\cloud_connect_from_windows.py"
Delete "$INSTDIR\cloud_connect_from_windows.py"
IfFileExists "$INSTDIR\nxkey.py" 0 +2
Delete "$INSTDIR\nxkey.py"
IfFileExists "$INSTDIR\OrderedDict.py" 0 +2
IfFileExists "$INSTDIR\OrderedDict.py" 0 +2
Delete "$INSTDIR\OrderedDict.py"
Delete "$INSTDIR\OrderedDict.py"
IfFileExists "$INSTDIR\putty.exe" 0 +2
IfFileExists "$INSTDIR\putty.exe" 0 +2
...
...
src/nsi/installer/cloud.py
View file @
8ac7e289
...
@@ -21,7 +21,7 @@ class Struct:
...
@@ -21,7 +21,7 @@ class Struct:
Keyword arguments:
Keyword arguments:
state -- State of the Virtual Computer (running, etc..)
state -- State of the Virtual Computer (running, etc..)
protocol -- SSH
, NX
and RDP possible
protocol -- SSH and RDP possible
host -- Address of the Virtual Computer
host -- Address of the Virtual Computer
port -- The port where we can access the Virtual Computer
port -- The port where we can access the Virtual Computer
user -- Username used for the connection
user -- Username used for the connection
...
...
src/nsi/installer/cloud_connect_from_windows.py
View file @
8ac7e289
...
@@ -16,7 +16,6 @@ import subprocess
...
@@ -16,7 +16,6 @@ import subprocess
import
time
import
time
import
tempfile
import
tempfile
import
nxkey
import
win32crypt
import
win32crypt
from
windowsclasses
import
ClientRegistry
from
windowsclasses
import
ClientRegistry
...
@@ -27,7 +26,7 @@ def connect(vm):
...
@@ -27,7 +26,7 @@ def connect(vm):
machine
machine
Keyword arguments:
Keyword arguments:
vm.protocol -- SSH
, NX
and RDP possible
vm.protocol -- SSH and RDP possible
vm.host -- Address of the Virtual Computer
vm.host -- Address of the Virtual Computer
vm.port -- The port where we can access the Virtual Computer
vm.port -- The port where we can access the Virtual Computer
vm.user -- Username used for the connection
vm.user -- Username used for the connection
...
@@ -49,33 +48,6 @@ def connect(vm):
...
@@ -49,33 +48,6 @@ def connect(vm):
subprocess
.
Popen
(
'"
%(path)
s
\\
putty.exe"
%(arguments)
s'
%
{
subprocess
.
Popen
(
'"
%(path)
s
\\
putty.exe"
%(arguments)
s'
%
{
'path'
:
directory
,
'path'
:
directory
,
'arguments'
:
arguments
},
shell
=
True
)
'arguments'
:
arguments
},
shell
=
True
)
elif
vm
.
protocol
==
"NX"
:
logger
.
info
(
'NX protocol received'
)
listdir
=
os
.
path
.
expanduser
(
"~
\\
.nx
\\
config
\\
*.nxs"
)
found
=
False
server
=
"
\"
Server host
\"
value=
\"
%
s
\"
"
%
vm
.
host
port
=
"
\"
Server port
\"
value=
\"
%
s
\"
"
%
vm
.
port
for
config_file
in
glob
.
glob
(
listdir
):
with
open
(
config_file
)
as
f
:
file
=
f
.
read
()
if
server
in
file
and
port
in
file
:
found
=
True
logger
.
info
(
'Config file found:
%
s'
,
config_file
)
break
if
not
found
:
logger
.
info
(
'No config file found, creating new one'
)
config_file
=
"
%
s
%
s
%
s"
%
(
os
.
path
.
expanduser
(
"~
\\
.nx
\\
config
\\
"
),
str
(
int
(
time
.
time
()
*
1000
)),
".nxs"
)
password
=
nxkey
.
NXKeyGen
(
vm
.
password
)
.
getEncrypted
()
config
=
NX_template
%
{
'USERNAME'
:
vm
.
user
,
'PASSWORD'
:
password
,
'HOST'
:
vm
.
host
,
'PORT'
:
vm
.
port
}
f
=
open
(
config_file
,
'w'
)
f
.
write
(
config
)
f
.
close
()
logger
.
info
(
'Config file created:
%
s'
,
config_file
)
logger
.
debug
(
'Popen the config file:
%
s'
,
config_file
)
subprocess
.
Popen
((
u'"
%
s"'
%
config_file
)
.
encode
(
locale
.
getpreferredencoding
()),
shell
=
True
)
elif
vm
.
protocol
==
"RDP"
:
elif
vm
.
protocol
==
"RDP"
:
logger
.
debug
(
'RDP protocol received'
)
logger
.
debug
(
'RDP protocol received'
)
full_address
=
"full address:s:
%
s:
%
s"
%
(
vm
.
host
,
vm
.
port
)
full_address
=
"full address:s:
%
s:
%
s"
%
(
vm
.
host
,
vm
.
port
)
...
@@ -97,24 +69,6 @@ def connect(vm):
...
@@ -97,24 +69,6 @@ def connect(vm):
os
.
remove
(
config_file
)
os
.
remove
(
config_file
)
logger
.
info
(
'Client finished working'
)
logger
.
info
(
'Client finished working'
)
NX_template
=
"""<!DOCTYPE NXClientSettings>
<NXClientSettings application="nxclient" version="1.3" >
<group name="General" >
<option key="Remember password" value="true" />
<option key="Resolution" value="fullscreen" />
<option key="Server host" value="
%(HOST)
s" />
<option key="Server port" value="
%(PORT)
s" />
<option key="Session" value="unix" />
</group>
<group name="Login" >
<option key="Auth" value="
%(PASSWORD)
s" />
<option key="Guest Mode" value="false" />
<option key="Login Method" value="nx" />
<option key="User" value="
%(USERNAME)
s" />
</group>
</NXClientSettings>"""
RPD_template
=
"""username:s:
%(USERNAME)
s
RPD_template
=
"""username:s:
%(USERNAME)
s
full address:s:
%(HOST)
s:
%(PORT)
s
full address:s:
%(HOST)
s:
%(PORT)
s
authentication level:i:0
authentication level:i:0
...
...
src/nsi/installer/install.cmd
View file @
8ac7e289
...
@@ -329,7 +329,6 @@ IF NOT "!output_on_screen!"=="False" (
...
@@ -329,7 +329,6 @@ IF NOT "!output_on_screen!"=="False" (
xcopy "!running_directory!cloud_connect_from_windows.py" "!install_location!\" /y
xcopy "!running_directory!cloud_connect_from_windows.py" "!install_location!\" /y
xcopy "!running_directory!win_install.py" "!install_location!\" /y
xcopy "!running_directory!win_install.py" "!install_location!\" /y
xcopy "!running_directory!windowsclasses.py" "!install_location!\" /y
xcopy "!running_directory!windowsclasses.py" "!install_location!\" /y
xcopy "!running_directory!nxkey.py" "!install_location!\" /y
xcopy "!running_directory!OrderedDict.py" "!install_location!\" /y
xcopy "!running_directory!OrderedDict.py" "!install_location!\" /y
xcopy "!running_directory!putty.exe" "!install_location!\" /y
xcopy "!running_directory!putty.exe" "!install_location!\" /y
IF NOT "!output_on_screen!"=="False" (
IF NOT "!output_on_screen!"=="False" (
...
...
src/nsi/installer/nx_client_installer.py
deleted
100644 → 0
View file @
ed03b793
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
NX Client for Windows installer
Checks whether NX Client for Windows is installed in the system, the
classes used for this process are used for other operations too.
"""
import
os
import
argparse
import
subprocess
import
time
import
windowsclasses
def
parse_arguments
():
"""
Argument parser, based on argparse module
Keyword arguments:
@return args -- arguments given by console
"""
parser
=
argparse
.
ArgumentParser
()
if
windowsclasses
.
DecideArchitecture
.
Is64Windows
():
local_default
=
(
windowsclasses
.
DecideArchitecture
.
GetProgramFiles64
()
+
"
\\
CIRCLE
\\
"
)
else
:
local_default
=
(
windowsclasses
.
DecideArchitecture
.
GetProgramFiles32
()
+
"
\\
CIRCLE
\\
"
)
if
(
not
os
.
path
.
exists
(
local_default
[:
-
1
])
and
os
.
path
.
exists
(
os
.
environ
[
'APPDATA'
]
+
"
\\
CIRCLE"
)):
local_default
=
os
.
environ
[
'APPDATA'
]
+
"
\\
CIRCLE
\\
"
parser
.
add_argument
(
"-l"
,
"--location"
,
help
=
"Location of the client files in the system"
,
default
=
local_default
)
args
=
parser
.
parse_args
()
return
args
def
main
():
try
:
nx_install_location
=
None
while
nx_install_location
is
None
:
print
"Checking whether NX Client for Windows is installed"
handler
=
windowsclasses
.
RegistryHandler
()
try
:
nx_install_location
=
handler
.
get_key_value
(
"SOFTWARE
\\
Microsoft
\\
Windows
\\
CurrentVersion
\\
"
+
"Uninstall
\\
nxclient_is1"
,
"InstallLocation"
,
"key"
)
print
(
"NX Client for Windows is found at "
"'
%
s'"
%
nx_install_location
)
process
=
subprocess
.
Popen
(
"
%
s
\\
nxclient.exe"
%
nx_install_location
)
time
.
sleep
(
2
)
process
.
terminate
()
except
:
print
"NX Client for Windows isn't installed on the system."
print
"
\t
Commencing the install"
subprocess
.
Popen
(
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
+
"
\\
nxclient-3.5.0-9.exe"
)
.
wait
()
except
:
pass
if
__name__
==
"__main__"
:
main
()
src/nsi/installer/nxclient-3.5.0-9.exe
deleted
100644 → 0
View file @
ed03b793
File deleted
src/nsi/installer/nxkey.py
deleted
100644 → 0
View file @
ed03b793
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Generating NoMachine NX scrambled key according to
https://www.nomachine.com/AR01C00125
"""
import
sys
import
random
from
xml.sax.saxutils
import
escape
class
NXKeyGen
:
"""
NXKeyGen class
Creates NoMachine NX scrambled keys
"""
numValidCharList
=
85
dummyString
=
"{{{{"
def
__init__
(
self
,
password
):
"""
Initialize the class
Keyword arguments:
@param password -- Password that will be scrambled
"""
self
.
password
=
password
def
getEncrypted
(
self
):
"""
Encrypt (scramble) the given password
Keyword arguments:
@return scrambleString -- Scrambled version of the original
password
"""
return
self
.
scrambleString
(
self
.
password
)
def
getvalidCharList
(
self
,
pos
):
"""
Valid character list
Keyword arguments:
@return validcharlist -- List of the valid characters
"""
validcharlist
=
[
"!"
,
"#"
,
"$"
,
"
%
"
,
"&"
,
"("
,
")"
,
"*"
,
"+"
,
"-"
,
"."
,
"0"
,
"1"
,
"2"
,
"3"
,
"4"
,
"5"
,
"6"
,
"7"
,
"8"
,
"9"
,
":"
,
";"
,
"<"
,
">"
,
"?"
,
"@"
,
"A"
,
"B"
,
"C"
,
"D"
,
"E"
,
"F"
,
"G"
,
"H"
,
"I"
,
"J"
,
"K"
,
"L"
,
"M"
,
"N"
,
"O"
,
"P"
,
"Q"
,
"R"
,
"S"
,
"T"
,
"U"
,
"V"
,
"W"
,
"X"
,
"Y"
,
"Z"
,
"["
,
"]"
,
"_"
,
"a"
,
"b"
,
"c"
,
"d"
,
"e"
,
"f"
,
"g"
,
"h"
,
"i"
,
"j"
,
"k"
,
"l"
,
"m"
,
"n"
,
"o"
,
"p"
,
"q"
,
"r"
,
"s"
,
"t"
,
"u"
,
"v"
,
"w"
,
"x"
,
"y"
,
"z"
,
"{"
,
"|"
,
"}"
]
return
validcharlist
[
pos
]
def
encodePassword
(
self
,
p
):
"""
Password encoder
Keyword arguments:
@return sPass -- Encoded password
"""
sPass
=
":"
sTmp
=
""
if
not
p
:
return
""
for
i
in
range
(
len
(
p
)):
c
=
p
[
i
:
i
+
1
]
a
=
ord
(
c
)
sTmp
=
str
(
a
+
i
+
1
)
+
":"
sPass
+=
sTmp
sTmp
=
""
return
sPass
def
findCharInList
(
self
,
c
):
"""
Character position finder
Keyword arguments:
@param c -- Character that needs to be matched if valid
@return i -- Place where the character is in the valid list
"""
i
=
-
1
for
j
in
range
(
self
.
numValidCharList
):
randchar
=
self
.
getvalidCharList
(
j
)
if
randchar
==
c
:
i
=
j
return
i
return
i
def
getRandomValidCharFromList
(
self
):
"""
Random valid character getter
Keyword arguments:
@return char -- Valid character placed 0-60 in the valid list
"""
return
self
.
getvalidCharList
(
random
.
randint
(
0
,
60
))
def
scrambleString
(
self
,
s
):
"""
Password scrambler
Keyword arguments:
@param s -- Password that needs to be scrambled
@return sRet -- NoMachine NX scrambled password
"""
sRet
=
""
if
not
s
:
return
s
strp
=
self
.
encodePassword
(
s
)
if
len
(
strp
)
<
32
:
sRet
+=
self
.
dummyString
for
iR
in
reversed
(
range
(
len
(
strp
))):
sRet
+=
strp
[
iR
:
iR
+
1
]
if
len
(
sRet
)
<
32
:
sRet
+=
self
.
dummyString
app
=
self
.
getRandomValidCharFromList
()
k
=
ord
(
app
)
l
=
k
+
len
(
sRet
)
-
2
sRet
=
app
+
sRet
for
i1
in
range
(
1
,
len
(
sRet
)):
app2
=
sRet
[
i1
:
i1
+
1
]
j
=
self
.
findCharInList
(
app2
)
if
j
==
-
1
:
return
sRet
i
=
(
j
+
l
*
(
i1
+
1
))
%
self
.
numValidCharList
car
=
self
.
getvalidCharList
(
i
)
sRet
=
self
.
substr_replace
(
sRet
,
car
,
i1
,
1
)
c
=
(
ord
(
self
.
getRandomValidCharFromList
()))
+
2
c2
=
chr
(
c
)
sRet
=
sRet
+
c2
return
escape
(
sRet
)
def
substr_replace
(
self
,
in_str
,
ch
,
pos
,
qt
):
"""
Replace a character at a special position
"""
clist
=
list
(
in_str
)
count
=
0
tmp_str
=
''
for
key
in
clist
:
if
count
!=
pos
:
tmp_str
+=
key
else
:
tmp_str
+=
ch
count
=
count
+
1
return
tmp_str
if
__name__
==
"__main__"
:
NXPass
=
NXKeyGen
(
sys
.
argv
[
1
])
print
NXPass
.
password
print
NXPass
.
getEncrypted
()
src/nsi/installer/win_install.py
View file @
8ac7e289
...
@@ -130,11 +130,6 @@ def main():
...
@@ -130,11 +130,6 @@ def main():
custom_protocol_register
(
custom_protocol
)
custom_protocol_register
(
custom_protocol
)
except
:
except
:
print
"Error! URL Protocol handler installation aborted!"
print
"Error! URL Protocol handler installation aborted!"
if
args
.
nx
:
print
"Running NX Client install"
subprocess
.
call
(
'python "
%
s
\\
nx_client_installer.py"'
%
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
)))
print
"Creating icon in the installation folder"
print
"Creating icon in the installation folder"
location
=
os
.
path
.
join
(
desktop_path
,
"CIRCLE Client.url"
)
location
=
os
.
path
.
join
(
desktop_path
,
"CIRCLE Client.url"
)
shortcut
=
open
(
location
,
"w"
)
shortcut
=
open
(
location
,
"w"
)
...
...
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