commit a1bf14a55c81948d2082ad5ed76a891e067e10b9
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Aug 5 19:15:11 2018 +0000
Update translations for torcheck
---
ka/torcheck.po | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/ka/torcheck.po b/ka/torcheck.po
index 96e550468..54c6276b7 100644
--- a/ka/torcheck.po
+++ b/ka/torcheck.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: The Tor Project\…
[View More]n"
"POT-Creation-Date: 2012-02-16 20:28+PDT\n"
-"PO-Revision-Date: 2018-08-05 18:44+0000\n"
+"PO-Revision-Date: 2018-08-05 18:55+0000\n"
"Last-Translator: A. C. <georgianization(a)outlook.com>\n"
"Language-Team: Georgian (http://www.transifex.com/otf/torproject/language/ka/)\n"
"MIME-Version: 1.0\n"
@@ -27,25 +27,25 @@ msgid ""
msgstr "გთხოვთ ეწვიოთ <a href=\"https://www.torproject.org/\">Tor-ის ვებსაიტს</a> დამატებითი ინფორმაციის მისაღებად Tor-ით უსაფრთხო სარგებლობის შესახებ. ახლა უკვე თავისუფლად შეგიძლიათ იმოგზაუროთ ინტერნეტში, ვინაობის გაუმჟღავნებლად."
msgid "There is a security update available for Tor Browser."
-msgstr ""
+msgstr "ხელმისაწვდომია უსაფრთხოების განახლება Tor-ბრაუზერისთვის"
msgid ""
"<a href=\"https://www.torproject.org/download/download-easy.html\">Click "
"here to go to the download page</a>"
-msgstr ""
+msgstr "<a href=\"https://www.torproject.org/download/download-easy.html\">დააწკაპეთ აქ ჩამოტვირთვის გვერდზე გადასასვლელად</a>"
msgid "Sorry. You are not using Tor."
-msgstr ""
+msgstr "სამწუხაროა. თქვენ არ იყენებთ Tor-ს."
msgid ""
"If you are attempting to use a Tor client, please refer to the <a "
"href=\"https://www.torproject.org/\">Tor website</a> and specifically the <a"
" href=\"https://www.torproject.org/docs/faq#DoesntWork\">instructions for "
"configuring your Tor client</a>."
-msgstr ""
+msgstr "თუ ცდილობთ გამართოთ Tor-ის პროგრამა, გთხოვთ ეწვიოთ <a href=\"https://www.torproject.org/\">Tor-ის ვებსაიტს</a> და მიჰყვეთ შესაბამის <a href=\"https://www.torproject.org/docs/faq#DoesntWork\">მითითებებს Tor-პროგრამის სწორად გასამართად</a>."
msgid "Sorry, your query failed or an unexpected response was received."
-msgstr ""
+msgstr "სამწუხაროდ თქვენი მოთხოვნა ვერ დამუშავდა ან მიღებულია მოულოდნელი პასუხი სერვერიდან."
msgid ""
"A temporary service outage prevents us from determining if your source IP "
@@ -53,13 +53,13 @@ msgid ""
msgstr ""
msgid "Your IP address appears to be: "
-msgstr ""
+msgstr "თქვენი IP-მისამართი უნდა იყოს:"
msgid "Are you using Tor?"
-msgstr ""
+msgstr "იყენებთ Tor-ს?"
msgid "This page is also available in the following languages:"
-msgstr ""
+msgstr "ეს გვერდი ასევე ხელმისაწვდომია შემდეგ ენებზე:"
msgid "For more information about this exit relay, see:"
msgstr ""
@@ -70,28 +70,28 @@ msgid ""
msgstr ""
msgid "Learn More »"
-msgstr ""
+msgstr "იხილეთ ვრცლად »"
msgid "Go"
-msgstr ""
+msgstr "გადასვლა"
msgid "Short User Manual"
-msgstr ""
+msgstr "შემოკლებული სახელმძღვანელო"
msgid "Donate to Support Tor"
-msgstr ""
+msgstr "შემოწირულობა Tor-ის მხარდასაჭერად"
msgid "Tor Q&A Site"
-msgstr ""
+msgstr "Tor-ის ხ.დ.კ. გვერდი"
msgid "Volunteer"
-msgstr ""
+msgstr "მოხალისე"
msgid "JavaScript is enabled."
-msgstr ""
+msgstr "JavaScript ჩართულია."
msgid "JavaScript is disabled."
-msgstr ""
+msgstr "JavaScript გამორთულია."
msgid "However, it does not appear to be Tor Browser."
msgstr ""
[View Less]
commit ac99916d10731cf8fa0794b343228f697a12201d
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Aug 5 18:45:12 2018 +0000
Update translations for torcheck
---
ka/torcheck.po | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/ka/torcheck.po b/ka/torcheck.po
index d42ed2f31..96e550468 100644
--- a/ka/torcheck.po
+++ b/ka/torcheck.po
@@ -2,28 +2,29 @@
# Copyright (C) 2008-2013 The Tor Project, Inc
#
# Translators:
+# A. C. &…
[View More]lt;georgianization(a)outlook.com>, 2018
msgid ""
msgstr ""
"Project-Id-Version: The Tor Project\n"
"POT-Creation-Date: 2012-02-16 20:28+PDT\n"
-"PO-Revision-Date: 2015-02-14 08:38+0000\n"
-"Last-Translator: runasand <runa.sandvik(a)gmail.com>\n"
-"Language-Team: Georgian (http://www.transifex.com/projects/p/torproject/language/ka/)\n"
+"PO-Revision-Date: 2018-08-05 18:44+0000\n"
+"Last-Translator: A. C. <georgianization(a)outlook.com>\n"
+"Language-Team: Georgian (http://www.transifex.com/otf/torproject/language/ka/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"Language: ka\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
msgid "Congratulations. This browser is configured to use Tor."
-msgstr ""
+msgstr "გილოცავთ. ბრაუზერი სრულად გამართულია Tor-ით სარგებლობისთვის."
msgid ""
"Please refer to the <a href=\"https://www.torproject.org/\">Tor website</a> "
"for further information about using Tor safely. You are now free to browse "
"the Internet anonymously."
-msgstr ""
+msgstr "გთხოვთ ეწვიოთ <a href=\"https://www.torproject.org/\">Tor-ის ვებსაიტს</a> დამატებითი ინფორმაციის მისაღებად Tor-ით უსაფრთხო სარგებლობის შესახებ. ახლა უკვე თავისუფლად შეგიძლიათ იმოგზაუროთ ინტერნეტში, ვინაობის გაუმჟღავნებლად."
msgid "There is a security update available for Tor Browser."
msgstr ""
[View Less]
commit f8e1bc76031b5ccc8e5683354a4a288d969ba6f5
Author: Translation commit bot <translation(a)torproject.org>
Date: Sun Aug 5 06:15:29 2018 +0000
Update translations for https_everywhere
---
tr/https-everywhere.dtd | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tr/https-everywhere.dtd b/tr/https-everywhere.dtd
index 9d732dc47..5bd4fbbba 100644
--- a/tr/https-everywhere.dtd
+++ b/tr/https-everywhere.dtd
@@ -22,8 +22,8 @@
<!ENTITY https-everywhere.prefs.…
[View More]reset_defaults "Varsayılanları Yükle">
<!ENTITY https-everywhere.prefs.reset_defaults_message "Bu işlem tüm kural kümelerini varsayılan duruma sıfırlar. Devam edilsin mi?">
-<!ENTITY https-everywhere.cancel.he_blocking_explainer "HTTPS Everywhere noticed you were navigating to a non-HTTPS page, and tried to send you to the HTTPS version instead. The HTTPS version is unavailable. Most likely this site does not support HTTPS, but it is also possible that an attacker is blocking the HTTPS version. If you wish to view the unencrypted version of this page, you can still do so by disabling the 'Block all unencrypted requests' option in your HTTPS Everywhere extension. Be aware that disabling this option could make your browser vulnerable to network-based downgrade attacks on websites you visit.">
-<!ENTITY https-everywhere.cancel.he_blocking_network "network-based downgrade attacks">
+<!ENTITY https-everywhere.cancel.he_blocking_explainer "HTTPS Everywhere size HTTPS kullanmadan baktığınız bir sayfanın size HTTPS sürümünü göndermeye çalıştığını algıladı. HTTPS sürüm kullanılamıyor. Genellikle bu durum sitenin HTTPS desteklemediği anlamına gelir ancak bir saldırgan sitenin HTTPS sürümünü engelliyor da olabilir. Bu sayfanın şifrelenmemiş sürümünü görüntülemek istiyorsanız, HTTPS Everywhere eklentisinin ayarlarından 'Şifrelenmemiş tüm istekleri engelle' seçeneğini devre dışı bırakabilirsiniz. Bu seçeneği devre dışı bıraktığınızda web tarayıcınızın ziyaret ettiğiniz web sitelerinden gelebilecek ağ temelli düşürme saldırılarına karşı açık olabileceğini unutmayın.">
+<!ENTITY https-everywhere.cancel.he_blocking_network "ağ temelli düşürme saldırıları">
<!ENTITY https-everywhere.chrome.stable_rules "Kararlı kurallar">
<!ENTITY https-everywhere.chrome.stable_rules_description "Şu web siteleri için şifreli bağlantı dayatılsın:">
[View Less]
commit 8ec17d804e7f6142fae0f14120bdf06e523e3982
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Aug 4 17:04:24 2018 -0700
Better error message if file objects aren't seekable
As reported by teor, stem.descriptor's parse_file() function cannot accept
stdin...
https://trac.torproject.org/projects/tor/ticket/23859
The trouble is that not all file objects in python are seekable. I'd *like*
to handle address this transparently by buffering …
[View More]the content...
try:
descriptor_file.tell()
except IOError:
# file's not seekable, wrapping in a buffer that is
descriptor_file = io.BytesIO(descriptor_file.read())
This works great if our stream has content...
% cat my_descriptors | python demo.py
*But* hangs indefinitely if no EOF is present in the stream.
% python demo.py <= hangs
Turns out non-blocking, platform independent reading of streams like stdin is
pretty tricky...
http://eyalarubas.com/python-subproc-nonblock.html
As such simply providing callers with a more descriptive exception. If they
know their stream won't block *they* can add the above wrapper to provide
us with a seekable file object.
---
stem/descriptor/__init__.py | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 05255648..a9860140 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -93,6 +93,13 @@ __all__ = [
'Descriptor',
]
+UNSEEKABLE_MSG = """\
+File object isn't seekable. Try wrapping it with a BytesIO instead...
+
+ content = my_file.read()
+ parsed_descriptors = stem.descriptor.parse_file(io.BytesIO(content))
+"""
+
KEYWORD_CHAR = 'a-zA-Z0-9-'
WHITESPACE = ' \t'
KEYWORD_LINE = re.compile('^([%s]+)(?:[%s]+(.*))?$' % (KEYWORD_CHAR, WHITESPACE))
@@ -218,6 +225,16 @@ def parse_file(descriptor_file, descriptor_type = None, validate = False, docume
return
+ # Not all files are seekable. If unseekable then advising the user.
+ #
+ # Python 3.x adds an io.seekable() method, but not an option with python 2.x
+ # so using an experimental call to tell() to determine this.
+
+ try:
+ descriptor_file.tell()
+ except IOError:
+ raise IOError(UNSEEKABLE_MSG)
+
# The tor descriptor specifications do not provide a reliable method for
# identifying a descriptor file's type and version so we need to guess
# based on its filename. Metrics descriptors, however, can be identified
[View Less]
commit 542fa1f60552c259e3510e27727a5a2d5f20b7e9
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Aug 4 14:55:40 2018 -0700
Provide None from get_exit_policy when not a relay
When not configured to be a tor relay tor's 'GETINFO exit-policy/full' query
won't function. Rather than attempt to guess the exit policy from other
configuration options providing None for 552 responses...
https://trac.torproject.org/projects/tor/ticket/25853https:/…
[View More]/gitweb.torproject.org/torspec.git/commit/?id=c5453a0
The above spec addition makes it ambiguous if 552 means 'not a relay' or
'we had an error' so reached out for clarificaition on how these should
be handled...
https://trac.torproject.org/projects/tor/ticket/27034
In the process also had to fix get_info to raise OperationFailed rather than
a ProtocolError so callers can get the underlying response codes.
---
docs/change_log.rst | 2 ++
stem/control.py | 41 +++++++++++++----------------------------
stem/response/getinfo.py | 8 ++++++++
test/unit/control/controller.py | 29 ++++++++++++++---------------
4 files changed, 37 insertions(+), 43 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst
index ca8b6805..282a02c8 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -52,7 +52,9 @@ The following are only available within Stem's `git repository
* Stacktrace if :func:`stem.connection.connect` had a string port argument
* More reliable ExitPolicy resolution (:trac:`25739`)
* More reliable caching during configuration changes, especially in multiple-controller situations (:trac:`25821`)
+ * :func:`~stem.control.Controller.get_info` commonly raised :class:`stem.ProtocolError` when it should provide :class:`stem.OperationFailed`
* :func:`~stem.control.Controller.get_microdescriptors` reads descriptors from the control port if available (:spec:`b5396d5`)
+ * :func:`~stem.control.Controller.get_exit_policy` now provides None if not configured to be a relay (:trac:`25853`, :spec:`c5453a0`)
* Added the delivered_read, delivered_written, overhead_read, and overhead_written attributes to :class:`~stem.response.events.CircuitBandwidthEvent` (:spec:`fbb38ec`)
* The *config* attribute of :class:`~stem.response.events.ConfChangedEvent` couldn't represent tor configuration options with multiple values. It has been replaced with new *changed* and *unset* attributes.
* Replaced socket's :func:`~stem.socket.ControlPort.get_address`, :func:`~stem.socket.ControlPort.get_port`, and :func:`~stem.socket.ControlSocketFile.get_socket_path` with attributes
diff --git a/stem/control.py b/stem/control.py
index 77e6140e..19a8759d 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1127,6 +1127,10 @@ class Controller(BaseController):
.. versionchanged:: 1.1.0
Added the get_bytes argument.
+ .. versionchanged:: 1.7.0
+ Errors commonly provided a :class:`stem.ProtocolError` when we should
+ raise a :class:`stem.OperationFailed`.
+
:param str,list params: GETINFO option or options to be queried
:param object default: response if the query fails
:param bool get_bytes: provides **bytes** values rather than a **str** under python 3.x
@@ -1275,10 +1279,13 @@ class Controller(BaseController):
parsing the user's torrc entries. This should be more reliable for
some edge cases. (:trac:`25739`)
+ .. versionchanged:: 1.7.0
+ Returning **None** if not contigured to be a relay.
+
:param object default: response if the query fails
:returns: :class:`~stem.exit_policy.ExitPolicy` of the tor instance that
- we're connected to
+ we're connected to, this is **None** if not configured to be a relay
:raises:
* :class:`stem.ControllerError` if unable to query the policy
@@ -1292,34 +1299,12 @@ class Controller(BaseController):
if not policy:
try:
policy = stem.exit_policy.ExitPolicy(*self.get_info('exit-policy/full').splitlines())
- except stem.ProtocolError:
- # When tor is unable to determine our address 'GETINFO
- # exit-policy/full' fails with...
- #
- # ProtocolError: GETINFO response didn't have an OK status:
- # router_get_my_routerinfo returned NULL
- #
- # https://trac.torproject.org/projects/tor/ticket/25842
- #
- # Failing back to the legacy method we used for getting our exit
- # policy.
+ self._set_cache({'exit_policy': policy})
+ except stem.OperationFailed as exc:
+ if exc.code == '552':
+ return None # not configured to be a relay
- rules = []
-
- if self.get_conf('ExitRelay') == '0':
- rules.append('reject *:*')
-
- if self.get_conf('ExitPolicyRejectPrivate') == '1':
- rules.append('reject private:*')
-
- for policy_line in self.get_conf('ExitPolicy', multiple = True):
- rules += policy_line.split(',')
-
- rules += self.get_info('exit-policy/default').split(',')
-
- policy = stem.exit_policy.get_config_policy(rules, self.get_info('address', None))
-
- self._set_cache({'exit_policy': policy})
+ raise
return policy
diff --git a/stem/response/getinfo.py b/stem/response/getinfo.py
index 03a2cf38..00c77b92 100644
--- a/stem/response/getinfo.py
+++ b/stem/response/getinfo.py
@@ -30,12 +30,20 @@ class GetInfoResponse(stem.response.ControlMessage):
if not self.is_ok() or not remaining_lines.pop() == b'OK':
unrecognized_keywords = []
+ error_code, error_msg = None, None
+
for code, _, line in self.content():
+ if code != '250':
+ error_code = code
+ error_msg = line
+
if code == '552' and line.startswith('Unrecognized key "') and line.endswith('"'):
unrecognized_keywords.append(line[18:-1])
if unrecognized_keywords:
raise stem.InvalidArguments('552', 'GETINFO request contained unrecognized keywords: %s\n' % ', '.join(unrecognized_keywords), unrecognized_keywords)
+ elif error_code:
+ raise stem.OperationFailed(error_code, error_msg)
else:
raise stem.ProtocolError("GETINFO response didn't have an OK status:\n%s" % self)
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index d8e1cc9d..3bdb7dce 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -58,19 +58,19 @@ class TestControl(unittest.TestCase):
msg_mock.return_value = ControlMessage.from_str('551 Address unknown\r\n')
self.assertEqual(None, self.controller._last_address_exc)
- self.assertRaisesRegexp(stem.ProtocolError, 'Address unknown', self.controller.get_info, 'address')
- self.assertEqual("GETINFO response didn't have an OK status:\nAddress unknown", str(self.controller._last_address_exc))
+ self.assertRaisesRegexp(stem.OperationFailed, 'Address unknown', self.controller.get_info, 'address')
+ self.assertEqual('Address unknown', str(self.controller._last_address_exc))
self.assertEqual(1, msg_mock.call_count)
# now that we have a cached failure we should provide that back
- self.assertRaisesRegexp(stem.ProtocolError, 'Address unknown', self.controller.get_info, 'address')
+ self.assertRaisesRegexp(stem.OperationFailed, 'Address unknown', self.controller.get_info, 'address')
self.assertEqual(1, msg_mock.call_count)
# invalidates the cache, transitioning from no address to having one
msg_mock.return_value = ControlMessage.from_str('250-address=17.2.89.80\r\n250 OK\r\n', 'GETINFO')
- self.assertRaisesRegexp(stem.ProtocolError, 'Address unknown', self.controller.get_info, 'address')
+ self.assertRaisesRegexp(stem.OperationFailed, 'Address unknown', self.controller.get_info, 'address')
self.controller._handle_event(ControlMessage.from_str('650 STATUS_SERVER NOTICE EXTERNAL_ADDRESS ADDRESS=17.2.89.80 METHOD=DIRSERV\r\n'))
self.assertEqual('17.2.89.80', self.controller.get_info('address'))
@@ -88,19 +88,19 @@ class TestControl(unittest.TestCase):
get_conf_mock.return_value = None
self.assertEqual(None, self.controller._last_fingerprint_exc)
- self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
- self.assertEqual("GETINFO response didn't have an OK status:\nNot running in server mode", str(self.controller._last_fingerprint_exc))
+ self.assertRaisesRegexp(stem.OperationFailed, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+ self.assertEqual('Not running in server mode', str(self.controller._last_fingerprint_exc))
self.assertEqual(1, msg_mock.call_count)
# now that we have a cached failure we should provide that back
- self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+ self.assertRaisesRegexp(stem.OperationFailed, 'Not running in server mode', self.controller.get_info, 'fingerprint')
self.assertEqual(1, msg_mock.call_count)
# ... but if we become a relay we'll call it again
get_conf_mock.return_value = '443'
- self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+ self.assertRaisesRegexp(stem.OperationFailed, 'Not running in server mode', self.controller.get_info, 'fingerprint')
self.assertEqual(2, msg_mock.call_count)
@patch('stem.control.Controller.get_info')
@@ -158,17 +158,11 @@ class TestControl(unittest.TestCase):
self.controller._is_caching_enabled = True
@patch('stem.control.Controller.get_info')
- @patch('stem.control.Controller.get_conf')
- def test_get_exit_policy(self, get_conf_mock, get_info_mock):
+ def test_get_exit_policy(self, get_info_mock):
"""
Exercises the get_exit_policy() method.
"""
- get_conf_mock.side_effect = lambda param, **kwargs: {
- 'ExitPolicyRejectPrivate': '1',
- 'ExitPolicy': ['accept *:80, accept *:443', 'accept 43.5.5.5,reject *:22'],
- }[param]
-
get_info_mock.side_effect = lambda param, default = None: {
'exit-policy/full': 'reject *:25,reject *:119,reject *:135-139,reject *:445,reject *:563,reject *:1214,reject *:4661-4666,reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*',
}[param]
@@ -190,6 +184,11 @@ class TestControl(unittest.TestCase):
self.assertEqual(expected, self.controller.get_exit_policy())
@patch('stem.control.Controller.get_info')
+ def test_get_exit_policy_if_not_relaying(self, get_info_mock):
+ get_info_mock.side_effect = stem.OperationFailed('552', 'Not running in server mode')
+ self.assertEqual(None, self.controller.get_exit_policy())
+
+ @patch('stem.control.Controller.get_info')
@patch('stem.control.Controller.get_conf')
def test_get_ports(self, get_conf_mock, get_info_mock):
"""
[View Less]
commit 52dec3290bc4da81ce13b308ace82210c4a91eab
Author: Translation commit bot <translation(a)torproject.org>
Date: Sat Aug 4 21:16:19 2018 +0000
Update translations for tor-launcher-properties
---
ka/torlauncher.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ka/torlauncher.properties b/ka/torlauncher.properties
index 66df6540a..bfb9364a2 100644
--- a/ka/torlauncher.properties
+++ b/ka/torlauncher.properties
@@ -7,7 +7,7 @@ torlauncher.…
[View More]tor_exited_during_startup=Tor exited during startup. This might be d
torlauncher.tor_exited=Tor unexpectedly exited. This might be due to a bug in Tor itself, another program on your system, or faulty hardware. Until you restart Tor, the Tor Browser will not able to reach any websites. If the problem persists, please send a copy of your Tor Log to the support team.
torlauncher.tor_exited2=Restarting Tor will not close your browser tabs.
torlauncher.tor_controlconn_failed=Could not connect to Tor control port.
-torlauncher.tor_failed_to_start=Tor failed to start.
+torlauncher.tor_failed_to_start=Tor-ის გაშვება ვერ მოხერხდა.
torlauncher.tor_control_failed=Failed to take control of Tor.
torlauncher.tor_bootstrap_failed=Tor failed to establish a Tor network connection.
torlauncher.tor_bootstrap_failed_details=%1$S failed (%2$S).
[View Less]