Skip to content
Projeler
Gruplar
Parçacıklar
Yardım
Yükleniyor...
Oturum aç / Kaydol
Gezinmeyi değiştir
C
cpython
Proje
Proje
Ayrıntılar
Etkinlik
Cycle Analytics
Depo (repository)
Depo (repository)
Dosyalar
Kayıtlar (commit)
Dallar (branch)
Etiketler
Katkıda bulunanlar
Grafik
Karşılaştır
Grafikler
Konular (issue)
0
Konular (issue)
0
Liste
Pano
Etiketler
Kilometre Taşları
Birleştirme (merge) Talepleri
0
Birleştirme (merge) Talepleri
0
CI / CD
CI / CD
İş akışları (pipeline)
İşler
Zamanlamalar
Grafikler
Paketler
Paketler
Wiki
Wiki
Parçacıklar
Parçacıklar
Üyeler
Üyeler
Collapse sidebar
Close sidebar
Etkinlik
Grafik
Grafikler
Yeni bir konu (issue) oluştur
İşler
Kayıtlar (commit)
Konu (issue) Panoları
Kenar çubuğunu aç
Batuhan Osman TASKAYA
cpython
Commits
5cd5f12a
Kaydet (Commit)
5cd5f12a
authored
Eyl 23, 2008
tarafından
Jesus Cea
Dosyalara gözat
Seçenekler
Dosyalara Gözat
İndir
Eposta Yamaları
Sade Fark
Bugfix for issue3885 and 'DB.verify()' crash.
Reviewed by Nick Coghlan.
üst
09979a13
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
123 additions
and
44 deletions
+123
-44
test_basics.py
Lib/bsddb/test/test_basics.py
+36
-16
_bsddb.c
Modules/_bsddb.c
+86
-27
bsddb.h
Modules/bsddb.h
+1
-1
No files found.
Lib/bsddb/test/test_basics.py
Dosyayı görüntüle @
5cd5f12a
...
@@ -573,6 +573,15 @@ class BasicTestCase(unittest.TestCase):
...
@@ -573,6 +573,15 @@ class BasicTestCase(unittest.TestCase):
#----------------------------------------
#----------------------------------------
def
test07_verify
(
self
):
# Verify bug solved in 4.7.3pre8
self
.
d
.
close
()
d
=
db
.
DB
(
self
.
env
)
d
.
verify
(
self
.
filename
)
#----------------------------------------
#----------------------------------------------------------------------
#----------------------------------------------------------------------
...
@@ -602,13 +611,13 @@ class BasicWithEnvTestCase(BasicTestCase):
...
@@ -602,13 +611,13 @@ class BasicWithEnvTestCase(BasicTestCase):
#----------------------------------------
#----------------------------------------
def
test0
7
_EnvRemoveAndRename
(
self
):
def
test0
8
_EnvRemoveAndRename
(
self
):
if
not
self
.
env
:
if
not
self
.
env
:
return
return
if
verbose
:
if
verbose
:
print
'
\n
'
,
'-='
*
30
print
'
\n
'
,
'-='
*
30
print
"Running
%
s.test0
7
_EnvRemoveAndRename..."
%
self
.
__class__
.
__name__
print
"Running
%
s.test0
8
_EnvRemoveAndRename..."
%
self
.
__class__
.
__name__
# can't rename or remove an open DB
# can't rename or remove an open DB
self
.
d
.
close
()
self
.
d
.
close
()
...
@@ -619,7 +628,7 @@ class BasicWithEnvTestCase(BasicTestCase):
...
@@ -619,7 +628,7 @@ class BasicWithEnvTestCase(BasicTestCase):
# dbremove and dbrename are in 4.1 and later
# dbremove and dbrename are in 4.1 and later
if
db
.
version
()
<
(
4
,
1
):
if
db
.
version
()
<
(
4
,
1
):
del
test0
7
_EnvRemoveAndRename
del
test0
8
_EnvRemoveAndRename
#----------------------------------------
#----------------------------------------
...
@@ -720,11 +729,11 @@ class BasicTransactionTestCase(BasicTestCase):
...
@@ -720,11 +729,11 @@ class BasicTransactionTestCase(BasicTestCase):
#----------------------------------------
#----------------------------------------
def
test0
7
_TxnTruncate
(
self
):
def
test0
8
_TxnTruncate
(
self
):
d
=
self
.
d
d
=
self
.
d
if
verbose
:
if
verbose
:
print
'
\n
'
,
'-='
*
30
print
'
\n
'
,
'-='
*
30
print
"Running
%
s.test0
7
_TxnTruncate..."
%
self
.
__class__
.
__name__
print
"Running
%
s.test0
8
_TxnTruncate..."
%
self
.
__class__
.
__name__
d
.
put
(
"abcde"
,
"ABCDE"
);
d
.
put
(
"abcde"
,
"ABCDE"
);
txn
=
self
.
env
.
txn_begin
()
txn
=
self
.
env
.
txn_begin
()
...
@@ -737,7 +746,7 @@ class BasicTransactionTestCase(BasicTestCase):
...
@@ -737,7 +746,7 @@ class BasicTransactionTestCase(BasicTestCase):
#----------------------------------------
#----------------------------------------
def
test0
8
_TxnLateUse
(
self
):
def
test0
9
_TxnLateUse
(
self
):
txn
=
self
.
env
.
txn_begin
()
txn
=
self
.
env
.
txn_begin
()
txn
.
abort
()
txn
.
abort
()
try
:
try
:
...
@@ -771,11 +780,11 @@ class BTreeRecnoTestCase(BasicTestCase):
...
@@ -771,11 +780,11 @@ class BTreeRecnoTestCase(BasicTestCase):
dbtype
=
db
.
DB_BTREE
dbtype
=
db
.
DB_BTREE
dbsetflags
=
db
.
DB_RECNUM
dbsetflags
=
db
.
DB_RECNUM
def
test0
7
_RecnoInBTree
(
self
):
def
test0
8
_RecnoInBTree
(
self
):
d
=
self
.
d
d
=
self
.
d
if
verbose
:
if
verbose
:
print
'
\n
'
,
'-='
*
30
print
'
\n
'
,
'-='
*
30
print
"Running
%
s.test0
7
_RecnoInBTree..."
%
self
.
__class__
.
__name__
print
"Running
%
s.test0
8
_RecnoInBTree..."
%
self
.
__class__
.
__name__
rec
=
d
.
get
(
200
)
rec
=
d
.
get
(
200
)
self
.
assertEqual
(
type
(
rec
),
type
(()))
self
.
assertEqual
(
type
(
rec
),
type
(()))
...
@@ -805,11 +814,11 @@ class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase):
...
@@ -805,11 +814,11 @@ class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase):
class
BasicDUPTestCase
(
BasicTestCase
):
class
BasicDUPTestCase
(
BasicTestCase
):
dbsetflags
=
db
.
DB_DUP
dbsetflags
=
db
.
DB_DUP
def
test0
8
_DuplicateKeys
(
self
):
def
test0
9
_DuplicateKeys
(
self
):
d
=
self
.
d
d
=
self
.
d
if
verbose
:
if
verbose
:
print
'
\n
'
,
'-='
*
30
print
'
\n
'
,
'-='
*
30
print
"Running
%
s.test0
8
_DuplicateKeys..."
%
\
print
"Running
%
s.test0
9
_DuplicateKeys..."
%
\
self
.
__class__
.
__name__
self
.
__class__
.
__name__
d
.
put
(
"dup0"
,
"before"
)
d
.
put
(
"dup0"
,
"before"
)
...
@@ -878,11 +887,11 @@ class BasicMultiDBTestCase(BasicTestCase):
...
@@ -878,11 +887,11 @@ class BasicMultiDBTestCase(BasicTestCase):
else
:
else
:
return
db
.
DB_BTREE
return
db
.
DB_BTREE
def
test
09
_MultiDB
(
self
):
def
test
10
_MultiDB
(
self
):
d1
=
self
.
d
d1
=
self
.
d
if
verbose
:
if
verbose
:
print
'
\n
'
,
'-='
*
30
print
'
\n
'
,
'-='
*
30
print
"Running
%
s.test
09
_MultiDB..."
%
self
.
__class__
.
__name__
print
"Running
%
s.test
10
_MultiDB..."
%
self
.
__class__
.
__name__
d2
=
db
.
DB
(
self
.
env
)
d2
=
db
.
DB
(
self
.
env
)
d2
.
open
(
self
.
filename
,
"second"
,
self
.
dbtype
,
d2
.
open
(
self
.
filename
,
"second"
,
self
.
dbtype
,
...
@@ -1014,9 +1023,20 @@ class DBPrivateObject(PrivateObject) :
...
@@ -1014,9 +1023,20 @@ class DBPrivateObject(PrivateObject) :
self
.
obj
=
db
.
DB
()
self
.
obj
=
db
.
DB
()
class
CrashAndBurn
(
unittest
.
TestCase
)
:
class
CrashAndBurn
(
unittest
.
TestCase
)
:
def
test01_OpenCrash
(
self
)
:
import
sys
# See http://bugs.python.org/issue3307
if
sys
.
version_info
[:
3
]
<
(
2
,
4
,
0
):
self
.
assertRaises
(
db
.
DBInvalidArgError
,
db
.
DB
,
None
,
65535
)
def
assertTrue
(
self
,
expr
,
msg
=
None
):
self
.
failUnless
(
expr
,
msg
=
msg
)
#def test01_OpenCrash(self) :
# # See http://bugs.python.org/issue3307
# self.assertRaises(db.DBInvalidArgError, db.DB, None, 65535)
def
test02_DBEnv_dealloc
(
self
):
# http://bugs.python.org/issue3885
import
gc
self
.
assertRaises
(
db
.
DBInvalidArgError
,
db
.
DBEnv
,
~
db
.
DB_RPCCLIENT
)
gc
.
collect
()
#----------------------------------------------------------------------
#----------------------------------------------------------------------
...
@@ -1044,7 +1064,7 @@ def test_suite():
...
@@ -1044,7 +1064,7 @@ def test_suite():
suite
.
addTest
(
unittest
.
makeSuite
(
HashMultiDBTestCase
))
suite
.
addTest
(
unittest
.
makeSuite
(
HashMultiDBTestCase
))
suite
.
addTest
(
unittest
.
makeSuite
(
DBEnvPrivateObject
))
suite
.
addTest
(
unittest
.
makeSuite
(
DBEnvPrivateObject
))
suite
.
addTest
(
unittest
.
makeSuite
(
DBPrivateObject
))
suite
.
addTest
(
unittest
.
makeSuite
(
DBPrivateObject
))
#
suite.addTest(unittest.makeSuite(CrashAndBurn))
suite
.
addTest
(
unittest
.
makeSuite
(
CrashAndBurn
))
return
suite
return
suite
...
...
Modules/_bsddb.c
Dosyayı görüntüle @
5cd5f12a
...
@@ -989,7 +989,7 @@ newDBObject(DBEnvObject* arg, int flags)
...
@@ -989,7 +989,7 @@ newDBObject(DBEnvObject* arg, int flags)
/* Forward declaration */
/* Forward declaration */
static
PyObject
*
DB_close_internal
(
DBObject
*
self
,
int
flags
);
static
PyObject
*
DB_close_internal
(
DBObject
*
self
,
int
flags
,
int
do_not_close
);
static
void
static
void
DB_dealloc
(
DBObject
*
self
)
DB_dealloc
(
DBObject
*
self
)
...
@@ -997,8 +997,15 @@ DB_dealloc(DBObject* self)
...
@@ -997,8 +997,15 @@ DB_dealloc(DBObject* self)
PyObject
*
dummy
;
PyObject
*
dummy
;
if
(
self
->
db
!=
NULL
)
{
if
(
self
->
db
!=
NULL
)
{
dummy
=
DB_close_internal
(
self
,
0
);
dummy
=
DB_close_internal
(
self
,
0
,
0
);
Py_XDECREF
(
dummy
);
/*
** Raising exceptions while doing
** garbage collection is a fatal error.
*/
if
(
dummy
)
Py_DECREF
(
dummy
);
else
PyErr_Clear
();
}
}
if
(
self
->
in_weakreflist
!=
NULL
)
{
if
(
self
->
in_weakreflist
!=
NULL
)
{
PyObject_ClearWeakRefs
((
PyObject
*
)
self
);
PyObject_ClearWeakRefs
((
PyObject
*
)
self
);
...
@@ -1052,8 +1059,15 @@ DBCursor_dealloc(DBCursorObject* self)
...
@@ -1052,8 +1059,15 @@ DBCursor_dealloc(DBCursorObject* self)
PyObject
*
dummy
;
PyObject
*
dummy
;
if
(
self
->
dbc
!=
NULL
)
{
if
(
self
->
dbc
!=
NULL
)
{
dummy
=
DBC_close_internal
(
self
);
dummy
=
DBC_close_internal
(
self
);
Py_XDECREF
(
dummy
);
/*
** Raising exceptions while doing
** garbage collection is a fatal error.
*/
if
(
dummy
)
Py_DECREF
(
dummy
);
else
PyErr_Clear
();
}
}
if
(
self
->
in_weakreflist
!=
NULL
)
{
if
(
self
->
in_weakreflist
!=
NULL
)
{
PyObject_ClearWeakRefs
((
PyObject
*
)
self
);
PyObject_ClearWeakRefs
((
PyObject
*
)
self
);
...
@@ -1071,6 +1085,7 @@ newDBEnvObject(int flags)
...
@@ -1071,6 +1085,7 @@ newDBEnvObject(int flags)
if
(
self
==
NULL
)
if
(
self
==
NULL
)
return
NULL
;
return
NULL
;
self
->
db_env
=
NULL
;
self
->
closed
=
1
;
self
->
closed
=
1
;
self
->
flags
=
flags
;
self
->
flags
=
flags
;
self
->
moduleFlags
.
getReturnsNone
=
DEFAULT_GET_RETURNS_NONE
;
self
->
moduleFlags
.
getReturnsNone
=
DEFAULT_GET_RETURNS_NONE
;
...
@@ -1107,8 +1122,15 @@ DBEnv_dealloc(DBEnvObject* self)
...
@@ -1107,8 +1122,15 @@ DBEnv_dealloc(DBEnvObject* self)
PyObject
*
dummy
;
PyObject
*
dummy
;
if
(
self
->
db_env
)
{
if
(
self
->
db_env
)
{
dummy
=
DBEnv_close_internal
(
self
,
0
);
dummy
=
DBEnv_close_internal
(
self
,
0
);
Py_XDECREF
(
dummy
);
/*
** Raising exceptions while doing
** garbage collection is a fatal error.
*/
if
(
dummy
)
Py_DECREF
(
dummy
);
else
PyErr_Clear
();
}
}
Py_XDECREF
(
self
->
event_notifyCallback
);
Py_XDECREF
(
self
->
event_notifyCallback
);
...
@@ -1186,8 +1208,17 @@ DBTxn_dealloc(DBTxnObject* self)
...
@@ -1186,8 +1208,17 @@ DBTxn_dealloc(DBTxnObject* self)
if
(
self
->
txn
)
{
if
(
self
->
txn
)
{
int
flag_prepare
=
self
->
flag_prepare
;
int
flag_prepare
=
self
->
flag_prepare
;
dummy
=
DBTxn_abort_discard_internal
(
self
,
0
);
dummy
=
DBTxn_abort_discard_internal
(
self
,
0
);
Py_XDECREF
(
dummy
);
/*
** Raising exceptions while doing
** garbage collection is a fatal error.
*/
if
(
dummy
)
Py_DECREF
(
dummy
);
else
PyErr_Clear
();
if
(
!
flag_prepare
)
{
if
(
!
flag_prepare
)
{
PyErr_Warn
(
PyExc_RuntimeWarning
,
PyErr_Warn
(
PyExc_RuntimeWarning
,
"DBTxn aborted in destructor. No prior commit() or abort()."
);
"DBTxn aborted in destructor. No prior commit() or abort()."
);
...
@@ -1280,7 +1311,14 @@ DBSequence_dealloc(DBSequenceObject* self)
...
@@ -1280,7 +1311,14 @@ DBSequence_dealloc(DBSequenceObject* self)
if
(
self
->
sequence
!=
NULL
)
{
if
(
self
->
sequence
!=
NULL
)
{
dummy
=
DBSequence_close_internal
(
self
,
0
,
0
);
dummy
=
DBSequence_close_internal
(
self
,
0
,
0
);
Py_XDECREF
(
dummy
);
/*
** Raising exceptions while doing
** garbage collection is a fatal error.
*/
if
(
dummy
)
Py_DECREF
(
dummy
);
else
PyErr_Clear
();
}
}
if
(
self
->
in_weakreflist
!=
NULL
)
{
if
(
self
->
in_weakreflist
!=
NULL
)
{
...
@@ -1485,10 +1523,10 @@ DB_associate(DBObject* self, PyObject* args, PyObject* kwargs)
...
@@ -1485,10 +1523,10 @@ DB_associate(DBObject* self, PyObject* args, PyObject* kwargs)
static
PyObject
*
static
PyObject
*
DB_close_internal
(
DBObject
*
self
,
int
flags
)
DB_close_internal
(
DBObject
*
self
,
int
flags
,
int
do_not_close
)
{
{
PyObject
*
dummy
;
PyObject
*
dummy
;
int
err
;
int
err
=
0
;
if
(
self
->
db
!=
NULL
)
{
if
(
self
->
db
!=
NULL
)
{
/* Can be NULL if db is not in an environment */
/* Can be NULL if db is not in an environment */
...
@@ -1511,10 +1549,20 @@ DB_close_internal(DBObject* self, int flags)
...
@@ -1511,10 +1549,20 @@ DB_close_internal(DBObject* self, int flags)
}
}
#endif
#endif
MYDB_BEGIN_ALLOW_THREADS
;
/*
err
=
self
->
db
->
close
(
self
->
db
,
flags
);
** "do_not_close" is used to dispose all related objects in the
MYDB_END_ALLOW_THREADS
;
** tree, without actually releasing the "root" object.
self
->
db
=
NULL
;
** This is done, for example, because function calls like
** "DB.verify()" implicitly close the underlying handle. So
** the handle doesn't need to be closed, but related objects
** must be cleaned up.
*/
if
(
!
do_not_close
)
{
MYDB_BEGIN_ALLOW_THREADS
;
err
=
self
->
db
->
close
(
self
->
db
,
flags
);
MYDB_END_ALLOW_THREADS
;
self
->
db
=
NULL
;
}
RETURN_IF_ERR
();
RETURN_IF_ERR
();
}
}
RETURN_NONE
();
RETURN_NONE
();
...
@@ -1526,7 +1574,7 @@ DB_close(DBObject* self, PyObject* args)
...
@@ -1526,7 +1574,7 @@ DB_close(DBObject* self, PyObject* args)
int
flags
=
0
;
int
flags
=
0
;
if
(
!
PyArg_ParseTuple
(
args
,
"|i:close"
,
&
flags
))
if
(
!
PyArg_ParseTuple
(
args
,
"|i:close"
,
&
flags
))
return
NULL
;
return
NULL
;
return
DB_close_internal
(
self
,
flags
);
return
DB_close_internal
(
self
,
flags
,
0
);
}
}
...
@@ -2146,7 +2194,7 @@ DB_open(DBObject* self, PyObject* args, PyObject* kwargs)
...
@@ -2146,7 +2194,7 @@ DB_open(DBObject* self, PyObject* args, PyObject* kwargs)
if
(
makeDBError
(
err
))
{
if
(
makeDBError
(
err
))
{
PyObject
*
dummy
;
PyObject
*
dummy
;
dummy
=
DB_close_internal
(
self
,
0
);
dummy
=
DB_close_internal
(
self
,
0
,
0
);
Py_XDECREF
(
dummy
);
Py_XDECREF
(
dummy
);
return
NULL
;
return
NULL
;
}
}
...
@@ -2840,21 +2888,24 @@ DB_verify(DBObject* self, PyObject* args, PyObject* kwargs)
...
@@ -2840,21 +2888,24 @@ DB_verify(DBObject* self, PyObject* args, PyObject* kwargs)
/* XXX(nnorwitz): it should probably be an exception if outFile
/* XXX(nnorwitz): it should probably be an exception if outFile
can't be opened. */
can't be opened. */
MYDB_BEGIN_ALLOW_THREADS
;
err
=
self
->
db
->
verify
(
self
->
db
,
fileName
,
dbName
,
outFile
,
flags
);
MYDB_END_ALLOW_THREADS
;
if
(
outFile
)
fclose
(
outFile
);
{
/* DB.verify acts as a DB handle destructor (like close) */
{
/* DB.verify acts as a DB handle destructor (like close) */
PyObject
*
error
;
PyObject
*
error
;
error
=
DB_close_internal
(
self
,
0
);
error
=
DB_close_internal
(
self
,
0
,
1
);
if
(
error
)
{
if
(
error
)
{
return
error
;
return
error
;
}
}
}
}
MYDB_BEGIN_ALLOW_THREADS
;
err
=
self
->
db
->
verify
(
self
->
db
,
fileName
,
dbName
,
outFile
,
flags
);
MYDB_END_ALLOW_THREADS
;
self
->
db
=
NULL
;
/* Implicit close; related objects already released */
if
(
outFile
)
fclose
(
outFile
);
RETURN_IF_ERR
();
RETURN_IF_ERR
();
RETURN_NONE
();
RETURN_NONE
();
}
}
...
@@ -3978,7 +4029,7 @@ DBEnv_close_internal(DBEnvObject* self, int flags)
...
@@ -3978,7 +4029,7 @@ DBEnv_close_internal(DBEnvObject* self, int flags)
Py_XDECREF
(
dummy
);
Py_XDECREF
(
dummy
);
}
}
while
(
self
->
children_dbs
)
{
while
(
self
->
children_dbs
)
{
dummy
=
DB_close_internal
(
self
->
children_dbs
,
0
);
dummy
=
DB_close_internal
(
self
->
children_dbs
,
0
,
0
);
Py_XDECREF
(
dummy
);
Py_XDECREF
(
dummy
);
}
}
}
}
...
@@ -4003,7 +4054,7 @@ DBEnv_close(DBEnvObject* self, PyObject* args)
...
@@ -4003,7 +4054,7 @@ DBEnv_close(DBEnvObject* self, PyObject* args)
if
(
!
PyArg_ParseTuple
(
args
,
"|i:close"
,
&
flags
))
if
(
!
PyArg_ParseTuple
(
args
,
"|i:close"
,
&
flags
))
return
NULL
;
return
NULL
;
return
DBEnv_close_internal
(
self
,
flags
);
return
DBEnv_close_internal
(
self
,
flags
);
}
}
...
@@ -5949,7 +6000,7 @@ DBTxn_abort_discard_internal(DBTxnObject* self, int discard)
...
@@ -5949,7 +6000,7 @@ DBTxn_abort_discard_internal(DBTxnObject* self, int discard)
}
}
#endif
#endif
while
(
self
->
children_dbs
)
{
while
(
self
->
children_dbs
)
{
dummy
=
DB_close_internal
(
self
->
children_dbs
,
0
);
dummy
=
DB_close_internal
(
self
->
children_dbs
,
0
,
0
);
Py_XDECREF
(
dummy
);
Py_XDECREF
(
dummy
);
}
}
...
@@ -6030,6 +6081,14 @@ DBSequence_close_internal(DBSequenceObject* self, int flags, int do_not_close)
...
@@ -6030,6 +6081,14 @@ DBSequence_close_internal(DBSequenceObject* self, int flags, int do_not_close)
self
->
txn
=
NULL
;
self
->
txn
=
NULL
;
}
}
/*
** "do_not_close" is used to dispose all related objects in the
** tree, without actually releasing the "root" object.
** This is done, for example, because function calls like
** "DBSequence.remove()" implicitly close the underlying handle. So
** the handle doesn't need to be closed, but related objects
** must be cleaned up.
*/
if
(
!
do_not_close
)
{
if
(
!
do_not_close
)
{
MYDB_BEGIN_ALLOW_THREADS
MYDB_BEGIN_ALLOW_THREADS
err
=
self
->
sequence
->
close
(
self
->
sequence
,
flags
);
err
=
self
->
sequence
->
close
(
self
->
sequence
,
flags
);
...
...
Modules/bsddb.h
Dosyayı görüntüle @
5cd5f12a
...
@@ -105,7 +105,7 @@
...
@@ -105,7 +105,7 @@
#error "eek! DBVER can't handle minor versions > 9"
#error "eek! DBVER can't handle minor versions > 9"
#endif
#endif
#define PY_BSDDB_VERSION "4.7.3pre
5
"
#define PY_BSDDB_VERSION "4.7.3pre
9
"
/* Python object definitions */
/* Python object definitions */
...
...
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