Commit 5ebc74ae authored by Benjamin Peterson's avatar Benjamin Peterson

Merged revisions...

Merged revisions 70980,71059,71225,71234,71241,71243,71249,71251,71255,71266,71299,71329,71397-71398,71486 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk

........
  r70980 | jack.diederich | 2009-04-01 15:26:13 -0500 (Wed, 01 Apr 2009) | 3 lines

  bounds check arguments to mmap.move().  All of them.  Really.
  fixes crasher on OS X 10.5
........
  r71059 | mark.dickinson | 2009-04-02 13:39:37 -0500 (Thu, 02 Apr 2009) | 2 lines

  sys.long_info attributes should be ints, not longs
........
  r71225 | georg.brandl | 2009-04-05 06:54:07 -0500 (Sun, 05 Apr 2009) | 1 line

  #5580: no need to use parentheses when converterr() argument is actually a type description.
........
  r71234 | georg.brandl | 2009-04-05 08:16:35 -0500 (Sun, 05 Apr 2009) | 1 line

  Whitespace normalization.
........
  r71241 | georg.brandl | 2009-04-05 09:48:49 -0500 (Sun, 05 Apr 2009) | 1 line

  #5471: fix expanduser() for $HOME set to "/".
........
  r71243 | georg.brandl | 2009-04-05 10:14:29 -0500 (Sun, 05 Apr 2009) | 1 line

  #5432: make plistlib docstring a raw string, since it contains examples with backslash escapes.
........
  r71249 | georg.brandl | 2009-04-05 11:30:43 -0500 (Sun, 05 Apr 2009) | 1 line

  #5444: adapt make.bat to new htmlhelp output file name.
........
  r71251 | georg.brandl | 2009-04-05 12:17:42 -0500 (Sun, 05 Apr 2009) | 1 line

  #5298: clarify docs about GIL by using more consistent wording.
........
  r71255 | georg.brandl | 2009-04-05 13:34:58 -0500 (Sun, 05 Apr 2009) | 1 line

  #602893: add indicator for current line in cgitb that doesnt rely on styling alone.
........
  r71266 | georg.brandl | 2009-04-05 15:23:13 -0500 (Sun, 05 Apr 2009) | 1 line

  Normalize issue referencing style.
........
  r71299 | gregory.p.smith | 2009-04-05 18:43:58 -0500 (Sun, 05 Apr 2009) | 3 lines

  Fixes issue5705: os.setuid() and friends did not accept the same range of
  values that pwd.getpwnam() returns.
........
  r71329 | benjamin.peterson | 2009-04-06 16:53:33 -0500 (Mon, 06 Apr 2009) | 1 line

  add create_connection to __all__ #5711
........
  r71397 | georg.brandl | 2009-04-08 11:36:39 -0500 (Wed, 08 Apr 2009) | 1 line

  Remove redundant backtick.
........
  r71398 | georg.brandl | 2009-04-08 11:39:04 -0500 (Wed, 08 Apr 2009) | 1 line

  Update ignore file for suspicious builder.
........
  r71486 | andrew.kuchling | 2009-04-11 11:18:14 -0500 (Sat, 11 Apr 2009) | 1 line

  Re-word
