Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
B
binwalk
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
fact-gitdep
binwalk
Commits
e407d275
Commit
e407d275
authored
Nov 26, 2013
by
devttys0
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added bash auto-completion
parent
b11b6654
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
254 additions
and
150 deletions
+254
-150
binwalk
src/bin/binwalk
+7
-150
binwalk.completion
src/binwalk.completion
+52
-0
cmdopts.py
src/binwalk/cmdopts.py
+151
-0
setup.py
src/setup.py
+44
-0
No files found.
src/bin/binwalk
View file @
e407d275
...
...
@@ -2,6 +2,7 @@
import
sys
import
os.path
import
binwalk
import
binwalk.cmdopts
from
binwalk.compat
import
*
from
threading
import
Thread
from
getopt
import
GetoptError
,
gnu_getopt
as
GetOpt
...
...
@@ -57,92 +58,6 @@ See http://binwalk.org/wiki/usage for more.
"""
%
(
name
,
name
,
name
,
name
,
name
,
name
,
name
,
name
))
sys
.
exit
(
0
)
def
usage
(
fd
):
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Binwalk v
%
s
\n
"
%
binwalk
.
Config
.
VERSION
)
fd
.
write
(
"Craig Heffner, http://www.devttys0.com
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Usage:
%
s [OPTIONS] [FILE1] [FILE2] [FILE3] ...
\n
"
%
os
.
path
.
basename
(
sys
.
argv
[
0
]))
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Signature Analysis:
\n
"
)
fd
.
write
(
"
\t
-B, --binwalk Perform a file signature scan (default)
\n
"
)
fd
.
write
(
"
\t
-R, --raw-bytes=<string> Search for a custom signature
\n
"
)
fd
.
write
(
"
\t
-A, --opcodes Scan for executable code signatures
\n
"
)
fd
.
write
(
"
\t
-C, --cast Cast file contents as various data types
\n
"
)
fd
.
write
(
"
\t
-m, --magic=<file> Specify an alternate magic file to use
\n
"
)
fd
.
write
(
"
\t
-x, --exclude=<filter> Exclude matches that have <filter> in their description
\n
"
)
fd
.
write
(
"
\t
-y, --include=<filter> Only search for matches that have <filter> in their description
\n
"
)
fd
.
write
(
"
\t
-I, --show-invalid Show results marked as invalid
\n
"
)
fd
.
write
(
"
\t
-T, --ignore-time-skew Do not show results that have timestamps more than 1 year in the future
\n
"
)
fd
.
write
(
"
\t
-k, --keep-going Show all matching results at a given offset, not just the first one
\n
"
)
fd
.
write
(
"
\t
-b, --dumb Disable smart signature keywords
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Strings Analysis:
\n
"
)
fd
.
write
(
"
\t
-S, --strings Scan for ASCII strings (may be combined with -B, -R, -A, or -E)
\n
"
)
fd
.
write
(
"
\t
-s, --strlen=<n> Set the minimum string length to search for (default: 3)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Entropy Analysis:
\n
"
)
fd
.
write
(
"
\t
-E, --entropy Plot file entropy (may be combined with -B, -R, -A, or -S)
\n
"
)
fd
.
write
(
"
\t
-H, --heuristic Identify unknown compression/encryption based on entropy heuristics (implies -E)
\n
"
)
fd
.
write
(
"
\t
-K, --block=<int> Set the block size for entropy analysis (default:
%
d)
\n
"
%
binwalk
.
entropy
.
FileEntropy
.
DEFAULT_BLOCK_SIZE
)
fd
.
write
(
"
\t
-a, --gzip Use gzip compression ratios to measure entropy
\n
"
)
fd
.
write
(
"
\t
-N, --no-plot Do not generate an entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-F, --marker=<offset:name> Add a marker to the entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-Q, --no-legend Omit the legend from the entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-J, --save-plot Save plot as an SVG (implied if multiple files are specified)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Binary Diffing:
\n
"
)
fd
.
write
(
"
\t
-W, --diff Hexdump / diff the specified files
\n
"
)
fd
.
write
(
"
\t
-K, --block=<int> Number of bytes to display per line (default:
%
d)
\n
"
%
binwalk
.
hexdiff
.
HexDiff
.
DEFAULT_BLOCK_SIZE
)
fd
.
write
(
"
\t
-G, --green Only show hex dump lines that contain bytes which were the same in all files
\n
"
)
fd
.
write
(
"
\t
-i, --red Only show hex dump lines that contain bytes which were different in all files
\n
"
)
fd
.
write
(
"
\t
-U, --blue Only show hex dump lines that contain bytes which were different in some files
\n
"
)
fd
.
write
(
"
\t
-w, --terse Diff all files, but only display a hex dump of the first file
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Extraction Options:
\n
"
)
fd
.
write
(
"
\t
-D, --dd=<type:ext[:cmd]> Extract <type> signatures, give the files an extension of <ext>, and execute <cmd>
\n
"
)
fd
.
write
(
"
\t
-e, --extract=[file] Automatically extract known file types; load rules from file, if specified
\n
"
)
fd
.
write
(
"
\t
-M, --matryoshka=[n] Recursively scan extracted files, up to n levels deep (8 levels of recursion is the default)
\n
"
)
fd
.
write
(
"
\t
-j, --max-size=<int> Limit extracted file sizes (default: no limit)
\n
"
)
fd
.
write
(
"
\t
-r, --rm Cleanup extracted files and zero-size files
\n
"
)
fd
.
write
(
"
\t
-d, --honor-footers Only extract files up to their corresponding footer signatures
\n
"
)
fd
.
write
(
"
\t
-z, --carve Carve data from files, but don't execute extraction utilities (implies -d)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Plugin Options:
\n
"
)
fd
.
write
(
"
\t
-X, --disable-plugin=<name> Disable a plugin by name
\n
"
)
fd
.
write
(
"
\t
-Y, --enable-plugin=<name> Enable a plugin by name
\n
"
)
fd
.
write
(
"
\t
-p, --disable-plugins Do not load any binwalk plugins
\n
"
)
fd
.
write
(
"
\t
-L, --list-plugins List all user and system plugins by name
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"General Options:
\n
"
)
fd
.
write
(
"
\t
-o, --offset=<int> Start scan at this file offset
\n
"
)
fd
.
write
(
"
\t
-l, --length=<int> Number of bytes to scan
\n
"
)
fd
.
write
(
"
\t
-g, --grep=<text> Grep results for the specified text
\n
"
)
fd
.
write
(
"
\t
-f, --file=<file> Log results to file
\n
"
)
fd
.
write
(
"
\t
-c, --csv Log results to file in csv format
\n
"
)
fd
.
write
(
"
\t
-O, --skip-unopened Ignore file open errors and process only the files that can be opened
\n
"
)
fd
.
write
(
"
\t
-t, --term Format output to fit the terminal window
\n
"
)
fd
.
write
(
"
\t
-q, --quiet Supress output to stdout
\n
"
)
fd
.
write
(
"
\t
-v, --verbose Be verbose (specify twice for very verbose)
\n
"
)
fd
.
write
(
"
\t
-u, --update Update magic signature files
\n
"
)
fd
.
write
(
"
\t
-?, --examples Show example usage
\n
"
)
fd
.
write
(
"
\t
-h, --help Show help output
\n
"
)
fd
.
write
(
"
\n
"
)
if
fd
==
sys
.
stderr
:
sys
.
exit
(
1
)
else
:
sys
.
exit
(
0
)
def
main
():
# The Binwalk class instance must be global so that the display_status thread can access it.
global
bwalk
...
...
@@ -198,77 +113,19 @@ def main():
config
=
binwalk
.
Config
()
short_options
=
"AaBbCcdEeGHhIiJkLMNnOPpQqrSTtUuvWwz?D:F:f:g:j:K:o:l:m:R:s:X:x:Y:y:"
long_options
=
[
"rm"
,
"help"
,
"green"
,
"red"
,
"blue"
,
"examples"
,
"quiet"
,
"csv"
,
"verbose"
,
"opcodes"
,
"cast"
,
"update"
,
"binwalk"
,
"keep-going"
,
"show-invalid"
,
"ignore-time-skew"
,
"honor-footers"
,
"profile"
,
"delay"
,
# delay is depreciated, but kept for backwards compatability
"skip-unopened"
,
"term"
,
"tim"
,
"terse"
,
"diff"
,
"dumb"
,
"entropy"
,
"heuristic"
,
"math"
,
"gzip"
,
"save-plot"
,
"no-plot"
,
"no-legend"
,
"strings"
,
"carve"
,
"matryoshka="
,
"list-plugins"
,
"disable-plugins"
,
"disable-plugin="
,
"enable-plugin="
,
"max-size="
,
"marker="
,
"strlen="
,
"file="
,
"block="
,
"offset="
,
"length="
,
"exclude="
,
"include="
,
"search="
,
"extract="
,
"dd="
,
"grep="
,
"magic="
,
"raw-bytes="
,
]
# Require at least one argument (the target file)
if
len
(
sys
.
argv
)
<
MIN_ARGC
:
usage
(
sys
.
stderr
)
binwalk
.
cmdopts
.
usage
(
sys
.
stderr
)
try
:
opts
,
args
=
GetOpt
(
sys
.
argv
[
1
:],
short_options
,
long_options
)
opts
,
args
=
GetOpt
(
sys
.
argv
[
1
:],
binwalk
.
cmdopts
.
short_options
,
binwalk
.
cmdopts
.
long_options
)
except
GetoptError
as
e
:
sys
.
stderr
.
write
(
"
%
s
\n
"
%
str
(
e
))
usage
(
sys
.
stderr
)
binwalk
.
cmdopts
.
usage
(
sys
.
stderr
)
for
opt
,
arg
in
opts
:
if
opt
in
(
"-h"
,
"--help"
):
usage
(
sys
.
stdout
)
binwalk
.
cmdopts
.
usage
(
sys
.
stdout
)
elif
opt
in
(
"-?"
,
"--examples"
):
examples
()
elif
opt
in
(
"-d"
,
"--delay"
,
"--honor-footers"
):
...
...
@@ -418,7 +275,7 @@ def main():
# The --profile option is handled prior to calling main()
elif
opt
not
in
(
'-P'
,
'--profile'
):
usage
(
sys
.
stderr
)
binwalk
.
cmdopts
.
usage
(
sys
.
stderr
)
# Keep track of the options and arguments.
# This is used later to determine which argv entries are file names.
...
...
@@ -461,7 +318,7 @@ def main():
if
not
verbose
:
verbose
=
1
elif
len
(
target_files
)
==
0
:
usage
(
sys
.
stderr
)
binwalk
.
cmdopts
.
usage
(
sys
.
stderr
)
# Instantiate the Binwalk class
bwalk
=
binwalk
.
Binwalk
(
magic_files
=
magic_files
,
...
...
src/binwalk.completion
0 → 100644
View file @
e407d275
# Bash auto-completion file for binwalk
_binwalk()
{
local cur prev opts long_opts short_opts file_opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
# Long and short options are dynamically generated by setup.py
long_opts="%%LONG_OPTS%%"
short_opts="%%SHORT_OPTS%%"
file_opts="--file --extract --magic"
opts="${long_opts} ${short_opts}"
# If this is a long option, then tab complete a list of matching long options
if [[ ${cur} == --* ]]
then
COMPREPLY=( $(compgen -W "${long_opts}" -- ${cur}) )
# Else, if this is any other option, tab complete it from then entire list of valid options
elif [[ ${cur} == -* ]]
then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
# Finally, if this is not an option at all, tab complete a list of files
else
COMPREPLY=( $(compgen -f ${cur}) )
fi
# If there is only one matching option, and if it is a long option, then don't put a space after the equals sign.
if [ "$(echo "$COMPREPLY" | wc -w)" == "1" ] && [ "$(echo "$COMPREPLY" | grep -e '=$')" != "" ]
then
compopt -o nospace;
# Else, if there are no matching options, check to see if the previous option is a long option that takes a file path argument
elif [ "$COMPREPLY" == "" ]
then
for file_opt in ${file_opts}
do
if [ "${prev}" == "${file_opt}" ]
then
# If the previous option does take a file path argument, tab complete a list of files
COMPREPLY=( $(compgen -f "") )
break
fi
done
fi
return 0
}
complete -F _binwalk binwalk
src/binwalk/cmdopts.py
0 → 100644
View file @
e407d275
# Contains all the command line options and usage output for the binwlak script.
# Placed here so that other scripts can programmatically access the command line options list (e.g., for auto-completion generation).
import
os
import
sys
import
binwalk.config
short_options
=
"AaBbCcdEeGHhIiJkLMNnOPpQqrSTtUuvWwz?D:F:f:g:j:K:o:l:m:R:s:X:x:Y:y:"
long_options
=
[
"rm"
,
"help"
,
"green"
,
"red"
,
"blue"
,
"examples"
,
"quiet"
,
"csv"
,
"verbose"
,
"opcodes"
,
"cast"
,
"update"
,
"binwalk"
,
"keep-going"
,
"show-invalid"
,
"ignore-time-skew"
,
"honor-footers"
,
"profile"
,
"delay"
,
# delay is depreciated, but kept for backwards compatability
"skip-unopened"
,
"term"
,
"tim"
,
"terse"
,
"diff"
,
"dumb"
,
"entropy"
,
"heuristic"
,
"math"
,
"gzip"
,
"save-plot"
,
"no-plot"
,
"no-legend"
,
"strings"
,
"carve"
,
"matryoshka="
,
"list-plugins"
,
"disable-plugins"
,
"disable-plugin="
,
"enable-plugin="
,
"max-size="
,
"marker="
,
"strlen="
,
"file="
,
"block="
,
"offset="
,
"length="
,
"exclude="
,
"include="
,
"search="
,
"extract="
,
"dd="
,
"grep="
,
"magic="
,
"raw-bytes="
,
]
def
usage
(
fd
):
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Binwalk v
%
s
\n
"
%
binwalk
.
config
.
Config
.
VERSION
)
fd
.
write
(
"Craig Heffner, http://www.devttys0.com
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Usage:
%
s [OPTIONS] [FILE1] [FILE2] [FILE3] ...
\n
"
%
os
.
path
.
basename
(
sys
.
argv
[
0
]))
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Signature Analysis:
\n
"
)
fd
.
write
(
"
\t
-B, --binwalk Perform a file signature scan (default)
\n
"
)
fd
.
write
(
"
\t
-R, --raw-bytes=<string> Search for a custom signature
\n
"
)
fd
.
write
(
"
\t
-A, --opcodes Scan for executable code signatures
\n
"
)
fd
.
write
(
"
\t
-C, --cast Cast file contents as various data types
\n
"
)
fd
.
write
(
"
\t
-m, --magic=<file> Specify an alternate magic file to use
\n
"
)
fd
.
write
(
"
\t
-x, --exclude=<filter> Exclude matches that have <filter> in their description
\n
"
)
fd
.
write
(
"
\t
-y, --include=<filter> Only search for matches that have <filter> in their description
\n
"
)
fd
.
write
(
"
\t
-I, --show-invalid Show results marked as invalid
\n
"
)
fd
.
write
(
"
\t
-T, --ignore-time-skew Do not show results that have timestamps more than 1 year in the future
\n
"
)
fd
.
write
(
"
\t
-k, --keep-going Show all matching results at a given offset, not just the first one
\n
"
)
fd
.
write
(
"
\t
-b, --dumb Disable smart signature keywords
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Strings Analysis:
\n
"
)
fd
.
write
(
"
\t
-S, --strings Scan for ASCII strings (may be combined with -B, -R, -A, or -E)
\n
"
)
fd
.
write
(
"
\t
-s, --strlen=<n> Set the minimum string length to search for (default: 3)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Entropy Analysis:
\n
"
)
fd
.
write
(
"
\t
-E, --entropy Plot file entropy (may be combined with -B, -R, -A, or -S)
\n
"
)
fd
.
write
(
"
\t
-H, --heuristic Identify unknown compression/encryption based on entropy heuristics (implies -E)
\n
"
)
fd
.
write
(
"
\t
-K, --block=<int> Set the block size for entropy analysis (default:
%
d)
\n
"
%
binwalk
.
entropy
.
FileEntropy
.
DEFAULT_BLOCK_SIZE
)
fd
.
write
(
"
\t
-a, --gzip Use gzip compression ratios to measure entropy
\n
"
)
fd
.
write
(
"
\t
-N, --no-plot Do not generate an entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-F, --marker=<offset:name> Add a marker to the entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-Q, --no-legend Omit the legend from the entropy plot graph
\n
"
)
fd
.
write
(
"
\t
-J, --save-plot Save plot as an SVG (implied if multiple files are specified)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Binary Diffing:
\n
"
)
fd
.
write
(
"
\t
-W, --diff Hexdump / diff the specified files
\n
"
)
fd
.
write
(
"
\t
-K, --block=<int> Number of bytes to display per line (default:
%
d)
\n
"
%
binwalk
.
hexdiff
.
HexDiff
.
DEFAULT_BLOCK_SIZE
)
fd
.
write
(
"
\t
-G, --green Only show hex dump lines that contain bytes which were the same in all files
\n
"
)
fd
.
write
(
"
\t
-i, --red Only show hex dump lines that contain bytes which were different in all files
\n
"
)
fd
.
write
(
"
\t
-U, --blue Only show hex dump lines that contain bytes which were different in some files
\n
"
)
fd
.
write
(
"
\t
-w, --terse Diff all files, but only display a hex dump of the first file
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Extraction Options:
\n
"
)
fd
.
write
(
"
\t
-D, --dd=<type:ext[:cmd]> Extract <type> signatures, give the files an extension of <ext>, and execute <cmd>
\n
"
)
fd
.
write
(
"
\t
-e, --extract=[file] Automatically extract known file types; load rules from file, if specified
\n
"
)
fd
.
write
(
"
\t
-M, --matryoshka=[n] Recursively scan extracted files, up to n levels deep (8 levels of recursion is the default)
\n
"
)
fd
.
write
(
"
\t
-j, --max-size=<int> Limit extracted file sizes (default: no limit)
\n
"
)
fd
.
write
(
"
\t
-r, --rm Cleanup extracted files and zero-size files
\n
"
)
fd
.
write
(
"
\t
-d, --honor-footers Only extract files up to their corresponding footer signatures
\n
"
)
fd
.
write
(
"
\t
-z, --carve Carve data from files, but don't execute extraction utilities (implies -d)
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"Plugin Options:
\n
"
)
fd
.
write
(
"
\t
-X, --disable-plugin=<name> Disable a plugin by name
\n
"
)
fd
.
write
(
"
\t
-Y, --enable-plugin=<name> Enable a plugin by name
\n
"
)
fd
.
write
(
"
\t
-p, --disable-plugins Do not load any binwalk plugins
\n
"
)
fd
.
write
(
"
\t
-L, --list-plugins List all user and system plugins by name
\n
"
)
fd
.
write
(
"
\n
"
)
fd
.
write
(
"General Options:
\n
"
)
fd
.
write
(
"
\t
-o, --offset=<int> Start scan at this file offset
\n
"
)
fd
.
write
(
"
\t
-l, --length=<int> Number of bytes to scan
\n
"
)
fd
.
write
(
"
\t
-g, --grep=<text> Grep results for the specified text
\n
"
)
fd
.
write
(
"
\t
-f, --file=<file> Log results to file
\n
"
)
fd
.
write
(
"
\t
-c, --csv Log results to file in csv format
\n
"
)
fd
.
write
(
"
\t
-O, --skip-unopened Ignore file open errors and process only the files that can be opened
\n
"
)
fd
.
write
(
"
\t
-t, --term Format output to fit the terminal window
\n
"
)
fd
.
write
(
"
\t
-q, --quiet Supress output to stdout
\n
"
)
fd
.
write
(
"
\t
-v, --verbose Be verbose (specify twice for very verbose)
\n
"
)
fd
.
write
(
"
\t
-u, --update Update magic signature files
\n
"
)
fd
.
write
(
"
\t
-?, --examples Show example usage
\n
"
)
fd
.
write
(
"
\t
-h, --help Show help output
\n
"
)
fd
.
write
(
"
\n
"
)
if
fd
==
sys
.
stderr
:
sys
.
exit
(
1
)
else
:
sys
.
exit
(
0
)
src/setup.py
View file @
e407d275
...
...
@@ -133,3 +133,47 @@ setup( name = "binwalk",
scripts
=
[
"bin/binwalk"
],
)
# If the bash auto-completion directory exists, generate an auto-completion file
bash_completion_dir
=
os
.
path
.
join
(
'/'
,
'etc'
,
'bash_completion.d'
)
if
os
.
path
.
exists
(
bash_completion_dir
):
import
binwalk.cmdopts
print
(
"Installing bash auto-completion file"
)
long_opts_key
=
'
%%
LONG_OPTS
%%
'
short_opts_key
=
'
%%
SHORT_OPTS
%%
'
file_opts_key
=
'
%%
FILE_OPTS
%%
'
file_name_in
=
'binwalk.completion'
file_name_out
=
os
.
path
.
join
(
bash_completion_dir
,
'binwalk'
)
long_opts
=
[]
short_opts
=
[]
file_opts
=
[
'--file='
,
'--extract='
,
'--magic='
]
for
opt
in
binwalk
.
cmdopts
.
short_options
:
if
opt
!=
':'
:
short_opts
.
append
(
'-'
+
opt
)
for
opt
in
binwalk
.
cmdopts
.
long_options
:
long_opts
.
append
(
'--'
+
opt
)
try
:
with
open
(
file_name_in
)
as
fd_in
:
with
open
(
file_name_out
,
'w'
)
as
fd_out
:
for
line
in
fd_in
.
readlines
():
if
long_opts_key
in
line
:
line
=
line
.
replace
(
long_opts_key
,
' '
.
join
(
long_opts
))
elif
short_opts_key
in
line
:
line
=
line
.
replace
(
short_opts_key
,
' '
.
join
(
short_opts
))
elif
file_opts_key
in
line
:
line
=
line
.
replace
(
file_opts_key
,
' '
.
join
(
file_opts
))
fd_out
.
write
(
line
)
except
Exception
as
e
:
print
(
"WARNING: Failed to install auto-completion file:
%
s"
%
e
)
try
:
os
.
unlink
(
file_name_out
)
except
:
pass
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