........
parent f4438468
...@@ -239,7 +239,7 @@ Buffer related functions ...@@ -239,7 +239,7 @@ Buffer related functions
| :cmacro:`PyBUF_FULL` | This is equivalent to ``(PyBUF_INDIRECT | | | :cmacro:`PyBUF_FULL` | This is equivalent to ``(PyBUF_INDIRECT | |
| | PyBUF_FORMAT | PyBUF_WRITABLE)``. | | | PyBUF_FORMAT | PyBUF_WRITABLE)``. |
+------------------------------+---------------------------------------------------+ +------------------------------+---------------------------------------------------+
| :cmacro:`PyBUF_FULL_RO`` | This is equivalent to ``(PyBUF_INDIRECT | | | :cmacro:`PyBUF_FULL_RO` | This is equivalent to ``(PyBUF_INDIRECT | |
| | PyBUF_FORMAT)``. | | | PyBUF_FORMAT)``. |
+------------------------------+---------------------------------------------------+ +------------------------------+---------------------------------------------------+
| :cmacro:`PyBUF_CONTIG` | This is equivalent to ``(PyBUF_ND | | | :cmacro:`PyBUF_CONTIG` | This is equivalent to ``(PyBUF_ND | |
......
This diff is collapsed.
...@@ -334,9 +334,9 @@ Other options ...@@ -334,9 +334,9 @@ Other options
There are still some other options which can be used to handle special cases. There are still some other options which can be used to handle special cases.
The :option:`optional` option is a boolean; if it is true, that specifies that The :option:`optional` option is a boolean; if it is true,
a build failure in the extension should not abort the build process, but simply a build failure in the extension will not abort the build process, but
not install the failing extension. instead simply not install the failing extension.
The :option:`extra_objects` option is a list of object files to be passed to the The :option:`extra_objects` option is a list of object files to be passed to the
linker. These files must not have extensions, as the default extension for the linker. These files must not have extensions, as the default extension for the
......
...@@ -4,6 +4,7 @@ setlocal ...@@ -4,6 +4,7 @@ setlocal
set SVNROOT=http://svn.python.org/projects set SVNROOT=http://svn.python.org/projects
if "%PYTHON%" EQU "" set PYTHON=..\pcbuild\python if "%PYTHON%" EQU "" set PYTHON=..\pcbuild\python
if "%HTMLHELP%" EQU "" set HTMLHELP=%ProgramFiles%\HTML Help Workshop\hhc.exe if "%HTMLHELP%" EQU "" set HTMLHELP=%ProgramFiles%\HTML Help Workshop\hhc.exe
if "%DISTVERSION%" EQU "" for /f "usebackq" %%v in (`%PYTHON% tools/sphinxext/patchlevel.py`) do set DISTVERSION=%%v
if "%1" EQU "" goto help if "%1" EQU "" goto help
if "%1" EQU "html" goto build if "%1" EQU "html" goto build
...@@ -52,7 +53,7 @@ if not exist build\%1 mkdir build\%1 ...@@ -52,7 +53,7 @@ if not exist build\%1 mkdir build\%1
if not exist build\doctrees mkdir build\doctrees if not exist build\doctrees mkdir build\doctrees
cmd /C %PYTHON% --version cmd /C %PYTHON% --version
cmd /C %PYTHON% tools\sphinx-build.py -b%1 -dbuild\doctrees . build\%* cmd /C %PYTHON% tools\sphinx-build.py -b%1 -dbuild\doctrees . build\%*
if "%1" EQU "htmlhelp" "%HTMLHELP%" build\htmlhelp\pydoc.hhp if "%1" EQU "htmlhelp" "%HTMLHELP%" build\htmlhelp\python%DISTVERSION:.=%.hhp
goto end goto end
:end :end
...@@ -48,6 +48,8 @@ library/hotshot,,:lineno,"ncalls tottime percall cumtime percall filename:li ...@@ -48,6 +48,8 @@ library/hotshot,,:lineno,"ncalls tottime percall cumtime percall filename:li
library/httplib,,:port,host:port library/httplib,,:port,host:port
library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS +HHMM""" library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS +HHMM"""
library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS +HHMM""" library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS +HHMM"""
library/itertools,,:stop,elements from seq[start:stop:step]
library/itertools,,:step,elements from seq[start:stop:step]
library/linecache,,:sys,"sys:x:3:3:sys:/dev:/bin/sh" library/linecache,,:sys,"sys:x:3:3:sys:/dev:/bin/sh"
library/logging,,:And, library/logging,,:And,
library/logging,,:package1, library/logging,,:package1,
......
...@@ -142,10 +142,11 @@ function calls leading up to the error, in the order they occurred.</p>''' ...@@ -142,10 +142,11 @@ function calls leading up to the error, in the order they occurred.</p>'''
i = lnum - index i = lnum - index
for line in lines: for line in lines:
num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;' num = small('&nbsp;' * (5-len(str(i))) + str(i)) + '&nbsp;'
line = '<tt>%s%s</tt>' % (num, pydoc.html.preformat(line))
if i in highlight: if i in highlight:
line = '<tt>=&gt;%s%s</tt>' % (num, pydoc.html.preformat(line))
rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line) rows.append('<tr><td bgcolor="#ffccee">%s</td></tr>' % line)
else: else:
line = '<tt>&nbsp;&nbsp;%s%s</tt>' % (num, pydoc.html.preformat(line))
rows.append('<tr><td>%s</td></tr>' % grey(line)) rows.append('<tr><td>%s</td></tr>' % grey(line))
i += 1 i += 1
......
"""plistlib.py -- a tool to generate and parse MacOSX .plist files. r"""plistlib.py -- a tool to generate and parse MacOSX .plist files.
The PropertList (.plist) file format is a simple XML pickle supporting The PropertList (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings. basic object types, like dictionaries, lists, numbers and strings.
......
...@@ -257,7 +257,10 @@ def expanduser(path): ...@@ -257,7 +257,10 @@ def expanduser(path):
userhome = pwent.pw_dir userhome = pwent.pw_dir
if isinstance(path, bytes): if isinstance(path, bytes):
userhome = userhome.encode(sys.getfilesystemencoding()) userhome = userhome.encode(sys.getfilesystemencoding())
userhome = userhome.rstrip(sep) root = b'/'
else:
root = '/'
userhome = userhome.rstrip(root) or userhome
return userhome + path[i:] return userhome + path[i:]
......
...@@ -52,7 +52,7 @@ try: ...@@ -52,7 +52,7 @@ try:
except ImportError: except ImportError:
EBADF = 9 EBADF = 9
__all__ = ["getfqdn"] __all__ = ["getfqdn", "create_connection"]
__all__.extend(os._get_exports_list(_socket)) __all__.extend(os._get_exports_list(_socket))
......
...@@ -357,15 +357,22 @@ class MmapTests(unittest.TestCase): ...@@ -357,15 +357,22 @@ class MmapTests(unittest.TestCase):
m.move(source, dest, size) m.move(source, dest, size)
except ValueError: except ValueError:
pass pass
self.assertRaises(ValueError, m.move, -1, -1, -1)
self.assertRaises(ValueError, m.move, -1, -1, 0) offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
self.assertRaises(ValueError, m.move, -1, 0, -1) (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
self.assertRaises(ValueError, m.move, 0, -1, -1) for source, dest, size in offsets:
self.assertRaises(ValueError, m.move, -1, 0, 0) self.assertRaises(ValueError, m.move, source, dest, size)
self.assertRaises(ValueError, m.move, 0, -1, 0)
self.assertRaises(ValueError, m.move, 0, 0, -1)
m.close() m.close()
m = mmap.mmap(-1, 1) # single byte
self.assertRaises(ValueError, m.move, 0, 0, 2)
self.assertRaises(ValueError, m.move, 1, 0, 1)
self.assertRaises(ValueError, m.move, 0, 1, 1)
m.move(0, 0, 1)
m.move(0, 0, 0)
def test_anonymous(self): def test_anonymous(self):
# anonymous mmap.mmap(-1, PAGE) # anonymous mmap.mmap(-1, PAGE)
m = mmap.mmap(-1, PAGESIZE) m = mmap.mmap(-1, PAGESIZE)
......
...@@ -660,6 +660,48 @@ if sys.platform != 'win32': ...@@ -660,6 +660,48 @@ if sys.platform != 'win32':
class Win32ErrorTests(unittest.TestCase): class Win32ErrorTests(unittest.TestCase):
pass pass
class PosixUidGidTests(unittest.TestCase):
if hasattr(os, 'setuid'):
def test_setuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setuid, 0)
self.assertRaises(OverflowError, os.setuid, 1<<32)
if hasattr(os, 'setgid'):
def test_setgid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setgid, 0)
self.assertRaises(OverflowError, os.setgid, 1<<32)
if hasattr(os, 'seteuid'):
def test_seteuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.seteuid, 0)
self.assertRaises(OverflowError, os.seteuid, 1<<32)
if hasattr(os, 'setegid'):
def test_setegid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setegid, 0)
self.assertRaises(OverflowError, os.setegid, 1<<32)
if hasattr(os, 'setreuid'):
def test_setreuid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setreuid, 0, 0)
self.assertRaises(OverflowError, os.setreuid, 1<<32, 0)
self.assertRaises(OverflowError, os.setreuid, 0, 1<<32)
if hasattr(os, 'setregid'):
def test_setregid(self):
if os.getuid() != 0:
self.assertRaises(os.error, os.setregid, 0, 0)
self.assertRaises(OverflowError, os.setregid, 1<<32, 0)
self.assertRaises(OverflowError, os.setregid, 0, 1<<32)
else:
class PosixUidGidTests(unittest.TestCase):
pass
def test_main(): def test_main():
support.run_unittest( support.run_unittest(
FileTests, FileTests,
...@@ -671,7 +713,8 @@ def test_main(): ...@@ -671,7 +713,8 @@ def test_main():
URandomTests, URandomTests,
ExecTests, ExecTests,
Win32ErrorTests, Win32ErrorTests,
TestInvalidFD TestInvalidFD,
PosixUidGidTests
) )
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -419,6 +419,11 @@ class PosixPathTest(unittest.TestCase): ...@@ -419,6 +419,11 @@ class PosixPathTest(unittest.TestCase):
self.assert_(isinstance(posixpath.expanduser(b"~root/"), bytes)) self.assert_(isinstance(posixpath.expanduser(b"~root/"), bytes))
self.assert_(isinstance(posixpath.expanduser(b"~foo/"), bytes)) self.assert_(isinstance(posixpath.expanduser(b"~foo/"), bytes))
orig_home = os.environ['HOME']
os.environ['HOME'] = '/'
self.assertEqual(posixpath.expanduser("~"), "/")
os.environ['HOME'] = orig_home
self.assertRaises(TypeError, posixpath.expanduser) self.assertRaises(TypeError, posixpath.expanduser)
def test_expandvars(self): def test_expandvars(self):
......
...@@ -344,6 +344,8 @@ class SysModuleTest(unittest.TestCase): ...@@ -344,6 +344,8 @@ class SysModuleTest(unittest.TestCase):
self.assertEqual(len(sys.int_info), 2) self.assertEqual(len(sys.int_info), 2)
self.assert_(sys.int_info.bits_per_digit % 5 == 0) self.assert_(sys.int_info.bits_per_digit % 5 == 0)
self.assert_(sys.int_info.sizeof_digit >= 1) self.assert_(sys.int_info.sizeof_digit >= 1)
self.assertEqual(type(sys.int_info.bits_per_digit), int)
self.assertEqual(type(sys.int_info.sizeof_digit), int)
self.assert_(isinstance(sys.hexversion, int)) self.assert_(isinstance(sys.hexversion, int))
self.assert_(isinstance(sys.maxsize, int)) self.assert_(isinstance(sys.maxsize, int))
self.assert_(isinstance(sys.maxunicode, int)) self.assert_(isinstance(sys.maxunicode, int))
...@@ -622,9 +624,9 @@ class SizeofTest(unittest.TestCase): ...@@ -622,9 +624,9 @@ class SizeofTest(unittest.TestCase):
check(1, size(vh) + self.longdigit) check(1, size(vh) + self.longdigit)
check(-1, size(vh) + self.longdigit) check(-1, size(vh) + self.longdigit)
PyLong_BASE = 2**sys.int_info.bits_per_digit PyLong_BASE = 2**sys.int_info.bits_per_digit
check(PyLong_BASE, size(vh) + 2*self.longdigit) check(int(PyLong_BASE), size(vh) + 2*self.longdigit)
check(PyLong_BASE**2-1, size(vh) + 2*self.longdigit) check(int(PyLong_BASE**2-1), size(vh) + 2*self.longdigit)
check(PyLong_BASE**2, size(vh) + 3*self.longdigit) check(int(PyLong_BASE**2), size(vh) + 3*self.longdigit)
# memory # memory
check(memoryview(b''), size(h + 'P PP2P2i7P')) check(memoryview(b''), size(h + 'P PP2P2i7P'))
# module # module
......
...@@ -620,24 +620,24 @@ mmap_seek_method(mmap_object *self, PyObject *args) ...@@ -620,24 +620,24 @@ mmap_seek_method(mmap_object *self, PyObject *args)
static PyObject * static PyObject *
mmap_move_method(mmap_object *self, PyObject *args) mmap_move_method(mmap_object *self, PyObject *args)
{ {
unsigned long dest, src, count; unsigned long dest, src, cnt;
CHECK_VALID(NULL); CHECK_VALID(NULL);
if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &count) || if (!PyArg_ParseTuple(args, "kkk:move", &dest, &src, &cnt) ||
!is_writable(self)) { !is_writable(self)) {
return NULL; return NULL;
} else { } else {
/* bounds check the values */ /* bounds check the values */
unsigned long pos = src > dest ? src : dest; if (cnt < 0 || (cnt + dest) < cnt || (cnt + src) < cnt ||
if (self->size < pos || count > self->size - pos) { src < 0 || src > self->size || (src + cnt) > self->size ||
dest < 0 || dest > self->size || (dest + cnt) > self->size) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"source or destination out of range"); "source, destination, or count out of range");
return NULL; return NULL;
} else { }
memmove(self->data+dest, self->data+src, count); memmove(self->data+dest, self->data+src, cnt);
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} }
}
} }
static struct PyMethodDef mmap_object_methods[] = { static struct PyMethodDef mmap_object_methods[] = {
......
...@@ -4168,9 +4168,15 @@ Set the current process's user id."); ...@@ -4168,9 +4168,15 @@ Set the current process's user id.");
static PyObject * static PyObject *
posix_setuid(PyObject *self, PyObject *args) posix_setuid(PyObject *self, PyObject *args)
{ {
int uid; long uid_arg;
if (!PyArg_ParseTuple(args, "i:setuid", &uid)) uid_t uid;
if (!PyArg_ParseTuple(args, "l:setuid", &uid_arg))
return NULL; return NULL;
uid = uid_arg;
if (uid != uid_arg) {
PyErr_SetString(PyExc_OverflowError, "user id too big");
return NULL;
}
if (setuid(uid) < 0) if (setuid(uid) < 0)
return posix_error(); return posix_error();
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -4187,10 +4193,16 @@ Set the current process's effective user id."); ...@@ -4187,10 +4193,16 @@ Set the current process's effective user id.");
static PyObject * static PyObject *
posix_seteuid (PyObject *self, PyObject *args) posix_seteuid (PyObject *self, PyObject *args)
{ {
int euid; long euid_arg;
if (!PyArg_ParseTuple(args, "i", &euid)) { uid_t euid;
if (!PyArg_ParseTuple(args, "l", &euid_arg))
return NULL;
euid = euid_arg;
if (euid != euid_arg) {
PyErr_SetString(PyExc_OverflowError, "user id too big");
return NULL; return NULL;
} else if (seteuid(euid) < 0) { }
if (seteuid(euid) < 0) {
return posix_error(); return posix_error();
} else { } else {
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -4207,10 +4219,16 @@ Set the current process's effective group id."); ...@@ -4207,10 +4219,16 @@ Set the current process's effective group id.");
static PyObject * static PyObject *
posix_setegid (PyObject *self, PyObject *args) posix_setegid (PyObject *self, PyObject *args)
{ {
int egid; long egid_arg;
if (!PyArg_ParseTuple(args, "i", &egid)) { gid_t egid;
if (!PyArg_ParseTuple(args, "l", &egid_arg))
return NULL;
egid = egid_arg;
if (egid != egid_arg) {
PyErr_SetString(PyExc_OverflowError, "group id too big");
return NULL; return NULL;
} else if (setegid(egid) < 0) { }
if (setegid(egid) < 0) {
return posix_error(); return posix_error();
} else { } else {
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -4227,10 +4245,17 @@ Set the current process's real and effective user ids."); ...@@ -4227,10 +4245,17 @@ Set the current process's real and effective user ids.");
static PyObject * static PyObject *
posix_setreuid (PyObject *self, PyObject *args) posix_setreuid (PyObject *self, PyObject *args)
{ {
int ruid, euid; long ruid_arg, euid_arg;
if (!PyArg_ParseTuple(args, "ii", &ruid, &euid)) { uid_t ruid, euid;
if (!PyArg_ParseTuple(args, "ll", &ruid_arg, &euid_arg))
return NULL; return NULL;
} else if (setreuid(ruid, euid) < 0) { ruid = ruid_arg;
euid = euid_arg;
if (euid != euid_arg || ruid != ruid_arg) {
PyErr_SetString(PyExc_OverflowError, "user id too big");
return NULL;
}
if (setreuid(ruid, euid) < 0) {
return posix_error(); return posix_error();
} else { } else {
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -4247,10 +4272,17 @@ Set the current process's real and effective group ids."); ...@@ -4247,10 +4272,17 @@ Set the current process's real and effective group ids.");
static PyObject * static PyObject *
posix_setregid (PyObject *self, PyObject *args) posix_setregid (PyObject *self, PyObject *args)
{ {
int rgid, egid; long rgid_arg, egid_arg;
if (!PyArg_ParseTuple(args, "ii", &rgid, &egid)) { gid_t rgid, egid;
if (!PyArg_ParseTuple(args, "ll", &rgid_arg, &egid_arg))
return NULL;
rgid = rgid_arg;
egid = egid_arg;
if (egid != egid_arg || rgid != rgid_arg) {
PyErr_SetString(PyExc_OverflowError, "group id too big");
return NULL; return NULL;
} else if (setregid(rgid, egid) < 0) { }
if (setregid(rgid, egid) < 0) {
return posix_error(); return posix_error();
} else { } else {
Py_INCREF(Py_None); Py_INCREF(Py_None);
...@@ -4267,9 +4299,15 @@ Set the current process's group id."); ...@@ -4267,9 +4299,15 @@ Set the current process's group id.");
static PyObject * static PyObject *
posix_setgid(PyObject *self, PyObject *args) posix_setgid(PyObject *self, PyObject *args)
{ {
int gid; long gid_arg;
if (!PyArg_ParseTuple(args, "i:setgid", &gid)) gid_t gid;
if (!PyArg_ParseTuple(args, "l:setgid", &gid_arg))
return NULL;
gid = gid_arg;
if (gid != gid_arg) {
PyErr_SetString(PyExc_OverflowError, "group id too big");
return NULL; return NULL;
}
if (setgid(gid) < 0) if (setgid(gid) < 0)
return posix_error(); return posix_error();
Py_INCREF(Py_None); Py_INCREF(Py_None);
......
...@@ -1147,7 +1147,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -1147,7 +1147,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if ((Py_ssize_t)strlen(ptr) != size) { if ((Py_ssize_t)strlen(ptr) != size) {
Py_DECREF(s); Py_DECREF(s);
return converterr( return converterr(
"(encoded string without NULL bytes)", "encoded string without NULL bytes",
arg, msgbuf, bufsize); arg, msgbuf, bufsize);
} }
*buffer = PyMem_NEW(char, size + 1); *buffer = PyMem_NEW(char, size + 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment