tor-commits
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
December 2012
- 17 participants
- 1600 discussions

18 Dec '12
commit d27e93219bd41f5a8b79e631c3ebb46f9fc396b1
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 19:45:06 2012 +0000
Update translations for gettor
---
es/gettor.po | 12 ++++++------
1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/es/gettor.po b/es/gettor.po
index 206f559..61cab43 100644
--- a/es/gettor.po
+++ b/es/gettor.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2011-11-13 22:01+0100\n"
-"PO-Revision-Date: 2012-12-18 18:44+0000\n"
+"PO-Revision-Date: 2012-12-18 19:35+0000\n"
"Last-Translator: strel <strelnic(a)gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/torproject/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -67,13 +67,13 @@ msgid ""
" linux-i386\n"
" linux-x86_64\n"
" source"
-msgstr "Le enviaré un paquete de Tor por correo si me indica cuál quiere.\nPor favor seleccione uno de los siguientes nombres de paquetes:\n\n windows\n macos-i386\n macos-ppc\n linux-i386\n linux-x86_64\n source"
+msgstr "Le enviaré un paquete de Tor por correo si me indica cuál quiere.\nPor favor, seleccione uno de los siguientes nombres de paquetes:\n\n windows\n macos-i386\n macos-ppc\n linux-i386\n linux-x86_64\n source"
#: lib/gettor/i18n.py:56
msgid ""
"Please reply to this mail, and tell me a single package name anywhere \n"
"in the body of your email."
-msgstr "Por favor responda a este correo, y mencione un sólo nombre\nde paquete en cualquier parte del cuerpo del correo."
+msgstr "Por favor, responda a este correo, y mencione un sólo\nnombre de paquete en cualquier parte del cuerpo del correo."
#: lib/gettor/i18n.py:59
msgid ""
@@ -87,18 +87,18 @@ msgid ""
"language you want in the address you send the mail to:\n"
"\n"
" gettor+fa(a)torproject.org"
-msgstr "Para obtener una versión de Tor traducida a su idioma, especifique\nel idioma que quiere en la dirección a la que enviará el correo:\n\n gettor+fa(a)torproject.org"
+msgstr "Para obtener una versión de Tor traducida a su idioma, especifique\nel idioma que quiere en la dirección a la que enviará el correo::\n\n gettor+fa(a)torproject.org"
#: lib/gettor/i18n.py:67
msgid ""
"This example will give you the requested package in a localized\n"
"version for Farsi (Persian). Check below for a list of supported language\n"
"codes. "
-msgstr "Con el ejemplo obtendría la versión localizada al persa (farsi) del\npaquete. Abajo encontrará una lista de códigos de idioma aceptados. "
+msgstr "Con el ejemplo obtendría la versión localizada al persa (farsi) del\npaquete solicitado. Abajo hay una lista de códigos de idioma aceptados. "
#: lib/gettor/i18n.py:71
msgid " List of supported locales:"
-msgstr "Lista de configuraciones regionales soportadas:"
+msgstr "Lista de localizaciones soportadas:"
#: lib/gettor/i18n.py:73
msgid "Here is a list of all available languages:"
1
0

[translation/gettor_completed] Update translations for gettor_completed
by translation@torproject.org 18 Dec '12
by translation@torproject.org 18 Dec '12
18 Dec '12
commit 34915d43f0307bbffc5b180b6d0a267c9cce3a55
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 18:45:09 2012 +0000
Update translations for gettor_completed
---
es/gettor.po | 16 ++++++++--------
1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/es/gettor.po b/es/gettor.po
index 6cc436a..206f559 100644
--- a/es/gettor.po
+++ b/es/gettor.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2011-11-13 22:01+0100\n"
-"PO-Revision-Date: 2012-12-18 18:14+0000\n"
+"PO-Revision-Date: 2012-12-18 18:44+0000\n"
"Last-Translator: strel <strelnic(a)gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/torproject/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -155,7 +155,7 @@ msgstr "Enviar este texto en un correo a GetTor causará que le enviemos el Paqu
msgid ""
"After having received all parts, you need to re-assemble them to \n"
"one package again. This is done as follows:"
-msgstr "Después de haber recibido todas las partes, necesita re-ensamblarlas\nen un sólo paquete de nuevo. Eso se hace de la siguiente manera:"
+msgstr "Después de haber recibido todas las partes, necesita re-ensamblarlas\nen un sólo paquete de nuevo. Esto se hace como sigue:"
#: lib/gettor/i18n.py:108
msgid "1.) Save all received attachments into one folder on your disk."
@@ -172,7 +172,7 @@ msgstr "2.) Descomprima todos los archivos terminados en \".z\". Si ha guardado
msgid ""
"3.) Verify all files as described in the mail you received with \n"
"each package. (gpg --verify)"
-msgstr "3.) Verifique todos los archivos tal como se describe en el correo \nque recibió con cada paquete (gpg --verify)."
+msgstr "3.) Verifique todos los archivos tal como se describe en el correo \nque recibió con cada paquete. (gpg --verify)"
#: lib/gettor/i18n.py:117
msgid ""
@@ -186,7 +186,7 @@ msgid ""
"5.) After unpacking is finished, you should find a newly created \n"
"\".exe\" file in your destination folder. Simply doubleclick\n"
"that and Tor Browser Bundle should start within a few seconds."
-msgstr "5.) Cuando termine de desempaquetar, debe encontrar el recién creado\narchivo \".exe\" en su carpeta destino. Simplemente haga doble clic en\nel archivo y el Paquete de Navegador Tor se iniciará en unos segundos."
+msgstr "5.) Cuando termine de desempaquetar, debe encontrar el archivo \".exe\"\nrecién creado en su carpeta destino. Simplemente haga doble clic en\nel archivo y el Paquete de Navegador Tor se iniciará en unos segundos."
#: lib/gettor/i18n.py:125
msgid "6.) That's it. You're done. Thanks for using Tor and have fun!"
@@ -243,7 +243,7 @@ msgid ""
"is no complete public list of them, even if your ISP is filtering\n"
"connections to all the known Tor relays, they probably won't be able\n"
"to block all the bridges."
-msgstr "Si su conexión a Internet bloquea el acceso a la red Tor, podría\nnecesitar un repetidor puente (bridge). Los bridges son repetidores\nde Tor que no están listados en el repositorio principal de repetidores\n(relays). Como no están listados públicamente, incluso si su proveedor\nde internet (ISP) está filtrando las conexiones con los repetidores de\nTor conocidos, probablemente no podrá bloquear todos los bridges."
+msgstr "Si su conexión a Internet bloquea el acceso a la red Tor, podría\nnecesitar un repetidor puente (bridge). Los bridges son repetidores\nde Tor que no están listados en el repositorio principal de repetidores\n(relays). Al no estar listados públicamente, incluso si su proveedor\nde internet (ISP) está filtrando las conexiones con los repetidores de\nTor conocidos, probablemente no podrá bloquear todos los bridges."
#: lib/gettor/i18n.py:162
msgid ""
@@ -257,7 +257,7 @@ msgstr "Puede conseguir un repetidor puente (bridge) enviando un correo que\ncon
msgid ""
"It is also possible to fetch bridges with a web browser at the following\n"
"url: https://bridges.torproject.org/"
-msgstr "También es posible obtener repetidores puente (bridges) con un\nnavegador web en la dirección: https://bridges.torproject.org/"
+msgstr "También es posible obtener repetidores puente (bridges) con un\nnavegador web, en la dirección: https://bridges.torproject.org/"
#: lib/gettor/i18n.py:170
msgid ""
@@ -266,7 +266,7 @@ msgid ""
"all split files to be received by you before you can save them all\n"
"into the same directory and unpack them by double-clicking the\n"
"first file."
-msgstr "NOTA IMPORTANTE:\nYa que esto forma parte de una solicitud de un archivo fraccionado,\nnecesita esperar a recibir todos los archivos parciales antes de que\npueda guardarlos todos en la misma carpeta y desempaquetarlos\nhaciendo doble-clic en el primero de los archivos."
+msgstr "NOTA IMPORTANTE:\nYa que esto forma parte de una solicitud de un archivo fraccionado,\nnecesita esperar hasta recibir todos los archivos parciales antes de\nque pueda guardarlos todos en la misma carpeta y desempaquetarlos\nhaciendo doble-clic en el primer archivo."
#: lib/gettor/i18n.py:176
msgid ""
@@ -299,7 +299,7 @@ msgid ""
"requested. Please send us another package name or request the same package \n"
"again, but remove the 'split' keyword. In that case we'll send you the whole \n"
"package. Make sure this is what you want."
-msgstr "Desafortunadamente no tenemos el archivo fraccionado del paquete que\nha solicitado. Por favor envíenos otro nombre de paquete, o solicite otra\nvez el mismo eliminando la palabra 'split' y en tal caso le enviaremos el paquete completo. Asegúrese previamente de que esto es lo que quiere."
+msgstr "Desafortunadamente no hay archivo fraccionado disponible del paquete\nque ha solicitado. Por favor, envíenos otro nombre de paquete o solicite\notra vez el mismo eliminando la palabra 'split' y en tal caso le enviaremos\nel paquete completo. Asegúrese antes de que esto es lo que quiere."
#: lib/gettor/i18n.py:193
msgid ""
1
0

18 Dec '12
commit f74c0e77ebeaf1581ea5875f5b164b0afa5cac93
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 18:45:07 2012 +0000
Update translations for gettor
---
es/gettor.po | 16 ++++++++--------
1 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/es/gettor.po b/es/gettor.po
index 6cc436a..206f559 100644
--- a/es/gettor.po
+++ b/es/gettor.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2011-11-13 22:01+0100\n"
-"PO-Revision-Date: 2012-12-18 18:14+0000\n"
+"PO-Revision-Date: 2012-12-18 18:44+0000\n"
"Last-Translator: strel <strelnic(a)gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/torproject/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -155,7 +155,7 @@ msgstr "Enviar este texto en un correo a GetTor causará que le enviemos el Paqu
msgid ""
"After having received all parts, you need to re-assemble them to \n"
"one package again. This is done as follows:"
-msgstr "Después de haber recibido todas las partes, necesita re-ensamblarlas\nen un sólo paquete de nuevo. Eso se hace de la siguiente manera:"
+msgstr "Después de haber recibido todas las partes, necesita re-ensamblarlas\nen un sólo paquete de nuevo. Esto se hace como sigue:"
#: lib/gettor/i18n.py:108
msgid "1.) Save all received attachments into one folder on your disk."
@@ -172,7 +172,7 @@ msgstr "2.) Descomprima todos los archivos terminados en \".z\". Si ha guardado
msgid ""
"3.) Verify all files as described in the mail you received with \n"
"each package. (gpg --verify)"
-msgstr "3.) Verifique todos los archivos tal como se describe en el correo \nque recibió con cada paquete (gpg --verify)."
+msgstr "3.) Verifique todos los archivos tal como se describe en el correo \nque recibió con cada paquete. (gpg --verify)"
#: lib/gettor/i18n.py:117
msgid ""
@@ -186,7 +186,7 @@ msgid ""
"5.) After unpacking is finished, you should find a newly created \n"
"\".exe\" file in your destination folder. Simply doubleclick\n"
"that and Tor Browser Bundle should start within a few seconds."
-msgstr "5.) Cuando termine de desempaquetar, debe encontrar el recién creado\narchivo \".exe\" en su carpeta destino. Simplemente haga doble clic en\nel archivo y el Paquete de Navegador Tor se iniciará en unos segundos."
+msgstr "5.) Cuando termine de desempaquetar, debe encontrar el archivo \".exe\"\nrecién creado en su carpeta destino. Simplemente haga doble clic en\nel archivo y el Paquete de Navegador Tor se iniciará en unos segundos."
#: lib/gettor/i18n.py:125
msgid "6.) That's it. You're done. Thanks for using Tor and have fun!"
@@ -243,7 +243,7 @@ msgid ""
"is no complete public list of them, even if your ISP is filtering\n"
"connections to all the known Tor relays, they probably won't be able\n"
"to block all the bridges."
-msgstr "Si su conexión a Internet bloquea el acceso a la red Tor, podría\nnecesitar un repetidor puente (bridge). Los bridges son repetidores\nde Tor que no están listados en el repositorio principal de repetidores\n(relays). Como no están listados públicamente, incluso si su proveedor\nde internet (ISP) está filtrando las conexiones con los repetidores de\nTor conocidos, probablemente no podrá bloquear todos los bridges."
+msgstr "Si su conexión a Internet bloquea el acceso a la red Tor, podría\nnecesitar un repetidor puente (bridge). Los bridges son repetidores\nde Tor que no están listados en el repositorio principal de repetidores\n(relays). Al no estar listados públicamente, incluso si su proveedor\nde internet (ISP) está filtrando las conexiones con los repetidores de\nTor conocidos, probablemente no podrá bloquear todos los bridges."
#: lib/gettor/i18n.py:162
msgid ""
@@ -257,7 +257,7 @@ msgstr "Puede conseguir un repetidor puente (bridge) enviando un correo que\ncon
msgid ""
"It is also possible to fetch bridges with a web browser at the following\n"
"url: https://bridges.torproject.org/"
-msgstr "También es posible obtener repetidores puente (bridges) con un\nnavegador web en la dirección: https://bridges.torproject.org/"
+msgstr "También es posible obtener repetidores puente (bridges) con un\nnavegador web, en la dirección: https://bridges.torproject.org/"
#: lib/gettor/i18n.py:170
msgid ""
@@ -266,7 +266,7 @@ msgid ""
"all split files to be received by you before you can save them all\n"
"into the same directory and unpack them by double-clicking the\n"
"first file."
-msgstr "NOTA IMPORTANTE:\nYa que esto forma parte de una solicitud de un archivo fraccionado,\nnecesita esperar a recibir todos los archivos parciales antes de que\npueda guardarlos todos en la misma carpeta y desempaquetarlos\nhaciendo doble-clic en el primero de los archivos."
+msgstr "NOTA IMPORTANTE:\nYa que esto forma parte de una solicitud de un archivo fraccionado,\nnecesita esperar hasta recibir todos los archivos parciales antes de\nque pueda guardarlos todos en la misma carpeta y desempaquetarlos\nhaciendo doble-clic en el primer archivo."
#: lib/gettor/i18n.py:176
msgid ""
@@ -299,7 +299,7 @@ msgid ""
"requested. Please send us another package name or request the same package \n"
"again, but remove the 'split' keyword. In that case we'll send you the whole \n"
"package. Make sure this is what you want."
-msgstr "Desafortunadamente no tenemos el archivo fraccionado del paquete que\nha solicitado. Por favor envíenos otro nombre de paquete, o solicite otra\nvez el mismo eliminando la palabra 'split' y en tal caso le enviaremos el paquete completo. Asegúrese previamente de que esto es lo que quiere."
+msgstr "Desafortunadamente no hay archivo fraccionado disponible del paquete\nque ha solicitado. Por favor, envíenos otro nombre de paquete o solicite\notra vez el mismo eliminando la palabra 'split' y en tal caso le enviaremos\nel paquete completo. Asegúrese antes de que esto es lo que quiere."
#: lib/gettor/i18n.py:193
msgid ""
1
0

[translation/gettor_completed] Update translations for gettor_completed
by translation@torproject.org 18 Dec '12
by translation@torproject.org 18 Dec '12
18 Dec '12
commit 82255e8175c6a1fb6037dedc77d9a5e2d97d83f1
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 18:15:07 2012 +0000
Update translations for gettor_completed
---
es/gettor.po | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/es/gettor.po b/es/gettor.po
index d99de69..6cc436a 100644
--- a/es/gettor.po
+++ b/es/gettor.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2011-11-13 22:01+0100\n"
-"PO-Revision-Date: 2012-12-09 07:00+0000\n"
+"PO-Revision-Date: 2012-12-18 18:14+0000\n"
"Last-Translator: strel <strelnic(a)gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/torproject/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -41,14 +41,14 @@ msgid ""
"We only process requests from email services that support \"DKIM\",\n"
"which is an email feature that lets us verify that the address in the\n"
"\"From\" line is actually the one who sent the mail."
-msgstr "Sólo procesamos solicitudes provenientes de servicios de correo\nelectrónico con soporte \"DKIM\", una función de correo que nos\npermite verificar que la dirección en el linea \"De:\" es realmente\nla dirección que envió el correo."
+msgstr "Sólo procesamos solicitudes provenientes de servicios de correo\nelectrónico con soporte \"DKIM\", una función de correo que nos\npermite verificar que la dirección en la linea \"De:\" es realmente\nla dirección que envió el correo."
#: lib/gettor/i18n.py:39
msgid ""
"(We apologize if you didn't ask for this mail. Since your email is from\n"
"a service that doesn't use DKIM, we're sending a short explanation,\n"
"and then we'll ignore this email address for the next day or so.)"
-msgstr "(Le pedimos disculpas si usted no solicitó este correo. Como el\nservicio de su cuenta de correo electrónico no usa DKIM, le estamos\nenviando una explicación breve de la situación, y luego ignoraremos\nesta dirección de correo durante el día siguiente, aproximadamente.)"
+msgstr "(Le pedimos disculpas si no usted solicitó este correo. Al provenir\nsu cuenta de correo electrónico de un servicio que no usa DKIM, le\nenviamos esta explicación breve de la situación, y luego ignoraremos\nesta dirección de correo durante el día siguiente, aproximadamente)"
#: lib/gettor/i18n.py:43 lib/gettor/i18n.py:130
msgid ""
1
0

18 Dec '12
commit 657f5d6e3dd03ee7610ff7f04c0e74e01af25180
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 18:15:04 2012 +0000
Update translations for gettor
---
es/gettor.po | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/es/gettor.po b/es/gettor.po
index d99de69..6cc436a 100644
--- a/es/gettor.po
+++ b/es/gettor.po
@@ -12,7 +12,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2011-11-13 22:01+0100\n"
-"PO-Revision-Date: 2012-12-09 07:00+0000\n"
+"PO-Revision-Date: 2012-12-18 18:14+0000\n"
"Last-Translator: strel <strelnic(a)gmail.com>\n"
"Language-Team: Spanish (http://www.transifex.com/projects/p/torproject/language/es/)\n"
"MIME-Version: 1.0\n"
@@ -41,14 +41,14 @@ msgid ""
"We only process requests from email services that support \"DKIM\",\n"
"which is an email feature that lets us verify that the address in the\n"
"\"From\" line is actually the one who sent the mail."
-msgstr "Sólo procesamos solicitudes provenientes de servicios de correo\nelectrónico con soporte \"DKIM\", una función de correo que nos\npermite verificar que la dirección en el linea \"De:\" es realmente\nla dirección que envió el correo."
+msgstr "Sólo procesamos solicitudes provenientes de servicios de correo\nelectrónico con soporte \"DKIM\", una función de correo que nos\npermite verificar que la dirección en la linea \"De:\" es realmente\nla dirección que envió el correo."
#: lib/gettor/i18n.py:39
msgid ""
"(We apologize if you didn't ask for this mail. Since your email is from\n"
"a service that doesn't use DKIM, we're sending a short explanation,\n"
"and then we'll ignore this email address for the next day or so.)"
-msgstr "(Le pedimos disculpas si usted no solicitó este correo. Como el\nservicio de su cuenta de correo electrónico no usa DKIM, le estamos\nenviando una explicación breve de la situación, y luego ignoraremos\nesta dirección de correo durante el día siguiente, aproximadamente.)"
+msgstr "(Le pedimos disculpas si no usted solicitó este correo. Al provenir\nsu cuenta de correo electrónico de un servicio que no usa DKIM, le\nenviamos esta explicación breve de la situación, y luego ignoraremos\nesta dirección de correo durante el día siguiente, aproximadamente)"
#: lib/gettor/i18n.py:43 lib/gettor/i18n.py:130
msgid ""
1
0
commit 7105ff0208a136d6b5ebf2f6830e9978eff01330
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue Dec 18 08:29:17 2012 -0800
Dropping the unused gtkTools
When we dropped our gtk gui the gtkTools util became unused.
---
src/util/__init__.py | 2 +-
src/util/gtkTools.py | 426 --------------------------------------------------
2 files changed, 1 insertions(+), 427 deletions(-)
diff --git a/src/util/__init__.py b/src/util/__init__.py
index 973527e..5afde12 100644
--- a/src/util/__init__.py
+++ b/src/util/__init__.py
@@ -4,5 +4,5 @@ application's status, making cross platform system calls, parsing tor data,
and safely working with curses (hiding some of the gory details).
"""
-__all__ = ["conf", "connections", "enum", "gtkTools", "hostnames", "log", "panel", "procTools", "procName", "sysTools", "textInput", "torConfig", "torTools", "uiTools"]
+__all__ = ["conf", "connections", "enum", "hostnames", "log", "panel", "procTools", "procName", "sysTools", "textInput", "torConfig", "torTools", "uiTools"]
diff --git a/src/util/gtkTools.py b/src/util/gtkTools.py
deleted file mode 100644
index 2960188..0000000
--- a/src/util/gtkTools.py
+++ /dev/null
@@ -1,426 +0,0 @@
-"""
-Helper module for getting Gtk+ theme colors.
-"""
-
-import gobject
-import gtk
-
-COLOR_MAP = {
- 'normal' : ('fg', gtk.STATE_NORMAL),
- 'active' : ('fg', gtk.STATE_ACTIVE),
- 'insensitive' : ('fg', gtk.STATE_INSENSITIVE),
-}
-
-class Theme:
- def __init__(self):
- self.colors = {}
-
- widget = gtk.Button()
-
- for (key, (prop, state)) in COLOR_MAP.items():
- self.colors[key] = getattr(widget.style, prop)[state]
-
-class ListWrapper(object):
- def __init__(self, container, model=None):
- self.container = []
- self.model = model
-
- for value in container:
- self.append(value)
-
- def append(self, value):
- self.container.append(value)
- gobject.idle_add(self._model_append, value)
-
- def empty(self):
- self.container = []
- gobject.idle_add(self._model_clear)
-
- def __str__(self):
- return str(self.container)
-
- def __repr__(self):
- return str(self.container)
-
- def __len__(self):
- return len(self.container)
-
- def __iadd__(self, other):
- for value in other:
- self.append(value)
-
- def __delitem__(self, key):
- del self.container[key]
-
- gobject.idle_add(self._model_del, key)
-
- def __getitem__(self, key):
- return self.container[key]
-
- def __setitem__(self, key, value):
- self.container[key] = value
-
- gobject.idle_add(self._model_set, key, value)
-
- def _model_append(self, value):
- if not self.model:
- return
-
- row = self._create_row_from_value(value)
- self.model.append(row)
-
- def _model_clear(self):
- if not self.model:
- return
-
- self.model.clear()
-
- def _model_del(self, key):
- if not self.model:
- return
-
- treeIter = self.model.get_iter(key)
- self.model.remove(treeIter)
-
- def _model_set(self, key, value):
- if not self.model:
- return
-
- row = self._create_row_from_value(value)
- self.model[key] = row
-
- def _create_row_from_value(self, value):
- raise NotImplementedError("Subclass must implement abstract method")
-
-class TreeWrapper(ListWrapper):
- def _model_append(self, value):
- if not self.model:
- return
-
- row = self._create_row_from_value(value)
- self.model.append(None, row)
-
-def response_to_dialog(entry, dialog, response):
- dialog.response(response)
-
-def input_size(prompt, default=None):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- hBox = gtk.HBox()
-
- dialog.vbox.pack_end(hBox, True, True, 0)
-
- spinButton = gtk.SpinButton(None)
- spinButton.connect('activate', response_to_dialog, dialog, gtk.RESPONSE_OK)
-
- spinButton.set_increments(1, 10)
- spinButton.set_range(0, 1024)
-
- hBox.pack_start(spinButton, True, True, 0)
-
- comboBox = gtk.combo_box_new_text()
-
- comboBox.append_text("B")
- comboBox.append_text("KB")
- comboBox.append_text("MB")
- comboBox.append_text("GB")
- comboBox.append_text("TB")
- comboBox.append_text("PB")
-
- hBox.pack_end(comboBox, False, False, 0)
-
- if default:
- value, units = default.split()
-
- spinButton.set_value(float(value))
-
- model = comboBox.get_model()
- modelUnits = [row[0] for row in model]
- index = modelUnits.index(units)
- comboBox.set_active(index)
-
- dialog.show_all()
- response = dialog.run()
-
- value = spinButton.get_value_as_int()
-
- model = comboBox.get_model()
- active = comboBox.get_active()
- (units,) = model[active]
-
- dialog.destroy()
-
- return "%d %s" % (value, units) if response == gtk.RESPONSE_OK else None
-
-def input_time(prompt, default=None):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- hBox = gtk.HBox()
-
- dialog.vbox.pack_end(hBox, True, True, 0)
-
- spinButton = gtk.SpinButton(None)
- spinButton.connect('activate', response_to_dialog, dialog, gtk.RESPONSE_OK)
-
- spinButton.set_increments(1, 10)
- spinButton.set_range(0, 1024)
-
- hBox.pack_start(spinButton, True, True, 0)
-
- comboBox = gtk.combo_box_new_text()
-
- comboBox.append_text("seconds")
- comboBox.append_text("minutes")
- comboBox.append_text("hours")
- comboBox.append_text("days")
-
- hBox.pack_end(comboBox, False, False, 0)
-
- if default:
- if default[-1:] != 's':
- default = default + 's'
-
- value, units = default.split()
-
- spinButton.set_value(float(value))
-
- model = comboBox.get_model()
- modelUnits = [row[0] for row in model]
- index = modelUnits.index(units)
- comboBox.set_active(index)
-
- dialog.show_all()
- response = dialog.run()
-
- value = spinButton.get_value_as_int()
-
- model = comboBox.get_model()
- active = comboBox.get_active()
- (units,) = model[active]
-
- dialog.destroy()
-
- return "%d %s" % (value, units) if response == gtk.RESPONSE_OK else None
-
-def input_int(prompt, default=None, csvResponse=False):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- spinButton = gtk.SpinButton(None)
- spinButton.connect('activate', response_to_dialog, dialog, gtk.RESPONSE_OK)
-
- spinButton.set_increments(1, 10)
- spinButton.set_range(0, 65535)
-
- dialog.vbox.pack_end(spinButton, True, True, 0)
-
- if default:
- spinButton.set_value(float(default))
-
- dialog.show_all()
- response = dialog.run()
-
- value = spinButton.get_value_as_int()
-
- dialog.destroy()
-
- return "%d" % (value) if response == gtk.RESPONSE_OK else None
-
-def input_string(prompt, default=None):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- entry = gtk.Entry()
- entry.connect('activate', response_to_dialog, dialog, gtk.RESPONSE_OK)
-
- dialog.vbox.pack_end(entry, True, True, 0)
-
- if default:
- entry.set_text(default)
-
- dialog.show_all()
- response = dialog.run()
-
- text = entry.get_text()
- dialog.destroy()
-
- return text if response == gtk.RESPONSE_OK else None
-
-def input_list(prompt, default, csv=False):
- def on_add_button_clicked(widget, listStore):
- newValue = input_string("Enter new value:")
-
- if newValue:
- row = (newValue,)
- listStore.append(row)
-
- def on_delete_button_clicked(widget, treeView):
- selection = treeView.get_selection()
- model, selectionIter = selection.get_selected()
-
- if (selectionIter):
- model.remove(selectionIter)
-
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- hBox = gtk.HBox()
-
- dialog.vbox.pack_start(hBox, False, False, 0)
-
- addButton = gtk.Button(stock=gtk.STOCK_ADD)
-
- hBox.pack_start(addButton, False, False, 0)
-
- deleteButton = gtk.Button(stock=gtk.STOCK_DELETE)
-
- hBox.pack_start(deleteButton, False, False, 0)
-
- scrolledWindow = gtk.ScrolledWindow()
-
- dialog.vbox.pack_end(scrolledWindow, True, True, 0)
-
- listStore = gtk.ListStore(str)
- treeView = gtk.TreeView(listStore)
- treeViewColumn = gtk.TreeViewColumn("Value")
- cellRenderer = gtk.CellRendererText()
-
- treeViewColumn.pack_start(cellRenderer, True)
- treeViewColumn.add_attribute(cellRenderer, 'text', 0)
- treeView.append_column(treeViewColumn)
-
- scrolledWindow.add(treeView)
-
- addButton.connect('clicked', on_add_button_clicked, listStore)
- deleteButton.connect('clicked', on_delete_button_clicked, treeView)
-
- separator = "," if csv else " "
- if default:
- for value in default.split(separator):
- row = (value,)
- listStore.append(row)
-
- dialog.show_all()
- response = dialog.run()
-
- dialog.destroy()
-
- if not response == gtk.RESPONSE_OK:
- return
-
- return None if len(listStore) == 0 else separator.join([row[0] for row in listStore])
-
-def input_bool(prompt, default=None):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_QUESTION,
- gtk.BUTTONS_OK_CANCEL,
- None)
-
- dialog.set_markup(prompt)
-
- hbox = gtk.HBox()
- buttonTrue = gtk.RadioButton(None, "True")
- buttonFalse = gtk.RadioButton(buttonTrue, "False")
- hbox.pack_start(buttonTrue, True, True, 0)
- hbox.pack_start(buttonFalse, True, True, 0)
-
- dialog.vbox.pack_end(hbox, True, True, 0)
-
- if not default == None:
- if default == 'True':
- buttonTrue.set_active(True)
- elif default == 'False':
- buttonFalse.set_active(True)
-
- dialog.show_all()
- response = dialog.run()
-
- choice = None
-
- if buttonTrue.get_active():
- choice = True
- elif buttonFalse.get_active():
- choice = False
-
- dialog.destroy()
-
- return choice if response == gtk.RESPONSE_OK else None
-
-def input_dir(prompt, default):
- dialog = gtk.FileChooserDialog(prompt,
- None,
- gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN, gtk.RESPONSE_OK))
- dialog.set_default_response(gtk.RESPONSE_OK)
-
- if default:
- dialog.set_filename(default)
-
- dialog.show_all()
- response = dialog.run()
-
- filename = dialog.get_filename()
-
- dialog.destroy()
-
- return filename if response == gtk.RESPONSE_OK else None
-
-def input_filename(prompt, default):
- dialog = gtk.FileChooserDialog(prompt,
- None,
- gtk.FILE_CHOOSER_ACTION_SAVE,
- (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
- gtk.STOCK_OPEN, gtk.RESPONSE_OK))
- dialog.set_default_response(gtk.RESPONSE_OK)
-
- if default:
- dialog.set_filename(default)
-
- dialog.show_all()
- response = dialog.run()
-
- filename = dialog.get_filename()
-
- dialog.destroy()
-
- return filename if response == gtk.RESPONSE_OK else None
-
-def showError(msg):
- dialog = gtk.MessageDialog(None,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- gtk.MESSAGE_ERROR,
- gtk.BUTTONS_OK,
- msg)
-
- dialog.run()
- dialog.destroy()
-
1
0
commit 26ea3b0e690bbbfaf992be19829ac2fa65fc0cc7
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue Dec 18 08:46:37 2012 -0800
Dropping the tor setup wizard
Arm's setup wizard was an interesting idea, and absurdly meticulously
engineered. However, I've *NEVER* heard of someone using it, and even I
wouldn't trust such a complicated and unsused code path to reliably do the
right thing.
There is little point in continuing to support it. It's a ton of code
complicating our codebase for no good reason, and only serves to confuse users
when they're greeted with the setup dialog.
Main takeaway: poll users before building a feature, otherwise you're likely
just wasting your time.
---
README | 1 -
armrc.sample | 6 +-
src/cli/__init__.py | 2 +-
src/cli/controller.py | 120 ---
src/cli/headerPanel.py | 9 -
src/cli/menu/actions.py | 5 -
src/cli/wizard.py | 1014 ----------------------
src/resources/exitNotice/how_tor_works_thumb.png | Bin 8147 -> 0 bytes
src/resources/exitNotice/index.html | 143 ---
src/resources/startTor | 28 -
src/resources/torrcOverride/override.c | 50 --
src/resources/torrcOverride/override.h | 1 -
src/resources/torrcOverride/override.py | 363 --------
src/resources/torrcTemplate.txt | 90 --
src/settings.cfg | 235 -----
src/starter.py | 21 +-
16 files changed, 4 insertions(+), 2084 deletions(-)
diff --git a/README b/README
index 4d0b4c1..8050657 100644
--- a/README
+++ b/README
@@ -201,7 +201,6 @@ Layout:
controller.py - main display loop, handling input and layout
headerPanel.py - top of all pages, providing general information
popups.py - toolkit providing display popups
- wizard.py - provides the relay setup wizard
logPanel.py - (page 1) displays tor, arm, and stem events
configPanel.py - (page 3) editor panel for the tor configuration
diff --git a/armrc.sample b/armrc.sample
index 458fcbe..9072713 100644
--- a/armrc.sample
+++ b/armrc.sample
@@ -36,10 +36,6 @@ features.acsSupport true
# none, red, green, yellow, blue, cyan, magenta, black, white
features.colorOverride none
-# If arm is attached to an arm started tor instance when it shuts down then
-# offers to shut down tor too if true, otherwise skips this prompt.
-features.offerTorShutdownOnQuit false
-
# Includes unicode characters in the interface.
features.printUnicode true
@@ -74,7 +70,7 @@ features.confirmQuit true
# we terminate right away. This is ignored if the user provides an option
# specifying how to connect to tor (ie, a 'startup.interface.*' option or
# startup argument).
-features.allowDetachedStartup true
+features.allowDetachedStartup false
# Paremters for the log panel
# ---------------------------
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index b0d6bd8..052e06c 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "controller", "headerPanel", "logPanel", "popups", "torrcPanel", "wizard"]
+__all__ = ["configPanel", "controller", "headerPanel", "logPanel", "popups", "torrcPanel"]
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 1f2bf2b..478bc02 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -9,7 +9,6 @@ import curses
import threading
import cli.menu.menu
-import cli.wizard
import cli.popups
import cli.headerPanel
import cli.logPanel
@@ -30,7 +29,6 @@ ARM_CONTROLLER = None
CONFIG = {"startup.events": "N3",
"startup.dataDirectory": "~/.arm",
"startup.blindModeEnabled": False,
- "features.offerTorShutdownOnQuit": False,
"features.panels.show.graph": True,
"features.panels.show.log": True,
"features.panels.show.connection": True,
@@ -41,7 +39,6 @@ CONFIG = {"startup.events": "N3",
"features.confirmQuit": True,
"features.graph.type": 1,
"features.graph.bw.prepopulate": True,
- "wizard.default": {},
"log.startTime": log.INFO,
"log.torEventTypeUnrecognized": log.INFO,
"log.configEntryUndefined": log.NOTICE,
@@ -171,7 +168,6 @@ class Controller:
self._isPaused = False
self._forceRedraw = False
self._isDone = False
- self._torManager = TorManager(self)
self._lastDrawn = 0
self.setMsg() # initializes our control message
@@ -401,13 +397,6 @@ class Controller:
if not os.path.exists(dataDir): os.makedirs(dataDir)
return dataDir
- def getTorManager(self):
- """
- Provides management utils for an arm managed tor instance.
- """
-
- return self._torManager
-
def isDone(self):
"""
True if arm should be terminated, false otherwise.
@@ -439,111 +428,6 @@ class Controller:
if isShutdownFlagPresent:
try: torTools.getConn().shutdown()
except IOError, exc: cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
-
- if CONFIG["features.offerTorShutdownOnQuit"]:
- conn = torTools.getConn()
-
- if self.getTorManager().isManaged(conn):
- while True:
- msg = "Shut down the Tor instance arm started (y/n)?"
- confirmationKey = cli.popups.showMsg(msg, attr = curses.A_BOLD)
-
- if confirmationKey in (ord('y'), ord('Y')):
- # attempts a graceful shutdown of tor, showing the issue if
- # unsuccessful then continuing the shutdown
- try: conn.shutdown()
- except IOError, exc: cli.popups.showMsg(str(exc), 3, curses.A_BOLD)
-
- break
- elif confirmationKey in (ord('n'), ord('N')):
- break
-
-class TorManager:
- """
- Bundle of utils for starting and manipulating an arm generated tor instance.
- """
-
- def __init__(self, controller):
- self._controller = controller
-
- def getTorrcPath(self):
- """
- Provides the path to a wizard generated torrc.
- """
-
- return self._controller.getDataDirectory() + "torrc"
-
- def isTorrcAvailable(self):
- """
- True if a wizard generated torrc exists and the user has permissions to
- run it, false otherwise.
- """
-
- torrcLoc = self.getTorrcPath()
- if os.path.exists(torrcLoc):
- # If we aren't running as root and would be trying to bind to low ports
- # then the startup will fail due to permissons. Attempts to check for
- # this in the torrc. If unable to read the torrc then we probably
- # wouldn't be able to use it anyway with our permissions.
-
- if os.getuid() != 0:
- try:
- return not torConfig.isRootNeeded(torrcLoc)
- except IOError, exc:
- log.log(log.INFO, "Failed to read torrc at '%s': %s" % (torrcLoc, exc))
- return False
- else: return True
-
- return False
-
- def isManaged(self, conn):
- """
- Returns true if the given tor instance is managed by us, false otherwise.
-
- Arguments:
- conn - controller instance to be checked
- """
-
- return conn.getInfo("config-file", None) == self.getTorrcPath()
-
- def startManagedInstance(self):
- """
- Starts a managed instance of tor, logging a warning if unsuccessful. This
- returns True if successful and False otherwise.
- """
-
- torrcLoc = self.getTorrcPath()
- os.system("tor --quiet -f %s&" % torrcLoc)
- startTime = time.time()
-
- # attempts to connect for five seconds (tor might or might not be
- # immediately available)
- raisedExc = None
-
- while time.time() - startTime < 5:
- try:
- self.connectManagedInstance()
- return True
- except IOError, exc:
- raisedExc = exc
- time.sleep(0.5)
-
- if raisedExc: log.log(log.WARN, str(raisedExc))
- return False
-
- def connectManagedInstance(self):
- """
- Attempts to connect to a managed tor instance, raising an IOError if
- unsuccessful.
- """
-
- try:
- controller = Controller.from_port(control_port = int(CONFIG["wizard.default"]["Control"]))
- controller.authenticate()
-
- torTools.getConn().init(controller)
- except Exception, exc:
- raise IOError("Unable to connect to Tor: %s" % exc)
def shutdownDaemons():
"""
@@ -631,7 +515,6 @@ def startTorMonitor(startTime):
cli.graphing.graphPanel.loadConfig(config)
cli.connections.connEntry.loadConfig(config)
- cli.wizard.loadConfig(config)
# attempts to fetch the tor pid, warning if unsuccessful (this is needed for
# checking its resource usage, among other things)
@@ -728,7 +611,6 @@ def drawTorMonitor(stdscr, startTime):
# main draw loop
overrideKey = None # uses this rather than waiting on user input
isUnresponsive = False # flag for heartbeat responsiveness check
- if not torTools.getConn().isAlive(): overrideKey = ord('w') # shows wizard
while not control.isDone():
displayPanels = control.getDisplayPanels()
@@ -777,8 +659,6 @@ def drawTorMonitor(stdscr, startTime):
log.log(log.ERR, "Error detected when reloading tor: %s" % sysTools.getFileErrorMsg(exc))
elif key == ord('h') or key == ord('H'):
overrideKey = cli.popups.showHelpPopup()
- elif key == ord('w') or key == ord('W'):
- cli.wizard.showWizard()
elif key == ord('l') - 96:
# force redraw when ctrl+l is pressed
control.redraw(True)
diff --git a/src/cli/headerPanel.py b/src/cli/headerPanel.py
index cf37ac1..4ab3daf 100644
--- a/src/cli/headerPanel.py
+++ b/src/cli/headerPanel.py
@@ -170,15 +170,6 @@ class HeaderPanel(panel.Panel, threading.Thread):
controller.authenticate(authValue) # already got the password above
except Exception, exc:
controller = None
-
- # attempts to use the wizard port too
- try:
- cli.controller.getController().getTorManager().connectManagedInstance()
- log.log(log.NOTICE, "Reconnected to Tor's control port")
- cli.popups.showMsg("Tor reconnected", 1)
- except:
- # displays notice for the first failed connection attempt
- if exc.args: cli.popups.showMsg("Unable to reconnect (%s)" % exc, 3)
if controller:
torTools.getConn().init(controller)
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 7f63b1b..30dff69 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -5,7 +5,6 @@ Generates the menu for arm, binding options with their related actions.
import functools
import cli.popups
-import cli.wizard
import cli.controller
import cli.menu.item
import cli.graphing.graphPanel
@@ -50,7 +49,6 @@ def makeActionsMenu():
"""
control = cli.controller.getController()
- manager = control.getTorManager()
conn = torTools.getConn()
headerPanel = control.getPanel("header")
actionsMenu = cli.menu.item.Submenu("Actions")
@@ -59,11 +57,8 @@ def makeActionsMenu():
if conn.isAlive():
actionsMenu.add(cli.menu.item.MenuItem("Stop Tor", conn.shutdown))
- elif manager.isTorrcAvailable():
- actionsMenu.add(cli.menu.item.MenuItem("Start Tor", manager.startManagedInstance))
actionsMenu.add(cli.menu.item.MenuItem("Reset Tor", conn.reload))
- actionsMenu.add(cli.menu.item.MenuItem("Setup Wizard", cli.wizard.showWizard))
if control.isPaused(): label, arg = "Unpause", False
else: label, arg = "Pause", True
diff --git a/src/cli/wizard.py b/src/cli/wizard.py
deleted file mode 100644
index 02a1f17..0000000
--- a/src/cli/wizard.py
+++ /dev/null
@@ -1,1014 +0,0 @@
-"""
-Provides user prompts for setting up a new relay. This autogenerates a torrc
-that's used by arm to run its own tor instance.
-"""
-
-import re
-import os
-import sys
-import random
-import shutil
-import getpass
-import platform
-import functools
-import curses
-
-import cli.popups
-import cli.controller
-
-import stem.version
-
-from util import connections, enum, log, sysTools, torConfig, torTools, uiTools
-
-# template used to generate the torrc
-TORRC_TEMPLATE = "resources/torrcTemplate.txt"
-
-# basic configuration types we can run as
-RelayType = enum.Enum("RESUME", "RELAY", "EXIT", "BRIDGE", "CLIENT")
-
-# all options that can be configured
-Options = enum.Enum("DIVIDER", "CONTROL", "NICKNAME", "CONTACT", "NOTIFY", "BANDWIDTH", "LIMIT", "CLIENT", "LOWPORTS", "PORTFORWARD", "SYSTEM", "STARTUP", "RSHUTDOWN", "CSHUTDOWN", "NOTICE", "POLICY", "WEBSITES", "EMAIL", "IM", "MISC", "PLAINTEXT", "DISTRIBUTE", "BRIDGED", "BRIDGE1", "BRIDGE2", "BRIDGE3", "REUSE")
-RelayOptions = {RelayType.RELAY: (Options.NICKNAME,
- Options.CONTACT,
- Options.NOTIFY,
- Options.BANDWIDTH,
- Options.LIMIT,
- Options.CLIENT,
- Options.LOWPORTS,
- Options.PORTFORWARD,
- Options.STARTUP,
- Options.RSHUTDOWN,
- Options.SYSTEM),
- RelayType.EXIT: (Options.NICKNAME,
- Options.CONTACT,
- Options.NOTIFY,
- Options.BANDWIDTH,
- Options.LIMIT,
- Options.CLIENT,
- Options.LOWPORTS,
- Options.PORTFORWARD,
- Options.STARTUP,
- Options.RSHUTDOWN,
- Options.SYSTEM,
- Options.DIVIDER,
- Options.NOTICE,
- Options.POLICY,
- Options.WEBSITES,
- Options.EMAIL,
- Options.IM,
- Options.MISC,
- Options.PLAINTEXT),
- RelayType.BRIDGE: (Options.DISTRIBUTE,
- Options.BANDWIDTH,
- Options.LIMIT,
- Options.CLIENT,
- Options.LOWPORTS,
- Options.PORTFORWARD,
- Options.STARTUP,
- Options.RSHUTDOWN,
- Options.SYSTEM),
- RelayType.CLIENT: (Options.BRIDGED,
- Options.BRIDGE1,
- Options.BRIDGE2,
- Options.BRIDGE3,
- Options.REUSE,
- Options.CSHUTDOWN,
- Options.SYSTEM)}
-
-# option sets
-CUSTOM_POLICIES = (Options.WEBSITES, Options.EMAIL, Options.IM, Options.MISC, Options.PLAINTEXT)
-BRIDGE_ENTRIES = (Options.BRIDGE1, Options.BRIDGE2, Options.BRIDGE3)
-
-# other options provided in the prompts
-CANCEL, NEXT, BACK = "Cancel", "Next", "Back"
-
-DESC_SIZE = 5 # height of the description field
-MSG_COLOR = "green"
-OPTION_COLOR = "yellow"
-DISABLED_COLOR = "cyan"
-
-# bracketing pairs used in email address obscuring
-BRACKETS = ((' ', ' '),
- ('<', '>'),
- ('[', ']'),
- ('(', ')'),
- ('{', '}'),
- ('|', '|'))
-
-# version requirements for options
-VERSION_REQUIREMENTS = {
- Options.PORTFORWARD: stem.version.Requirement.TORRC_PORT_FORWARDING,
-}
-
-# tor's defaults for config options, used to filter unneeded options
-TOR_DEFAULTS = {Options.BANDWIDTH: "5 MB",
- Options.REUSE: "10 minutes"}
-
-# path for the torrc to be placed if replacing the torrc for the system wide
-# tor instance
-SYSTEM_DROP_PATH = "/var/lib/tor-arm/torrc"
-OVERRIDE_SCRIPT = "/usr/share/arm/resources/torrcOverride/override.py"
-OVERRIDE_SETUID_SCRIPT = "/usr/bin/torrc-override"
-
-CONFIG = {"wizard.message.role": "",
- "wizard.message.relay": "",
- "wizard.message.exit": "",
- "wizard.message.bridge": "",
- "wizard.message.client": "",
- "wizard.toggle": {},
- "wizard.disabled": [],
- "wizard.suboptions": [],
- "wizard.default": {},
- "wizard.blankValue": {},
- "wizard.label.general": {},
- "wizard.label.role": {},
- "wizard.label.opt": {},
- "wizard.description.general": {},
- "wizard.description.role": {},
- "wizard.description.opt": {},
- "port.category": {},
- "port.exit.all": [],
- "port.exit.web": [],
- "port.exit.mail": [],
- "port.exit.im": [],
- "port.exit.misc": [],
- "port.encrypted": []}
-
-def loadConfig(config):
- config.update(CONFIG)
-
-class ConfigOption:
- """
- Attributes of a configuraition option.
- """
-
- def __init__(self, key, group, default):
- """
- Configuration option constructor.
-
- Arguments:
- key - configuration option identifier used when querying attributes
- group - configuration attribute group this belongs to
- default - initial value, uses the config default if unset
- """
-
- self.key = key
- self.group = group
- self.descriptionCache = None
- self.descriptionCacheArg = None
- self.value = default
- self.validator = None
- self._isEnabled = True
-
- def getKey(self):
- return self.key
-
- def getValue(self):
- return self.value
-
- def getDisplayValue(self):
- if not self.value and self.key in CONFIG["wizard.blankValue"]:
- return CONFIG["wizard.blankValue"][self.key]
- else: return self.value
-
- def getDisplayAttr(self):
- myColor = OPTION_COLOR if self.isEnabled() else DISABLED_COLOR
- return curses.A_BOLD | uiTools.getColor(myColor)
-
- def isEnabled(self):
- return self._isEnabled
-
- def setEnabled(self, isEnabled):
- self._isEnabled = isEnabled
-
- def setValidator(self, validator):
- """
- Custom function used to check that a value is valid before setting it.
- This functor should accept two arguments: this option and the value we're
- attempting to set. If its invalid then a ValueError with the reason is
- expected.
-
- Arguments:
- validator - functor for checking the validitiy of values we set
- """
-
- self.validator = validator
-
- def setValue(self, value):
- """
- Attempts to set our value. If a validator has been set then we first check
- if it's alright, raising a ValueError with the reason if not.
-
- Arguments:
- value - value we're attempting to set
- """
-
- if self.validator: self.validator(self, value)
- self.value = value
-
- def getLabel(self, prefix = ""):
- return prefix + CONFIG["wizard.label.%s" % self.group].get(self.key, "")
-
- def getDescription(self, width, prefix = ""):
- if not self.descriptionCache or self.descriptionCacheArg != width:
- optDescription = CONFIG["wizard.description.%s" % self.group].get(self.key, "")
- self.descriptionCache = _splitStr(optDescription, width)
- self.descriptionCacheArg = width
-
- return [prefix + line for line in self.descriptionCache]
-
-class ToggleConfigOption(ConfigOption):
- """
- Configuration option representing a boolean.
- """
-
- def __init__(self, key, group, default, trueLabel, falseLabel):
- ConfigOption.__init__(self, key, group, default)
- self.trueLabel = trueLabel
- self.falseLabel = falseLabel
-
- def getDisplayValue(self):
- return self.trueLabel if self.value else self.falseLabel
-
- def toggle(self):
- # This isn't really here to validate the value (after all this is a
- # boolean, the options are limited!), but rather give a method for functors
- # to be triggered when selected.
-
- if self.validator: self.validator(self, not self.value)
- self.value = not self.value
-
-def showWizard():
- """
- Provides a series of prompts, allowing the user to spawn a customized tor
- instance.
- """
-
- if not sysTools.isAvailable("tor"):
- msg = "Unable to run the setup wizard. Is tor installed?"
- log.log(log.WARN, msg)
- return
-
- try:
- torVersion = stem.version.get_system_tor_version()
- except IOError, exc:
- torVersion = None
- log.log(log.INFO, "'tor --version' query failed: %s" % exc)
-
- relayType, config = None, {}
- for option in Options.values():
- if option == Options.DIVIDER:
- config[option] = option
- continue
-
- toggleValues = CONFIG["wizard.toggle"].get(option)
- default = CONFIG["wizard.default"].get(option, "")
-
- if toggleValues:
- if "," in toggleValues:
- trueLabel, falseLabel = toggleValues.split(",", 1)
- else: trueLabel, falseLabel = toggleValues, ""
-
- isSet = default.lower() == "true"
- config[option] = ToggleConfigOption(option, "opt", isSet, trueLabel.strip(), falseLabel.strip())
- else: config[option] = ConfigOption(option, "opt", default)
-
- # sets input validators
- config[Options.BANDWIDTH].setValidator(_relayRateValidator)
- config[Options.LIMIT].setValidator(_monthlyLimitValidator)
- config[Options.BRIDGE1].setValidator(_bridgeDestinationValidator)
- config[Options.BRIDGE2].setValidator(_bridgeDestinationValidator)
- config[Options.BRIDGE3].setValidator(_bridgeDestinationValidator)
- config[Options.REUSE].setValidator(_circDurationValidator)
-
- # enables custom policies when 'custom' is selected and disables otherwise
- policyOpt = config[Options.POLICY]
- customPolicies = [config[opt] for opt in CUSTOM_POLICIES]
- policyOpt.setValidator(functools.partial(_toggleEnabledAction, customPolicies))
- _toggleEnabledAction(customPolicies, policyOpt, policyOpt.getValue())
-
- lowPortsOpt = config[Options.LOWPORTS]
- disclaimerNotice = [config[Options.NOTICE]]
- lowPortsOpt.setValidator(functools.partial(_toggleEnabledAction, disclaimerNotice))
- _toggleEnabledAction(disclaimerNotice, lowPortsOpt, lowPortsOpt.getValue())
-
- # enables bridge entries when "Use Bridges" is set and disables otherwise
- useBridgeOpt = config[Options.BRIDGED]
- bridgeEntries = [config[opt] for opt in BRIDGE_ENTRIES]
- useBridgeOpt.setValidator(functools.partial(_toggleEnabledAction, bridgeEntries))
- _toggleEnabledAction(bridgeEntries, useBridgeOpt, useBridgeOpt.getValue())
-
- # enables running at startup when 'Use System Instance' is deselected and
- # disables otherwise
- systemOpt = config[Options.SYSTEM]
- startupOpt = [config[Options.STARTUP]]
- systemOpt.setValidator(functools.partial(_toggleEnabledAction, startupOpt, True))
- _toggleEnabledAction(startupOpt, systemOpt, not systemOpt.getValue())
-
- # remembers the last selection made on the type prompt page
- controller = cli.controller.getController()
- manager = controller.getTorManager()
- relaySelection = RelayType.RESUME if manager.isTorrcAvailable() else RelayType.RELAY
-
- # excludes options that are either disabled or for a future tor version
- disabledOpt = list(CONFIG["wizard.disabled"])
-
- for opt, optVersion in VERSION_REQUIREMENTS.items():
- if torVersion is None or not torVersion.meets_requirements(optVersion):
- disabledOpt.append(opt)
-
- # the port forwarding option would only work if tor-fw-helper is in the path
- if not Options.PORTFORWARD in disabledOpt:
- if not sysTools.isAvailable("tor-fw-helper"):
- disabledOpt.append(Options.PORTFORWARD)
-
- # If we haven't run 'resources/torrcOverride/override.py --init' or lack
- # permissions then we aren't able to deal with the system wide tor instance.
- # Also drop the option if we aren't installed since override.py won't be at
- # the expected path.
- if not os.path.exists(os.path.dirname(SYSTEM_DROP_PATH)) or not os.path.exists(OVERRIDE_SCRIPT):
- disabledOpt.append(Options.SYSTEM)
-
- # TODO: The STARTUP option is currently disabled in the 'settings.cfg', and I
- # don't currently have plans to implement it (it would be a big pita, and the
- # tor deb already handles it). *If* it is implemented then I'd limit support
- # for the option to Debian and Ubuntu to start with, via the following...
-
- # Running at startup is currently only supported for Debian and Ubuntu.
- # Patches welcome for supporting other platforms.
- #if not platform.dist()[0] in ("debian", "Ubuntu"):
- # disabledOpt.append(Options.STARTUP)
-
- while True:
- if relayType == None:
- selection = promptRelayType(relaySelection)
-
- if selection == CANCEL: break
- elif selection == RelayType.RESUME:
- if not manager.isManaged(torTools.getConn()):
- manager.startManagedInstance()
-
- break
- else: relayType, relaySelection = selection, selection
- else:
- selection = promptConfigOptions(relayType, config, disabledOpt)
-
- if selection == BACK: relayType = None
- elif selection == CANCEL: break
- elif selection == NEXT:
- generatedTorrc = getTorrc(relayType, config, disabledOpt)
-
- torrcLocation = manager.getTorrcPath()
- isSystemReplace = not Options.SYSTEM in disabledOpt and config[Options.SYSTEM].getValue()
- if isSystemReplace: torrcLocation = SYSTEM_DROP_PATH
-
- controller.redraw()
- confirmationSelection = showConfirmationDialog(generatedTorrc, torrcLocation)
-
- if confirmationSelection == NEXT:
- log.log(log.INFO, "Writing torrc to '%s':\n%s" % (torrcLocation, generatedTorrc))
-
- # if the torrc already exists then save it to a _bak file
- isBackedUp = False
- if os.path.exists(torrcLocation) and not isSystemReplace:
- try:
- shutil.copy(torrcLocation, torrcLocation + "_bak")
- isBackedUp = True
- except IOError, exc:
- log.log(log.WARN, "Unable to backup the torrc: %s" % exc)
-
- # writes the torrc contents
- try:
- torrcFile = open(torrcLocation, "w")
- torrcFile.write(generatedTorrc)
- torrcFile.close()
- except IOError, exc:
- log.log(log.ERR, "Unable to make torrc: %s" % exc)
- break
-
- # logs where we placed the torrc
- msg = "Tor configuration placed at '%s'" % torrcLocation
- if isBackedUp:
- msg += " (the previous torrc was moved to 'torrc_bak')"
-
- log.log(log.NOTICE, msg)
-
- dataDir = cli.controller.getController().getDataDirectory()
-
- pathPrefix = os.path.dirname(sys.argv[0])
- if pathPrefix and not pathPrefix.endswith("/"):
- pathPrefix = pathPrefix + "/"
-
- # copies exit notice into data directory if it's being used
- if Options.NOTICE in RelayOptions[relayType] and config[Options.NOTICE].getValue() and config[Options.LOWPORTS].getValue():
- src = "%sresources/exitNotice" % pathPrefix
- dst = "%sexitNotice" % dataDir
-
- if not os.path.exists(dst):
- shutil.copytree(src, dst)
-
- # providing a notice that it has sections specific to us operators
- msg = "Exit notice placed at '%s/index.html'. Some of the sections are specific to US relay operators so please change the \"FIXME\" sections if this is inappropriate." % dst
- log.log(log.NOTICE, msg)
-
- runCommand, exitCode = None, 1
-
- if isSystemReplace:
- # running override.py needs root so...
- # - if running as root (bad user, no biscuit!) then run it directly
- # - if the setuid binary is available at '/usr/bin/torrc-override'
- # then use that
- # - attempt sudo in case passwordless sudo is available
- # - if all of the above fail then log instructions
-
- if os.geteuid() == 0: runCommand = OVERRIDE_SCRIPT
- elif os.path.exists(OVERRIDE_SETUID_SCRIPT): runCommand = OVERRIDE_SETUID_SCRIPT
- else:
- # The -n argument to sudo is *supposed* to be available starting
- # with 1.7.0 [1] however this is a dirty lie (Ubuntu 9.10 uses
- # 1.7.0 and even has the option in its man page, but it doesn't
- # work). Instead checking for version 1.7.1.
- #
- # [1] http://www.sudo.ws/pipermail/sudo-users/2009-January/003889.html
-
- sudoVersionResult = sysTools.call("sudo -V")
-
- # version output looks like "Sudo version 1.7.2p7"
- if len(sudoVersionResult) == 1 and sudoVersionResult[0].count(" ") >= 2:
- versionNum = 0
-
- for comp in sudoVersionResult[0].split(" ")[2].split("."):
- if comp and comp[0].isdigit():
- versionNum = (10 * versionNum) + int(comp)
- else:
- # invalid format
- log.log(log.INFO, "Unrecognized sudo version string: %s" % sudoVersionResult[0])
- versionNum = 0
- break
-
- if versionNum >= 171:
- runCommand = "sudo -n %s" % OVERRIDE_SCRIPT
- else:
- log.log(log.INFO, "Insufficient sudo version for the -n argument")
-
- if runCommand: exitCode = os.system("%s > /dev/null 2>&1" % runCommand)
-
- if exitCode != 0:
- msg = "Tor needs root permissions to replace the system wide torrc. To continue...\n- open another terminal\n- run \"sudo %s\"\n- press 'x' here to tell tor to reload" % OVERRIDE_SCRIPT
- log.log(log.NOTICE, msg)
- else: torTools.getConn().reload()
- elif manager.isTorrcAvailable():
- # If we're connected to a managed instance then just need to
- # issue a sighup to pick up the new settings. Otherwise starts
- # a new tor instance.
-
- conn = torTools.getConn()
- if manager.isManaged(conn): conn.reload()
- else: manager.startManagedInstance()
- else:
- # If we don't have permissions to run the torrc we just made then
- # makes a shell script they can run as root to start tor.
-
- src = "%sresources/startTor" % pathPrefix
- dst = "%sstartTor" % dataDir
- if not os.path.exists(dst): shutil.copy(src, dst)
-
- msg = "Tor needs root permissions to start with this configuration (it will drop itself to the current user afterward). To continue...\n- open another terminal\n- run \"sudo %s\"\n- press 'r' here to tell arm to reconnect" % dst
- log.log(log.NOTICE, msg)
-
- break
- elif confirmationSelection == CANCEL: break
-
- # redraws screen to clear away the dialog we just showed
- cli.controller.getController().redraw()
-
-def promptRelayType(initialSelection):
- """
- Provides a prompt for selecting the general role we'd like Tor to run with.
- This returns a RelayType enumeration for the selection, or CANCEL if the
- dialog was canceled.
- """
-
- options = [ConfigOption(opt, "role", opt) for opt in RelayType.values()]
- options.append(ConfigOption(CANCEL, "general", CANCEL))
- selection = RelayType.indexOf(initialSelection)
- height = 28
-
- # drops the resume option if it isn't applicable
- control = cli.controller.getController()
- if not control.getTorManager().isTorrcAvailable():
- options.pop(0)
- height -= 3
- selection -= 1
-
- popup, _, _ = cli.popups.init(height, 58)
- if not popup: return
-
- try:
- popup.win.box()
- curses.cbreak()
-
- # provides the welcoming message
- topContent = _splitStr(CONFIG["wizard.message.role"], 54)
- for i in range(len(topContent)):
- popup.addstr(i + 1, 2, topContent[i], curses.A_BOLD | uiTools.getColor(MSG_COLOR))
-
- while True:
- y, offset = len(topContent) + 1, 0
-
- for opt in options:
- optionFormat = uiTools.getColor(MSG_COLOR)
- if opt == options[selection]: optionFormat |= curses.A_STANDOUT
-
- # Curses has a weird bug where there's a one-pixel alignment
- # difference between bold and regular text, so it looks better
- # to render the whitespace here as not being bold.
-
- offset += 1
- label = opt.getLabel(" ")
- popup.addstr(y + offset, 2, label, optionFormat | curses.A_BOLD)
- popup.addstr(y + offset, 2 + len(label), " " * (54 - len(label)), optionFormat)
- offset += 1
-
- for line in opt.getDescription(52, " "):
- popup.addstr(y + offset, 2, uiTools.padStr(line, 54), optionFormat)
- offset += 1
-
- popup.win.refresh()
- key = control.getScreen().getch()
-
- if key == curses.KEY_UP: selection = (selection - 1) % len(options)
- elif key == curses.KEY_DOWN: selection = (selection + 1) % len(options)
- elif uiTools.isSelectionKey(key): return options[selection].getValue()
- elif key in (27, ord('q'), ord('Q')): return CANCEL # esc or q - cancel
- finally:
- cli.popups.finalize()
-
-def promptConfigOptions(relayType, config, disabledOpt):
- """
- Prompts the user for the configuration of an internal relay.
- """
-
- topContent = _splitStr(CONFIG.get("wizard.message.%s" % relayType.lower(), ""), 54)
-
- options = [config[opt] for opt in RelayOptions[relayType] if not opt in disabledOpt]
- options.append(Options.DIVIDER)
- options.append(ConfigOption(BACK, "general", "(to role selection)"))
- options.append(ConfigOption(NEXT, "general", "(to confirm options)"))
-
- popupHeight = len(topContent) + len(options) + DESC_SIZE + 5
- popup, _, _ = cli.popups.init(popupHeight, 58)
- if not popup: return
- control = cli.controller.getController()
- key, selection = 0, 0
-
- try:
- curses.cbreak()
-
- while True:
- popup.win.erase()
- popup.win.box()
-
- # provides the description for the relay type
- for i in range(len(topContent)):
- popup.addstr(i + 1, 2, topContent[i], curses.A_BOLD | uiTools.getColor(MSG_COLOR))
-
- y, offset = len(topContent) + 1, 0
- for opt in options:
- if opt == Options.DIVIDER:
- offset += 1
- continue
-
- optionFormat = opt.getDisplayAttr()
- if opt == options[selection]: optionFormat |= curses.A_STANDOUT
-
- offset, indent = offset + 1, 0
- if opt.getKey() in CONFIG["wizard.suboptions"]:
- # If the next entry is also a suboption then show a 'T', otherwise
- # end the bracketing.
-
- bracketChar, nextIndex = curses.ACS_LLCORNER, options.index(opt) + 1
- if nextIndex < len(options) and isinstance(options[nextIndex], ConfigOption):
- if options[nextIndex].getKey() in CONFIG["wizard.suboptions"]:
- bracketChar = curses.ACS_LTEE
-
- popup.addch(y + offset, 3, bracketChar, opt.getDisplayAttr())
- popup.addch(y + offset, 4, curses.ACS_HLINE, opt.getDisplayAttr())
-
- indent = 3
-
- labelFormat = " %%-%is%%s" % (30 - indent)
- label = labelFormat % (opt.getLabel(), opt.getDisplayValue())
- popup.addstr(y + offset, 2 + indent, uiTools.padStr(label, 54 - indent), optionFormat)
-
- # little hack to make "Block" policies red
- if opt != options[selection] and not opt.getValue() and opt.getKey() in CUSTOM_POLICIES:
- optionFormat = curses.A_BOLD | uiTools.getColor("red")
- popup.addstr(y + offset, 33, opt.getDisplayValue(), optionFormat)
-
- # divider between the options and description
- offset += 2
- popup.addch(y + offset, 0, curses.ACS_LTEE)
- popup.addch(y + offset, popup.getWidth() - 1, curses.ACS_RTEE)
- popup.hline(y + offset, 1, popup.getWidth() - 2)
-
- # description for the currently selected option
- for line in options[selection].getDescription(54, " "):
- offset += 1
- popup.addstr(y + offset, 1, line, uiTools.getColor(MSG_COLOR))
-
- popup.win.refresh()
- key = control.getScreen().getch()
-
- if key in (curses.KEY_UP, curses.KEY_DOWN):
- posOffset = -1 if key == curses.KEY_UP else 1
- selection = (selection + posOffset) % len(options)
-
- # skips disabled options and dividers
- while options[selection] == Options.DIVIDER or not options[selection].isEnabled():
- selection = (selection + posOffset) % len(options)
- elif uiTools.isSelectionKey(key):
- if selection == len(options) - 2: return BACK # selected back
- elif selection == len(options) - 1: return NEXT # selected next
- elif isinstance(options[selection], ToggleConfigOption):
- options[selection].toggle()
- else:
- newValue = popup.getstr(y + selection + 1, 33, options[selection].getValue(), curses.A_STANDOUT | uiTools.getColor(OPTION_COLOR), 23)
- if newValue:
- try: options[selection].setValue(newValue.strip())
- except ValueError, exc:
- cli.popups.showMsg(str(exc), 3)
- cli.controller.getController().redraw()
- elif key in (27, ord('q'), ord('Q')): return CANCEL
- finally:
- cli.popups.finalize()
-
-def getTorrc(relayType, config, disabledOpt):
- """
- Provides the torrc generated for the given options.
- """
-
- # TODO: When Robert's 'ownership' feature is available take advantage of it
- # for the RSHUTDOWN and CSHUTDOWN options.
-
- pathPrefix = os.path.dirname(sys.argv[0])
- if pathPrefix and not pathPrefix.endswith("/"):
- pathPrefix = pathPrefix + "/"
-
- templateFile = open("%s%s" % (pathPrefix, TORRC_TEMPLATE), "r")
- template = templateFile.readlines()
- templateFile.close()
-
- # generates the options the template expects
- templateOptions = {}
-
- for key, value in config.items():
- if isinstance(value, ConfigOption):
- value = value.getValue()
-
- if key == Options.BANDWIDTH and value.endswith("/s"):
- # truncates "/s" from the rate for RelayBandwidthRate entry
- value = value[:-2]
- elif key == Options.NOTICE:
- # notice option is only applied if using low ports
- value &= config[Options.LOWPORTS].getValue()
- elif key == Options.CONTACT and _isEmailAddress(value):
- # obscures the email address
- value = _obscureEmailAddress(value)
-
- templateOptions[key.upper()] = value
-
- templateOptions[relayType.upper()] = True
- templateOptions["LOW_PORTS"] = config[Options.LOWPORTS].getValue()
-
- # uses double the relay rate for bursts
- bwOpt = Options.BANDWIDTH.upper()
-
- if templateOptions[bwOpt] != TOR_DEFAULTS[Options.BANDWIDTH]:
- relayRateComp = templateOptions[bwOpt].split(" ")
- templateOptions["BURST"] = "%i %s" % (int(relayRateComp[0]) * 2, " ".join(relayRateComp[1:]))
-
- # paths for our tor related resources
-
- dataDir = cli.controller.getController().getDataDirectory()
- templateOptions["NOTICE_PATH"] = "%sexitNotice/index.html" % dataDir
- templateOptions["LOG_ENTRY"] = "notice file %stor_log" % dataDir
- templateOptions["USERNAME"] = getpass.getuser()
-
- # using custom data directory, unless this is for a system wide instance
- if not config[Options.SYSTEM].getValue() or Options.SYSTEM in disabledOpt:
- templateOptions["DATA_DIR"] = "%stor_data" % dataDir
-
- policyCategories = []
- if not config[Options.POLICY].getValue():
- policyCategories = ["web", "mail", "im", "misc"]
- else:
- if config[Options.WEBSITES].getValue(): policyCategories.append("web")
- if config[Options.EMAIL].getValue(): policyCategories.append("mail")
- if config[Options.IM].getValue(): policyCategories.append("im")
- if config[Options.MISC].getValue(): policyCategories.append("misc")
-
- # uses the CSHUTDOWN or RSHUTDOWN option based on if we're running as a
- # client or not
- if relayType == RelayType.CLIENT:
- templateOptions["SHUTDOWN"] = templateOptions[Options.CSHUTDOWN.upper()]
- else:
- templateOptions["SHUTDOWN"] = templateOptions[Options.RSHUTDOWN.upper()]
-
- if policyCategories:
- isEncryptedOnly = not config[Options.PLAINTEXT].getValue()
-
- policyLines = []
- for category in ["all"] + policyCategories:
- # shows a comment at the start of the section saying what it's for
- topicComment = CONFIG["port.category"].get(category)
- if topicComment:
- for topicComp in _splitStr(topicComment, 78):
- policyLines.append("# " + topicComp)
-
- for portEntry in CONFIG.get("port.exit.%s" % category, []):
- # port entry might be an individual port or a range
-
- if isEncryptedOnly and (not portEntry in CONFIG["port.encrypted"]):
- continue # opting to not include plaintext port and ranges
-
- if "-" in portEntry:
- # if this is a range then use the first port's description
- comment = connections.PORT_USAGE.get(portEntry[:portEntry.find("-")])
- else: comment = connections.PORT_USAGE.get(portEntry)
-
- entry = "ExitPolicy accept *:%s" % portEntry
- if comment: policyLines.append("%-30s# %s" % (entry, comment))
- else: policyLines.append(entry)
-
- if category != policyCategories[-1]:
- policyLines.append("") # newline to split categories
-
- templateOptions["EXIT_POLICY"] = "\n".join(policyLines)
-
- # includes input bridges
- bridgeLines = []
- for bridgeOpt in [Options.BRIDGE1, Options.BRIDGE2, Options.BRIDGE3]:
- bridgeValue = config[bridgeOpt].getValue()
- if bridgeValue: bridgeLines.append("Bridge %s" % bridgeValue)
-
- templateOptions["BRIDGES"] = "\n".join(bridgeLines)
-
- # removes disabled options
- for opt in disabledOpt:
- if opt.upper() in templateOptions:
- del templateOptions[opt.upper()]
-
- startupOpt = Options.STARTUP.upper()
- if not config[Options.STARTUP].isEnabled() and startupOpt in templateOptions:
- del templateOptions[startupOpt]
-
- # removes options if they match the tor defaults
- for opt in TOR_DEFAULTS:
- if templateOptions[opt.upper()] == TOR_DEFAULTS[opt]:
- del templateOptions[opt.upper()]
-
- return torConfig.renderTorrc(template, templateOptions)
-
-def showConfirmationDialog(torrcContents, torrcLocation):
- """
- Shows a confirmation dialog with the given torrc contents, returning CANCEL,
- NEXT, or BACK based on the selection.
-
- Arguments:
- torrcContents - lines of torrc contents to be presented
- torrcLocation - path where the torrc will be placed
- """
-
- torrcLines = torrcContents.split("\n")
- options = ["Cancel", "Back to Setup", "Start Tor"]
-
- control = cli.controller.getController()
- screenHeight = control.getScreen().getmaxyx()[0]
- stickyHeight = sum([stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
- isScrollbarVisible = len(torrcLines) + stickyHeight + 5 > screenHeight
-
- xOffset = 3 if isScrollbarVisible else 0
- popup, width, height = cli.popups.init(len(torrcLines) + 5, 84 + xOffset)
- if not popup: return False
-
- try:
- scroll, selection = 0, 2
- curses.cbreak()
-
- while True:
- popup.win.erase()
- popup.win.box()
-
- # renders the scrollbar
- if isScrollbarVisible:
- popup.addScrollBar(scroll, scroll + height - 5, len(torrcLines), 1, height - 4, 1)
-
- # shows the path where the torrc will be placed
- titleMsg = "The following will be placed at '%s':" % torrcLocation
- popup.addstr(0, 0, titleMsg, curses.A_STANDOUT)
-
- # renders the torrc contents
- for i in range(scroll, min(len(torrcLines), height - 5 + scroll)):
- # parses the argument and comment from options
- option, arg, comment = uiTools.cropStr(torrcLines[i], width - 4 - xOffset), "", ""
-
- div = option.find("#")
- if div != -1: option, comment = option[:div], option[div:]
-
- div = option.strip().find(" ")
- if div != -1: option, arg = option[:div], option[div:]
-
- drawX = 2 + xOffset
- popup.addstr(i + 1 - scroll, drawX, option, curses.A_BOLD | uiTools.getColor("green"))
- drawX += len(option)
- popup.addstr(i + 1 - scroll, drawX, arg, curses.A_BOLD | uiTools.getColor("cyan"))
- drawX += len(arg)
- popup.addstr(i + 1 - scroll, drawX, comment, uiTools.getColor("white"))
-
- # divider between the torrc and the options
- popup.addch(height - 4, 0, curses.ACS_LTEE)
- popup.addch(height - 4, width, curses.ACS_RTEE)
- popup.hline(height - 4, 1, width - 1)
- if isScrollbarVisible: popup.addch(height - 4, 2, curses.ACS_BTEE)
-
- # renders the selection options
- confirmationMsg = "Run tor with the above configuration?"
- popup.addstr(height - 3, width - len(confirmationMsg) - 1, confirmationMsg, uiTools.getColor("green") | curses.A_BOLD)
-
- drawX = width - 1
- for i in range(len(options) - 1, -1, -1):
- optionLabel = " %s " % options[i]
- drawX -= (len(optionLabel) + 4)
-
- selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
- popup.addstr(height - 2, drawX, "[", uiTools.getColor("green"))
- popup.addstr(height - 2, drawX + 1, optionLabel, uiTools.getColor("green") | selectionFormat | curses.A_BOLD)
- popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]", uiTools.getColor("green"))
-
- drawX -= 1 # space gap between the options
-
- popup.win.refresh()
- key = cli.controller.getController().getScreen().getch()
-
- if key == curses.KEY_LEFT:
- selection = (selection - 1) % len(options)
- elif key == curses.KEY_RIGHT:
- selection = (selection + 1) % len(options)
- elif uiTools.isScrollKey(key):
- scroll = uiTools.getScrollPosition(key, scroll, height - 5, len(torrcLines))
- elif uiTools.isSelectionKey(key):
- if selection == 0: return CANCEL
- elif selection == 1: return BACK
- else: return NEXT
- elif key in (27, ord('q'), ord('Q')): return CANCEL
- finally:
- cli.popups.finalize()
-
-def _splitStr(msg, width):
- """
- Splits a string into substrings of a given length.
-
- Arguments:
- msg - string to be broken up
- width - max length of any returned substring
- """
-
- results = []
- while msg:
- msgSegment, msg = uiTools.cropStr(msg, width, None, endType = None, getRemainder = True)
- if not msgSegment: break # happens if the width is less than the first word
- results.append(msgSegment.strip())
-
- return results
-
-def _isEmailAddress(address):
- """
- True if the input is an email address, false otherwise.
- """
-
- # just checks if there's an '@' and '.' in the input w/o whitespace
- emailMatcher = re.compile("\S*(a)\S*\.\S*")
- return emailMatcher.match(address)
-
-def _obscureEmailAddress(address):
- """
- Makes some effort to obscure an email address while keeping it readable.
-
- Arguments:
- address - actual email address
- """
-
- address = _obscureChar(address, '@', (_randomCase("at"), ))
- address = _obscureChar(address, '.', (_randomCase("dot"), ))
- return address
-
-def _randomCase(word):
- """
- Provides a word back with the case of its letters randomized.
-
- Arguments:
- word - word for which to randomize the case
- """
-
- result = []
- for letter in word:
- result.append(random.choice((letter.lower(), letter.upper())))
-
- return "".join(result)
-
-def _obscureChar(inputText, target, options):
- """
- Obscures the given character from the input, replacing it with something
- from a set of options and bracketing the selection.
-
- Arguments:
- inputText - text to be obscured
- target - character to be replaced
- options - replacement options for the character
- """
-
- leftSpace = random.randint(0, 3)
- leftFill = random.choice((' ', '_', '-', '=', '<'))
-
- rightSpace = random.randint(0, 3)
- rightFill = random.choice((' ', '_', '-', '=', '>'))
-
- bracketLeft, bracketRight = random.choice(BRACKETS)
- optSelection = random.choice(options)
- replacement = "".join((bracketLeft, leftFill * leftSpace, optSelection, rightFill * rightSpace, bracketRight))
-
- return inputText.replace(target, replacement)
-
-def _toggleEnabledAction(toggleOptions, option, value, invert = False):
- """
- Enables or disables custom exit policy options based on our selection.
-
- Arguments:
- toggleOptions - configuration options to be toggled to match our our
- selection (ie, true -> enabled, false -> disabled)
- options - our config option
- value - the value we're being set to
- invert - inverts selection if true
- """
-
- if invert: value = not value
-
- for opt in toggleOptions:
- opt.setEnabled(value)
-
-def _relayRateValidator(option, value):
- if value.count(" ") != 1:
- msg = "This should be a rate measurement (for instance, \"5 MB/s\")"
- raise ValueError(msg)
-
- rate, units = value.split(" ", 1)
- acceptedUnits = ("KB/s", "MB/s", "GB/s")
- if not rate.isdigit():
- raise ValueError("'%s' isn't an integer" % rate)
- elif not units in acceptedUnits:
- msg = "'%s' is an invalid rate, options include \"%s\"" % (units, "\", \"".join(acceptedUnits))
- raise ValueError(msg)
- elif (int(rate) < 20 and units == "KB/s") or int(rate) < 1:
- raise ValueError("To be usable as a relay the rate must be at least 20 KB/s")
-
-def _monthlyLimitValidator(option, value):
- if value.count(" ") != 1:
- msg = "This should be a traffic size (for instance, \"5 MB\")"
- raise ValueError(msg)
-
- rate, units = value.split(" ", 1)
- acceptedUnits = ("MB", "GB", "TB")
- if not rate.isdigit():
- raise ValueError("'%s' isn't an integer" % rate)
- elif not units in acceptedUnits:
- msg = "'%s' is an invalid unit, options include \"%s\"" % (units, "\", \"".join(acceptedUnits))
- raise ValueError(msg)
- elif (int(rate) < 50 and units == "MB") or int(rate) < 1:
- raise ValueError("To be usable as a relay's monthly limit should be at least 50 MB")
-
-def _bridgeDestinationValidator(option, value):
- if value.count(":") != 1:
- raise ValueError("Bridges are of the form '<ip address>:<port>'")
-
- ipAddr, port = value.split(":", 1)
- if not connections.isValidIpAddress(ipAddr):
- raise ValueError("'%s' is not a valid ip address" % ipAddr)
- elif not port.isdigit() or int(port) < 0 or int(port) > 65535:
- raise ValueError("'%s' isn't a valid port number" % port)
-
-def _circDurationValidator(option, value):
- if value.count(" ") != 1:
- msg = "This should be a time measurement (for instance, \"10 minutes\")"
- raise ValueError(msg)
-
- rate, units = value.split(" ", 1)
- acceptedUnits = ("minute", "minutes", "hour", "hours")
- if not rate.isdigit():
- raise ValueError("'%s' isn't an integer" % rate)
- elif not units in acceptedUnits:
- msg = "'%s' is an invalid rate, options include \"minutes\" or \"hours\""
- raise ValueError(msg)
- elif (int(rate) < 5 and units in ("minute", "minutes")) or int(rate) < 1:
- raise ValueError("This would cause high network load, don't set this to less than five minutes")
-
diff --git a/src/resources/exitNotice/how_tor_works_thumb.png b/src/resources/exitNotice/how_tor_works_thumb.png
deleted file mode 100644
index b9ffe0d..0000000
Binary files a/src/resources/exitNotice/how_tor_works_thumb.png and /dev/null differ
diff --git a/src/resources/exitNotice/index.html b/src/resources/exitNotice/index.html
deleted file mode 100644
index 7475984..0000000
--- a/src/resources/exitNotice/index.html
+++ /dev/null
@@ -1,143 +0,0 @@
-<?xml version="1.0"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-<title>This is a Tor Exit Router</title>
-
-<!--
-
-This notice is intended to be placed on a virtual host for a domain that
-your Tor exit node IP reverse resolves to so that people who may be about
-to file an abuse complaint would check it first before bothering you or
-your ISP. Ex:
-http://tor-exit.yourdomain.org or http://tor-readme.yourdomain.org.
-
-This type of setup has proven very effective at reducing abuse complaints
-for exit node operators.
-
-There are a few places in this document that you may want to customize.
-They are marked with FIXME.
-
-Notice Source:
-https://gitweb.torproject.org/tor.git/blob_plain/HEAD:/contrib/tor-exit-notice.html
-
--->
-
-</head>
-<body>
-
-<p style="text-align:center; font-size:xx-large; font-weight:bold">This is a
-Tor Exit Router</p>
-
-<p>
-Most likely you are accessing this website because you had some issue with
-the traffic coming from this IP. This router is part of the <a
-href="https://www.torproject.org/">Tor Anonymity Network</a>, which is
-dedicated to <a href="https://www.torproject.org/about/overview">providing
-privacy</a> to people who need it most: average computer users. This
-router IP should be generating no other traffic, unless it has been
-compromised.</p>
-
-
-<p style="text-align:center">
-<a href="https://www.torproject.org/about/overview">
-<img src="how_tor_works_thumb.png" alt="How Tor works" style="border-style:none"/>
-</a></p>
-
-<p>
-Tor sees use by <a href="https://www.torproject.org/about/torusers">many
-important segments of the population</a>, including whistle blowers,
-journalists, Chinese dissidents skirting the Great Firewall and oppressive
-censorship, abuse victims, stalker targets, the US military, and law
-enforcement, just to name a few. While Tor is not designed for malicious
-computer users, it is true that they can use the network for malicious ends.
-In reality however, the actual amount of <a
-href="https://www.torproject.org/docs/faq-abuse">abuse</a> is quite low. This
-is largely because criminals and hackers have significantly better access to
-privacy and anonymity than do the regular users whom they prey upon. Criminals
-can and do <a
-href="http://voices.washingtonpost.com/securityfix/2008/08/web_fraud_20_tools.html">build,
-sell, and trade</a> far larger and <a
-href="http://voices.washingtonpost.com/securityfix/2008/08/web_fraud_20_distribut…">more
-powerful networks</a> than Tor on a daily basis. Thus, in the mind of this
-operator, the social need for easily accessible censorship-resistant private,
-anonymous communication trumps the risk of unskilled bad actors, who are
-almost always more easily uncovered by traditional police work than by
-extensive monitoring and surveillance anyway.</p>
-
-<p>
-In terms of applicable law, the best way to understand Tor is to consider it a
-network of routers operating as common carriers, much like the Internet
-backbone. However, unlike the Internet backbone routers, Tor routers
-explicitly do not contain identifiable routing information about the source of
-a packet, and no single Tor node can determine both the origin and destination
-of a given transmission.</p>
-
-<p>
-As such, there is little the operator of this router can do to help you track
-the connection further. This router maintains no logs of any of the Tor
-traffic, so there is little that can be done to trace either legitimate or
-illegitimate traffic (or to filter one from the other). Attempts to
-seize this router will accomplish nothing.</p>
-
-<!-- FIXME: US-Only section. Remove if you are a non-US operator -->
-
-<p>
-Furthermore, this machine also serves as a carrier of email, which means that
-its contents are further protected under the ECPA. <a
-href="http://www4.law.cornell.edu/uscode/html/uscode18/usc_sec_18_00002707----000…">18
-USC 2707</a> explicitly allows for civil remedies ($1000/account
-<i><b>plus</b></i> legal fees)
-in the event of a seizure executed without good faith or probable cause (it
-should be clear at this point that traffic with this originating IP address
-should not constitute probable cause to seize the machine). Similar
-considerations exist for 1st amendment content on this machine.</p>
-
-<!-- FIXME: May or may not be US-only. Some non-US tor nodes have in
- fact reported DMCA harassment... -->
-
-<p>
-If you are a representative of a company who feels that this router is being
-used to violate the DMCA, please be aware that this machine does not host or
-contain any illegal content. Also be aware that network infrastructure
-maintainers are not liable for the type of content that passes over their
-equipment, in accordance with <a
-href="http://www4.law.cornell.edu/uscode/html/uscode17/usc_sec_17_00000512----000…">DMCA
-"safe harbor" provisions</a>. In other words, you will have just as much luck
-sending a takedown notice to the Internet backbone providers. Please consult
-<a href="https://www.torproject.org/eff/tor-dmca-response">EFF's prepared
-response</a> for more information on this matter.</p>
-
-<p>For more information, please consult the following documentation:</p>
-
-<ol>
-<li><a href="https://www.torproject.org/about/overview">Tor Overview</a></li>
-<li><a href="https://www.torproject.org/docs/faq-abuse">Tor Abuse FAQ</a></li>
-<li><a href="https://www.torproject.org/eff/tor-legal-faq">Tor Legal FAQ</a></li>
-</ol>
-
-<p>
-That being said, if you still have a complaint about the router, you may
-email the <a href="mailto:FIXME_YOUR_EMAIL_ADDRESS">maintainer</a>. If
-complaints are related to a particular service that is being abused, I will
-consider removing that service from my exit policy, which would prevent my
-router from allowing that traffic to exit through it. I can only do this on an
-IP+destination port basis, however. Common P2P ports are
-already blocked.</p>
-
-<p>
-You also have the option of blocking this IP address and others on
-the Tor network if you so desire. The Tor project provides a <a
-href="https://check.torproject.org/cgi-bin/TorBulkExitList.py">web service</a>
-to fetch a list of all IP addresses of Tor exit nodes that allow exiting to a
-specified IP:port combination, and an official <a
-href="https://www.torproject.org/tordnsel/dist/">DNSRBL</a> is also available to
-determine if a given IP address is actually a Tor exit server. Please
-be considerate
-when using these options. It would be unfortunate to deny all Tor users access
-to your site indefinitely simply because of a few bad apples.</p>
-
-</body>
-</html>
diff --git a/src/resources/startTor b/src/resources/startTor
deleted file mode 100755
index 812a75a..0000000
--- a/src/resources/startTor
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-
-# When binding to privileged ports the tor process needs to start with root
-# permissions, then lower the user it's running as afterward.
-
-# checks that we're running as root
-
-if [ "$(id -u)" != "0" ]; then
- printf "This script needs root permissions to run. Try again with \"sudo ${0}\".\n\n"
- exit 1
-fi
-
-# Checks that the torrc in this directory has a "User <username>" entry. If
-# they ran the wizard multiple times then we might currently have a torrc
-# without it, causing this to run tor as root (... not what we wanted).
-
-torrcLoc=$( dirname "$0" )/torrc
-if ! `grep -q "^User " ${torrcLoc}`; then
- printf "The tor configuration file (${torrcLoc}) doesn't lower its\n"
- printf "permissions. You should only be using this script to run tor instances that\n"
- printf "need root permissions to start.\n\n"
- exit 1
-fi
-
-# starts the tor process
-
-tor --quiet -f $torrcLoc&
-
diff --git a/src/resources/torrcOverride/override.c b/src/resources/torrcOverride/override.c
deleted file mode 100644
index b989584..0000000
--- a/src/resources/torrcOverride/override.c
+++ /dev/null
@@ -1,50 +0,0 @@
-//
-// This is a very small C wrapper that invokes
-// $(DESTDIR)/usr/bin/tor-arm-replace-torrc.py to work around setuid scripting
-// issues on the Gnu/Linux operating system.
-//
-// We assume you have installed it as such for GROUP
-// "debian-arm" - This should ensure that only members of the GROUP group will
-// be allowed to run this program. When run this program will execute the
-// $(DESTDIR)/usr/bin/tor-arm-replace-torrc.py program and will run with the
-// uid and group as marked by the OS.
-//
-// Compile it like so:
-//
-// make
-//
-// Or by hand like so:
-//
-// gcc -o tor-arm-replace-torrc tor-arm-replace-torrc.c
-//
-// Make it useful like so:
-//
-// chown root:debian-arm tor-arm-replace-torrc
-// chmod 04750 tor-arm-replace-torrc
-//
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-//
-// If you place a user inside of the $GROUP - they are now able to reconfigure
-// Tor. This may lead them to do nasty things on your system. If you start Tor
-// as root, you should consider that adding a user to $GROUP is similar to
-// giving those users root directly.
-//
-// This program was written simply to help a users who run arm locally and is
-// not required if arm is communicating with a remote Tor process.
-//
-// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-//
-//
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "tor-arm-replace-torrc.h"
-
-int main()
-{
- return execve(TOR_ARM_REPLACE_TORRC, NULL, NULL);
-}
diff --git a/src/resources/torrcOverride/override.h b/src/resources/torrcOverride/override.h
deleted file mode 100644
index 8bb8869..0000000
--- a/src/resources/torrcOverride/override.h
+++ /dev/null
@@ -1 +0,0 @@
-#define TOR_ARM_REPLACE_TORRC /usr/share/arm/resources/torrcOverride/override.py
diff --git a/src/resources/torrcOverride/override.py b/src/resources/torrcOverride/override.py
deleted file mode 100755
index 8261eab..0000000
--- a/src/resources/torrcOverride/override.py
+++ /dev/null
@@ -1,363 +0,0 @@
-#!/usr/bin/python
-
-"""
-This overwrites the system wide torrc, located at /etc/tor/torrc, with the
-contents of the ARM_CONFIG_FILE. The system wide torrc is owned by root so
-this will effectively need root permissions and for us to be in GROUP.
-
-This file is completely unusable until we make a tor-arm user and perform
-other prep by running with the '--init' argument.
-
-After that's done this is meant to either be used by arm automatically after
-writing a torrc to ARM_CONFIG_FILE or by users manually. For arm to use this
-automatically you'll either need to...
-
-- compile override.c, setuid on the binary, and move it to your path
- cd /usr/share/arm/resources/torrcOverride
- make
- chown root:tor-arm override
- chmod 04750 override
- mv override /usr/bin/torrc-override
-
-- allow passwordless sudo for this script
- edit /etc/sudoers and add a line with:
- <arm user> ALL= NOPASSWD: /usr/share/arm/resources/torrcOverride/override.py
-
-To perform this manually run:
-/usr/share/arm/resources/torrcOverride/override.py
-pkill -sighup tor
-"""
-
-import os
-import re
-import sys
-import grp
-import pwd
-import time
-import shutil
-import tempfile
-import signal
-
-USER = "tor-arm"
-GROUP = "tor-arm"
-TOR_CONFIG_FILE = "/etc/tor/torrc"
-ARM_CONFIG_FILE = "/var/lib/tor-arm/torrc"
-RUN_VERIFY = True # use 'tor --verify-config' to check the torrc
-
-# regex patterns for options the wizard may include
-WIZARD_OPT = ("^DataDirectory \S+$",
- "^Log notice file \S+$",
- "^ControlPort 9052$",
- "^CookieAuthentication 1$",
- "^RunAsDaemon 1$",
- "^User \S+$",
- "^ORPort (443|9001)$",
- "^DirPort (80|9030)$",
- "^Nickname ",
- "^ContactInfo ",
- "^BridgeRelay 1$",
- "^PublishServerDescriptor 0$",
- "^RelayBandwidthRate ",
- "^RelayBandwidthBurst ",
- "^AccountingMax ",
- "^SocksPort 0$",
- "^PortForwarding 1$",
- "^ExitPolicy (accept|reject) \S+$",
- "^DirPortFrontPage \S+$",
- "^ClientOnly 1$",
- "^MaxCircuitDirtiness ",
- "^UseBridges 1$",
- "^Bridge ")
-
-HELP_MSG = """Usage %s [OPTION]
- Backup the system wide torrc (%s) and replace it with the
- contents of %s.
-
- --init creates the necessary user and paths
- --remove reverts changes made with --init
- --validate PATH checks if the given file is wizard generated
-""" % (os.path.basename(sys.argv[0]), TOR_CONFIG_FILE, ARM_CONFIG_FILE)
-
-def init():
- """
- Performs system preparation needed for this script to run, adding the tor-arm
- user and setting up paths/permissions.
- """
-
- # the following is just here if we have a custom destination directory (which
- # arm doesn't currently account for)
- if not os.path.exists("/bin/"):
- print "making '/bin'..."
- os.mkdir("/bin")
-
- if not os.path.exists("/var/lib/tor-arm/"):
- print "making '/var/lib/tor-arm'..."
- os.makedirs("/var/lib/tor-arm")
-
- if not os.path.exists("/var/lib/tor-arm/torrc"):
- print "making '/var/lib/tor-arm/torrc'..."
- open("/var/lib/tor-arm/torrc", 'w').close()
-
- try: gid = grp.getgrnam(GROUP).gr_gid
- except KeyError:
- print "adding %s group..." % GROUP
- os.system("addgroup --quiet --system %s" % GROUP)
- gid = grp.getgrnam(GROUP).gr_gid
- print " done, gid: %s" % gid
-
- try: pwd.getpwnam(USER).pw_uid
- except KeyError:
- print "adding %s user..." % USER
- os.system("adduser --quiet --ingroup %s --no-create-home --home /var/lib/tor-arm/ --shell /bin/sh --system %s" % (GROUP, USER))
- uid = pwd.getpwnam(USER).pw_uid
- print " done, uid: %s" % uid
-
- os.chown("/bin", 0, 0)
- os.chown("/var/lib/tor-arm", 0, gid)
- os.chmod("/var/lib/tor-arm", 0750)
- os.chown("/var/lib/tor-arm/torrc", 0, gid)
- os.chmod("/var/lib/tor-arm/torrc", 0760)
-
-def remove():
- """
- Reverts the changes made by init, and also removes the optional
- /bin/torrc-override binary if it exists.
- """
-
- print "removing %s user..." % USER
- os.system("deluser --quiet %s" % USER)
-
- print "removing %s group..." % GROUP
- os.system("delgroup --quiet %s" % GROUP)
-
- if os.path.exists("/bin/torrc-override"):
- try:
- print "removing '/bin/torrc-override'..."
- os.remove("/bin/torrc-override")
- except OSError, exc:
- print " unsuccessful: %s" % exc
-
- if os.path.exists("/var/lib/tor-arm/"):
- try:
- print "removing '/var/lib/tor-arm'..."
- shutil.rmtree("/var/lib/tor-arm/")
- except Exception, exc:
- print " unsuccessful: %s" % exc
-
-def replaceTorrc():
- # TODO: The setresgid and setresuid functions are only available in
- # python 2.7 (arm aims for 2.5 compatability). I'm not spotting a method
- # for setting the saved user id without it, though. :/
-
- majorVersion, minorVersion = sys.version_info[:2]
- canSetSavedUid = majorVersion >= 3 or (majorVersion == 2 and minorVersion >= 7)
-
- orig_uid = os.getuid()
- orig_euid = os.geteuid()
-
- # the USER and GROUP must exist on this system
- try:
- dropped_uid = pwd.getpwnam(USER).pw_uid
- dropped_gid = grp.getgrnam(GROUP).gr_gid
- dropped_euid, dropped_egid = dropped_uid, dropped_gid
- except KeyError:
- print "tor-arm user and group was not found, have you run this script with '--init'?"
- exit(1)
-
- # if we're actually root, we skip this group check
- # root can get away with all of this
- if orig_uid != 0:
- # check that the user is in GROUP
- if not dropped_gid in os.getgroups():
- print "Your user needs to be a member of the %s group for this to work" % GROUP
- sys.exit(1)
-
- # drop to the unprivileged group, and lose the rest of the groups
- os.setgid(dropped_gid)
- os.setegid(dropped_egid)
-
- if canSetSavedUid:
- # only usable in python 2.7 or later
- os.setresgid(dropped_gid, dropped_egid, dropped_gid)
- else:
- os.setregid(dropped_gid, dropped_egid)
-
- os.setgroups([dropped_gid])
-
- # make a tempfile and write out the contents
- try:
- tf = tempfile.NamedTemporaryFile(delete=False) # uses mkstemp internally
-
- # allows our child process to write to tf.name (not only if their uid matches, not their gid)
- os.chown(tf.name, dropped_uid, orig_euid)
- except:
- print "We were unable to make a temporary file"
- sys.exit(1)
-
- fork_pid = os.fork()
-
- # open the suspect config after we drop privs
- # we assume the dropped privs are still enough to write to the tf
- if (fork_pid == 0):
- signal.signal(signal.SIGCHLD, signal.SIG_IGN)
-
- # Drop privs forever in the child process
- # I believe this drops os.setfsuid os.setfsgid stuff
- # Clear all other supplemental groups for dropped_uid
- os.setgroups([dropped_gid])
-
- if canSetSavedUid:
- # only usable in python 2.7 or later
- os.setresgid(dropped_gid, dropped_egid, dropped_gid)
- os.setresuid(dropped_uid, dropped_euid, dropped_uid)
- else:
- os.setregid(dropped_gid, dropped_egid)
- os.setreuid(dropped_uid, dropped_euid)
-
- os.setgid(dropped_gid)
- os.setegid(dropped_egid)
- os.setuid(dropped_uid)
- os.seteuid(dropped_euid)
-
- try:
- af = open(ARM_CONFIG_FILE) # this is totally unpriv'ed
-
- # ensure that the fd we opened has the properties we requrie
- configStat = os.fstat(af.fileno()) # this happens on the unpriv'ed FD
- if configStat.st_gid != dropped_gid:
- print "Arm's configuration file (%s) must be owned by the group %s" % (ARM_CONFIG_FILE, GROUP)
- sys.exit(1)
-
- # if everything checks out, we're as safe as we're going to get
- armConfig = af.read(1024 * 1024) # limited read but not too limited
- af.close()
- tf.file.write(armConfig)
- tf.flush()
- except:
- print "Unable to open the arm config as unpriv'ed user"
- sys.exit(1)
- finally:
- tf.close()
- sys.exit(0)
- else:
- # If we're here, we're in the parent waiting for the child's death
- # man, unix is really weird...
- _, status = os.waitpid(fork_pid, 0)
-
- if status != 0 or not os.path.exists(tf.name):
- print "The child seems to have failed; exiting!"
- tf.close()
- sys.exit(1)
-
- # attempt to verify that the config is OK
- if RUN_VERIFY:
- # raise privilages to drop them with 'su'
- os.setuid(0)
- os.seteuid(0)
- os.setgid(0)
- os.setegid(0)
-
- # drop privilages and exec tor to verify it as the dropped_uid
- print "Using Tor to verify that arm will not break Tor's config:"
- verifyCmd = "su -c 'tor --verify-config -f %s' %s" % (tf.name, USER)
- success = os.system(verifyCmd)
-
- if success != 0:
- print "Tor says the new configuration file is invalid: %s (%s)" % (ARM_CONFIG_FILE, tf.name)
- sys.exit(1)
-
- # validates that the torrc matches what the wizard could produce
- torrcFile = open(tf.name)
- torrcContents = torrcFile.readlines()
- torrcFile.close()
-
- if not isWizardGenerated(torrcContents):
- print "torrc doesn't match what we'd expect from the setup wizard"
- sys.exit(1)
-
- # backup the previous tor config
- if os.path.exists(TOR_CONFIG_FILE):
- try:
- backupFilename = "%s_backup_%i" % (TOR_CONFIG_FILE, int(time.time()))
- shutil.copy(TOR_CONFIG_FILE, backupFilename)
- except IOError, exc:
- print "Unable to backup %s (%s)" % (TOR_CONFIG_FILE, exc)
- sys.exit(1)
-
- # overwrites TOR_CONFIG_FILE with ARM_CONFIG_FILE as loaded into tf.name
- try:
- shutil.copy(tf.name, TOR_CONFIG_FILE)
- print "Successfully reconfigured Tor"
- except IOError, exc:
- print "Unable to copy %s to %s (%s)" % (tf.name, TOR_CONFIG_FILE, exc)
- sys.exit(1)
-
- # unlink our temp file
- try:
- os.remove(tf.name)
- except:
- print "Unable to close temp file %s" % tf.name
- sys.exit(1)
-
- sys.exit(0)
-
-def isWizardGenerated(torrcLines):
- """
- True if the given torrc contents could be generated by the wizard, false
- otherwise. This just checks the basic format and options used, not the
- validity of most generated values so this could still be rejected by tor.
- """
-
- wizardPatterns = [re.compile(opt) for opt in WIZARD_OPT]
-
- for line in torrcLines:
- commentStart = line.find("#")
- if commentStart != -1: line = line[:commentStart]
-
- line = line.strip()
- if not line: continue # blank line
-
- isRecognized = False
- for pattern in wizardPatterns:
- if pattern.match(line):
- isRecognized = True
- break
-
- if not isRecognized:
- print "Unrecognized torrc contents: %s" % line
- return False
-
- return True
-
-if __name__ == "__main__":
- # sanity check that we're on linux
- if os.name != "posix":
- print "This is a script specifically for configuring Linux"
- sys.exit(1)
-
- if len(sys.argv) == 3 and sys.argv[1] == "--validate":
- torrcFile = open(sys.argv[2])
- torrcContents = torrcFile.readlines()
- torrcFile.close()
-
- isValid = isWizardGenerated(torrcContents)
- if isValid: print "torrc validated"
- else: print "torrc invalid"
-
- sys.exit(0)
-
- # check that we're running effectively as root
- if os.geteuid() != 0:
- print "This script needs to be run as root"
- sys.exit(1)
-
- if len(sys.argv) < 2:
- replaceTorrc()
- elif len(sys.argv) == 2 and sys.argv[1] == "--init":
- init()
- elif len(sys.argv) == 2 and sys.argv[1] == "--remove":
- remove()
- else:
- print HELP_MSG
- sys.exit(1)
-
diff --git a/src/resources/torrcTemplate.txt b/src/resources/torrcTemplate.txt
deleted file mode 100644
index c6e61f0..0000000
--- a/src/resources/torrcTemplate.txt
+++ /dev/null
@@ -1,90 +0,0 @@
-# This is a tor configuration made by arm. To change the configuration by hand
-# edit this file then either...
-# - tell arm to reset tor by pressing 'x'
-# - run 'pkill -sighup tor'
-# - or restart tor
-#
-# Descriptions of all of these configuration attributes (and many more) are
-# available in the tor man page.
-
-[IF SHUTDOWN]
- [NEWLINE]
- # The following flag tells arm to shut down tor when it quits.
- # ARM_SHUTDOWN
-[END IF]
-
-[NEWLINE]
-DataDirectory [DATA_DIR] # location to store runtime data
-Log [LOG_ENTRY] # location to log notices, warnings, and errors
-ControlPort 9052 # port controllers can connect to
-CookieAuthentication 1 # method for controller authentication
-
-[IF RELAY | EXIT | BRIDGE]
- RunAsDaemon 1 # runs as a background process
-
- [IF LOWPORTS | STARTUP]
- User [USERNAME] # lowers our permissions to this user
- [END IF]
-[END IF]
-[NEWLINE]
-
-[IF RELAY | EXIT | BRIDGE]
- [IF LOWPORTS]
- ORPort 443 # port used for relaying traffic
- [ELSE]
- ORPort 9001 # port used for relaying traffic
- [END IF]
-
- [IF RELAY | EXIT]
- [IF LOWPORTS]
- DirPort 80 # port used for mirroring directory information
- [ELSE]
- DirPort 9030 # port used for mirroring directory information
- [END IF]
-
- Nickname [NICKNAME] # name for this relay
- ContactInfo [CONTACT] # contact information in case there's an issue
- [END IF]
-
- [IF BRIDGE]
- BridgeRelay 1 # makes us a bridge rather than a public relay
-
- [IF NOT DISTRIBUTE]
- PublishServerDescriptor 0 # keeps our bridge descriptor private
- [END IF]
- [END IF]
-
- RelayBandwidthRate [BANDWIDTH] # limit for the bandwidth we'll use to relay
- RelayBandwidthBurst [BURST] # maximum rate when relaying bursts of traffic
- AccountingMax [LIMIT] # maximum amount of traffic we'll relay per month
-
- [IF NOT CLIENT]
- SocksPort 0 # prevents tor from being used as a client
- [END IF]
-
- [IF PORTFORWARD]
- PortForwarding 1 # negotiates UPnP and NAT-PMP if needed
- [END IF]
-
- [IF RELAY | BRIDGE]
- ExitPolicy reject *:* # prevents us from connecting to non-relays
- [ELSE]
- [IF NOTICE]
- DirPortFrontPage [NOTICE_PATH] # disclaimer saying that this is an exit
- [END IF]
-
- [NEWLINE]
- [EXIT_POLICY]
- ExitPolicy reject *:* # prevents any exit traffic not permitted above
- [END IF]
-[ELSE]
- ClientOnly 1 # prevents us from ever being used as a relay
- MaxCircuitDirtiness [REUSE] # duration that circuits can be reused
-
- [IF BRIDGED]
- UseBridges 1 # uses the following bridges to connect
- [BRIDGES]
- [END IF]
-[END IF]
-[NEWLINE]
-
diff --git a/src/settings.cfg b/src/settings.cfg
index 606ae43..8cd92c1 100644
--- a/src/settings.cfg
+++ b/src/settings.cfg
@@ -351,136 +351,6 @@ msg.ARM_DEBUG GETINFO traffic/written
msg.ARM_DEBUG GETCONF
msg.ARM_DEBUG Unable to query process resource usage from ps
-# configuration option attributes used in the relay setup wizard
-wizard.message.role Welcome to the Tor network! This will step you through the configuration process for becoming a part of it. To start with, what role would you like to have?
-wizard.message.relay Internal relays provide connections within the Tor network. Since you will only be connecting to Tor users and relays this is an easy, hassle free way of helping to make the network better.
-wizard.message.exit Exits connect between the Tor network and the outside Internet. This is the most vitally important role you can take, but it also needs some forethought. Please read 'http://www.atagar.com/torExitTips/' before proceeding further to avoid any nasty surprises!
-wizard.message.bridge Bridges are non-public relays used as stepping stones for censored users for accessing the Tor network.
-wizard.message.client This will make use of the Tor network without contributing to it. For easy, pre-configured setups that will help you to use Tor safely see 'http://www.atagar.com/torUsageTips/'.
-
-wizard.toggle Notify => Yes, No
-wizard.toggle Client => Enabled, Disabled
-wizard.toggle Lowports => Yes, No
-wizard.toggle Portforward => Enabled, Disabled
-wizard.toggle Startup => Yes, No
-wizard.toggle Rshutdown => Yes, No
-wizard.toggle Cshutdown => Yes, No
-wizard.toggle System => Yes, No
-wizard.toggle Notice => Yes, No
-wizard.toggle Policy => Custom, Default
-wizard.toggle Websites => Allow, Block
-wizard.toggle Email => Allow, Block
-wizard.toggle Im => Allow, Block
-wizard.toggle Misc => Allow, Block
-wizard.toggle Plaintext => Allow, Block
-wizard.toggle Distribute => Automated, Manual
-wizard.toggle Bridged => Yes, No
-
-# the following options haven't been implemented yet
-wizard.disabled Notify
-wizard.disabled Startup
-
-wizard.suboptions Websites
-wizard.suboptions Email
-wizard.suboptions Im
-wizard.suboptions Misc
-wizard.suboptions Plaintext
-wizard.suboptions Bridge1
-wizard.suboptions Bridge2
-wizard.suboptions Bridge3
-
-wizard.default Control => 9052
-wizard.default Notify => true
-wizard.default Bandwidth => 5 MB/s
-wizard.default Startup => true
-wizard.default Rshutdown => false
-wizard.default Cshutdown => true
-wizard.default System => true
-wizard.default Client => false
-wizard.default Lowports => true
-wizard.default Portforward => true
-wizard.default Notice => true
-wizard.default Policy => false
-wizard.default Websites => true
-wizard.default Email => true
-wizard.default Im => true
-wizard.default Misc => true
-wizard.default Plaintext => true
-wizard.default Distribute => true
-wizard.default Bridged => false
-wizard.default Reuse => 10 minutes
-
-wizard.blankValue Nickname => Unnamed
-wizard.blankValue Bridge1 => <ip address>:<port>
-wizard.blankValue Bridge2 => <ip address>:<port>
-wizard.blankValue Bridge3 => <ip address>:<port>
-
-wizard.label.general Cancel => Cancel
-wizard.label.general Back => Previous
-wizard.label.general Next => Next
-wizard.label.role Resume => Use Last Configuration
-wizard.label.role Relay => Internal Relay
-wizard.label.role Exit => Exit Relay
-wizard.label.role Bridge => Bridge
-wizard.label.role Client => Client
-wizard.label.opt Nickname => Nickname
-wizard.label.opt Contact => Contact Information
-wizard.label.opt Notify => Issue Notification
-wizard.label.opt Bandwidth => Relay Speed
-wizard.label.opt Limit => Monthly Limit
-wizard.label.opt Client => Client Usage
-wizard.label.opt Lowports => Use Popular Ports
-wizard.label.opt Portforward => Port Forwarding
-wizard.label.opt Startup => Run At Startup
-wizard.label.opt Rshutdown => Shutdown With Arm
-wizard.label.opt Cshutdown => Shutdown With Arm
-wizard.label.opt System => Use System Instance
-wizard.label.opt Notice => Disclaimer Notice
-wizard.label.opt Policy => Exit Policy
-wizard.label.opt Websites => Web Browsing
-wizard.label.opt Email => Receiving Email
-wizard.label.opt Im => Instant Messaging
-wizard.label.opt Misc => Other Services
-wizard.label.opt Plaintext => Unencrypted Traffic
-wizard.label.opt Distribute => Distribution
-wizard.label.opt Bridged => Use Bridges
-wizard.label.opt Bridge1 => First Bridge
-wizard.label.opt Bridge2 => Second Bridge
-wizard.label.opt Bridge3 => Third Bridge
-wizard.label.opt Reuse => Circuit Duration
-
-wizard.description.general Cancel => Close without starting Tor.
-wizard.description.role Resume => Start Tor with the last configuration you made.
-wizard.description.role Relay => Provides interconnections with other Tor relays. This is a safe and easy way of making the network better.
-wizard.description.role Exit => Connects between Tor network and the outside Internet. This is a vital role, but can lead to abuse complaints.
-wizard.description.role Bridge => Non-public relay specifically for helping censored users.
-wizard.description.role Client => Use the network without contributing to it.
-wizard.description.opt Nickname => Human friendly name for your relay. If this is unique then it's used instead of your fingerprint (a forty character hex string) when pages like TorStatus refer to you.
-wizard.description.opt Contact => Address we can contact you at if there's a problem with your relay. This is public information so, if it looks like an email address, we'll obscure it a bit.
-wizard.description.opt Notify => Sends automated email notifications to the above address if your relay is unreachable or out of date. This service is provided by Tor Weather (https://weather.torproject.org/) and will send you a confirmation email before it's started.
-wizard.description.opt Bandwidth => Limit for the average rate at which you relay traffic.
-wizard.description.opt Limit => Maximum amount of traffic to relay each month. Some ISPs, like Comcast, cap their customer's Internet usage so this is an easy way of staying below that limit.
-wizard.description.opt Client => Enable this if you would like to use Tor yourself. This opens or closes the SOCKS port used by applications for connecting to Tor.
-wizard.description.opt Lowports => Relays using port 443 rather than 9001. This helps some users that would otherwise be blocked, but requires that tor is started with root permissions (after that it lowers itself to those of the current user).
-wizard.description.opt Portforward => If needed, attempts NAT traversal using UPnP and NAT-PMP. This allows for automatic port forwarding on most home routers.
-wizard.description.opt Startup => Runs Tor in the background when the system starts.
-wizard.description.opt Rshutdown => When you quit arm the Tor process is stopped thirty seconds later. This delay is so people using you can gracefully switch their circuits.
-wizard.description.opt Cshutdown => Stops the Tor process when you quit arm.
-wizard.description.opt System => Use the system wide tor instance rather than making one of our own.
-wizard.description.opt Notice => Provides a disclaimer that this is an exit on port 80 (http://www.atagar.com/exitNotice)
-wizard.description.opt Policy => Ports allowed to exit from your relay. The default policy allows for common services while limiting the chance of getting a DMCA takedown for torrent traffic (http://www.atagar.com/exitPolicy)
-wizard.description.opt Websites => General Internet browsing including HTTP (80), HTTPS (443), common alternatives (81, 8008), and proxies (3128, 8080)
-wizard.description.opt Email => Protocols for receiving, but not sending email. This includes POP3 (110), POP3S (995), IMAP (143, 220), and IMAPS (993).
-wizard.description.opt Im => Common instant messaging protocols including Jabber, IRC, ICQ, AIM, Yahoo, MSN, SILC, GroupWise, Gadu-Gadu, Sametime, and Zephyr.
-wizard.description.opt Misc => Protocols from the default policy that aren't among the above.
-wizard.description.opt Plaintext => When blocked the policy will exclude ports that aren't commonly encrypted.
-wizard.description.opt Distribute => If automated then we'll attempt to get your bridge to censored users (email auto-responders, activist networks, etc). Otherwise it'll be up to you to distribute the bridge address to people who need it.
-wizard.description.opt Bridged => If you're being blocked from Tor then bridges provide a stepping stone you can use to connect. To get bridges visit 'https://bridges.torproject.org/' and enter the IP/port into the following entries (for instance "141.201.27.48:443").
-wizard.description.opt Bridge1 => Bridge used to connect to the Tor network.
-wizard.description.opt Bridge2 => Fallback bridge used for connecting if the first is unavailable.
-wizard.description.opt Bridge3 => Fallback bridge used for connecting if neither of the first two are available.
-wizard.description.opt Reuse => Duration that circuits will be reused before replacements are made for new connections. It's good to periodically change the route you use, but making circuits takes a fair bit of work so don't set this to be too low.
-
# valid torrc aliases from the _option_abbrevs struct of src/or/config.c
# These couldn't be requested via GETCONF (in 0.2.1.19), but I think this has
# been fixed. Discussion is in:
@@ -835,108 +705,3 @@ port.label.23399 Skype
port.label.30301 BitTorrent
port.label.33434 traceroute
-# Exit policy categories and attributes used by the relay setup wizard. The
-# full policy is the Reduced Exit Policy, revision 9 (edited 6/28/11):
-# https://trac.torproject.org/projects/tor/wiki/doc/ReducedExitPolicy?version…
-
-port.category all => The following sets which ports can exit the tor network through you. For more information and updates on the suggested policy see: https://trac.torproject.org/projects/tor/wiki/doc/ReducedExitPolicy
-port.category web => ports for general internet browsing
-port.category mail => ports for receiving email
-port.category im => ports for instant messaging
-port.category misc => ports for other services
-
-port.exit.misc 20-23 # FTP, SSH, Telnet
-port.exit.misc 43 # WHOIS
-port.exit.all 53 # DNS
-port.exit.misc 79 # Finger
-port.exit.web 80 # HTTP
-port.exit.web 81 # HTTP alternate?
-port.exit.misc 88 # Kerberos
-port.exit.mail 110 # POP3
-port.exit.mail 143 # IMAP
-port.exit.im 194 # IRC
-port.exit.mail 220 # IMAP3
-port.exit.web 443 # HTTPS
-port.exit.misc 464 # Kerberos
-port.exit.im 531 # AIM/IRC
-port.exit.misc 543-544 # Kerberos
-port.exit.misc 563 # NNTPS
-port.exit.im 706 # SILC
-port.exit.misc 749 # Kerberos
-port.exit.misc 873 # rsync
-port.exit.misc 902-904 # VMware
-port.exit.misc 981 # SofaWare Firewall Administration
-port.exit.misc 989-990 # FTPS
-port.exit.misc 991 # NAS
-port.exit.misc 992 # Telnet
-port.exit.mail 993 # IMAPS
-port.exit.im 994 # IRC over SSL
-port.exit.mail 995 # POP3S
-port.exit.misc 1194 # OpenVPN
-port.exit.misc 1220 # QuickTime
-port.exit.misc 1293 # PKT-KRB-IPSec
-port.exit.misc 1500 # NetGuard GuardianPro Firewall Administration / VLSI License Manager
-port.exit.im 1533 # Sametime
-port.exit.im 1677 # GroupWise
-port.exit.misc 1723 # Microsoft Point-to-Point Tunneling Protocol
-port.exit.misc 1863 # MSNP
-port.exit.misc 2082 # Infowave Mobility Server
-port.exit.misc 2083 # Secure Radius Service
-port.exit.misc 2086 # GNUnet
-port.exit.misc 2087 # Event Logging Integration
-port.exit.misc 2095-2096 # NBX SER / DIR
-port.exit.im 2102-2104 # Zephyr
-port.exit.web 3128 # Squid Proxy
-port.exit.misc 3389 # Windows Based Terminal
-port.exit.misc 3690 # SVN
-port.exit.misc 4321 # RWHOIS
-port.exit.misc 4643 # Virtuozzo Power Panel
-port.exit.im 5050 # Yahoo IM
-port.exit.im 5190 # AIM/ICQ
-port.exit.im 5222 # Jabber
-port.exit.im 5223 # Jabber over SSL
-port.exit.misc 5228 # Android Market
-port.exit.misc 5900 # VNC
-port.exit.im 6660-6669 # IRC
-port.exit.im 6679 # IRC over SSL
-port.exit.im 6697 # IRC over SSL
-port.exit.misc 8000 # Intel Remote Desktop Management Interface
-port.exit.web 8008 # HTTP alternate
-port.exit.im 8074 # Gadu-Gadu
-port.exit.web 8080 # HTTP Proxies
-port.exit.misc 8087 # Simplify Media SPP Protocol
-port.exit.misc 8088 # Radan HTTP
-port.exit.misc 8443 # PCsync HTTPS
-port.exit.misc 8888 # NewsEDGE
-port.exit.misc 9418 # Git
-port.exit.misc 9999 # distinct
-port.exit.misc 10000 # Web-based Linux admin tool
-port.exit.misc 19294 # Google Voice
-port.exit.misc 19638 # Ensim Control Panel
-
-# Commonly encrypted ports (used to allow for policies that only include
-# encrypted traffic)
-
-port.encrypted 22
-port.encrypted 53 # dns - not really encrypted but want it anyway
-port.encrypted 88
-port.encrypted 443
-port.encrypted 464
-port.encrypted 543
-port.encrypted 544
-port.encrypted 563
-port.encrypted 749
-port.encrypted 981
-port.encrypted 989
-port.encrypted 990
-port.encrypted 993
-port.encrypted 995
-port.encrypted 1194
-port.encrypted 1293
-port.encrypted 1723
-port.encrypted 2083
-port.encrypted 5223
-port.encrypted 6679
-port.encrypted 6697
-port.encrypted 8443
-
diff --git a/src/starter.py b/src/starter.py
index 2b5063e..ccf7aad 100644
--- a/src/starter.py
+++ b/src/starter.py
@@ -40,8 +40,7 @@ CONFIG = {"startup.controlPassword": None,
"startup.blindModeEnabled": False,
"startup.events": "N3",
"startup.dataDirectory": "~/.arm",
- "wizard.default": {},
- "features.allowDetachedStartup": True,
+ "features.allowDetachedStartup": False,
"features.config.descriptions.enabled": True,
"features.config.descriptions.persist": True,
"log.configDescriptions.readManPageSuccess": util.log.INFO,
@@ -123,8 +122,7 @@ def allowConnectionTypes():
skipSocketConnection = isPortArgPresent and not isSocketArgPresent
# Flag to indicate if we'll start arm reguardless of being unable to connect
- # to Tor. This is the default behavior if the user hasn't provided a port or
- # socket to connect to, so we can show the relay setup wizard.
+ # to Tor.
allowDetachedStart = CONFIG["features.allowDetachedStartup"] and not isPortArgPresent and not isSocketArgPresent
@@ -227,21 +225,6 @@ def _getController(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
except Exception, exc:
if controller: controller.close()
- # attempts to connect with the default wizard address too
- wizardPort = CONFIG["wizard.default"].get("Control")
-
- if wizardPort and wizardPort.isdigit():
- wizardPort = int(wizardPort)
-
- # Attempt to connect to the wizard port. If the connection fails then
- # don't print anything and continue with the error case for the initial
- # connection failure. Otherwise, return the connection result.
-
- if controlPort != wizardPort:
- controller = _getController(controlAddr, wizardPort)
- if controller != None: return controller
- else: return None # wizard connection attempt, don't print anything
-
if passphrase and str(exc) == "Unable to authenticate: password incorrect":
# provide a warning that the provided password didn't work, then try
# again prompting for the user to enter it
1
0
commit 582bd8e556ea26cc1c7e2ba2aa3a6b8d859fdd2b
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue Dec 18 08:09:27 2012 -0800
Dropping the interpretor panel
Since introducing the interpretor panel to arm the program's been plagued
with stability issues (https://trac.torproject.org/6439) I suspect that the
root cause is related to the readline module somehow being imported, though
I tried to keep that import away from the cli use cases.
The interpretor panel was a sweet idea and will live on as a separate
application. However, as a part of arm it has been surprisingly unused (I've
*NEVER* heard of someone making use of it). Oh well...
---
README | 2 -
armrc.sample | 1 -
src/cli/__init__.py | 2 +-
src/cli/controller.py | 5 -
src/cli/interpretorPanel.py | 137 ------
src/starter.py | 14 +-
src/util/__init__.py | 2 +-
src/util/torInterpretor.py | 1065 -------------------------------------------
8 files changed, 6 insertions(+), 1222 deletions(-)
diff --git a/README b/README
index e5676f7..4d0b4c1 100644
--- a/README
+++ b/README
@@ -206,7 +206,6 @@ Layout:
logPanel.py - (page 1) displays tor, arm, and stem events
configPanel.py - (page 3) editor panel for the tor configuration
torrcPanel.py - (page 4) displays torrc and validation
- interpretorPanel.py - (page 5) interpretor for control port access
util/
__init__.py
@@ -221,7 +220,6 @@ Layout:
sysTools.py - helper for system calls, providing client side caching
textInput.py - expands the capabilities of text input fields
torConfig.py - functions for working with the torrc and config options
- torInterpretor.py - provides a shell around raw control port access
torTools.py - Stem wrapper, providing caching and derived information
uiTools.py - helper functions for presenting the user interface
diff --git a/armrc.sample b/armrc.sample
index 5fb9564..458fcbe 100644
--- a/armrc.sample
+++ b/armrc.sample
@@ -18,7 +18,6 @@ features.panels.show.log true
features.panels.show.connection true
features.panels.show.config true
features.panels.show.torrc true
-features.panels.show.interpretor true
# Read the proc contents directly instead of calling ps, netstat, and other
# resolvers. This provides very sizable performance benefits (around 90%
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index fa23640..b0d6bd8 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "controller", "headerPanel", "interpretorPanel", "logPanel", "popups", "torrcPanel", "wizard"]
+__all__ = ["configPanel", "controller", "headerPanel", "logPanel", "popups", "torrcPanel", "wizard"]
diff --git a/src/cli/controller.py b/src/cli/controller.py
index de77e5a..1f2bf2b 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -15,7 +15,6 @@ import cli.headerPanel
import cli.logPanel
import cli.configPanel
import cli.torrcPanel
-import cli.interpretorPanel
import cli.graphing.graphPanel
import cli.graphing.bandwidthStats
import cli.graphing.connStats
@@ -37,7 +36,6 @@ CONFIG = {"startup.events": "N3",
"features.panels.show.connection": True,
"features.panels.show.config": True,
"features.panels.show.torrc": True,
- "features.panels.show.interpretor": True,
"features.redrawRate": 5,
"features.refreshRate": 5,
"features.confirmQuit": True,
@@ -99,9 +97,6 @@ def initController(stdscr, startTime):
if CONFIG["features.panels.show.torrc"]:
pagePanels.append([cli.torrcPanel.TorrcPanel(stdscr, cli.torrcPanel.Config.TORRC, config)])
- if CONFIG["features.panels.show.interpretor"]:
- pagePanels.append([cli.interpretorPanel.InterpretorPanel(stdscr)])
-
# initializes the controller
ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels)
diff --git a/src/cli/interpretorPanel.py b/src/cli/interpretorPanel.py
deleted file mode 100644
index 246636c..0000000
--- a/src/cli/interpretorPanel.py
+++ /dev/null
@@ -1,137 +0,0 @@
-"""
-Panel providing raw control port access with syntax hilighting, usage
-information, tab completion, and other usability features.
-"""
-
-import curses
-
-from util import panel, textInput, torInterpretor, torTools, uiTools
-
-USAGE_INFO = "to use this panel press enter"
-PROMPT_LINE = [torInterpretor.PROMPT, (USAGE_INFO, torInterpretor.USAGE_FORMAT)]
-
-# lazy loaded mapping of interpretor attributes to curses formatting constants
-FORMATS = {}
-
-def getFormat(formatAttr):
- """
- Provides the curses drawing attributes for torInterpretor formats.
-
- Arguments:
- formatAttr - list of formatting attributes
- """
-
- # initializes formats if they haven't yet been loaded
- if not FORMATS:
- for colorEnum in torInterpretor.Color.values():
- FORMATS[colorEnum] = uiTools.getColor(colorEnum.lower())
-
- FORMATS[torInterpretor.Attr.BOLD] = curses.A_BOLD
- FORMATS[torInterpretor.Attr.UNDERLINE] = curses.A_UNDERLINE
- FORMATS[torInterpretor.Attr.HILIGHT] = curses.A_STANDOUT
-
- cursesFormatting = curses.A_NORMAL
-
- for attr in formatAttr:
- cursesFormatting |= FORMATS.get(attr, curses.A_NORMAL)
-
- return cursesFormatting
-
-class InterpretorPanel(panel.Panel):
- def __init__(self, stdscr):
- panel.Panel.__init__(self, stdscr, "interpretor", 0)
- self.interpretor = torInterpretor.ControlInterpretor()
- self.inputCompleter = torInterpretor.TorControlCompleter()
- self.isInputMode = False
- self.scroll = 0
-
- def prompt(self):
- """
- Enables the interpretor, prompting for input until the user enters esc or
- a blank line.
- """
-
- self.isInputMode = True
- panel.CURSES_LOCK.acquire()
-
- while self.isInputMode:
- self.redraw(True)
-
- # intercepts input so user can cycle through the history
- validator = textInput.BasicValidator()
- validator = textInput.HistoryValidator(list(reversed(self.interpretor.getBacklog())), validator)
- validator = textInput.TabCompleter(self.inputCompleter.getMatches, validator)
-
- xOffset = len(torInterpretor.PROMPT[0])
- displayLength = len(self.interpretor.getDisplayContents(PROMPT_LINE))
- if displayLength > self.maxY - 1:
- xOffset += 3 # offset for scrollbar
-
- inputLine = min(self.maxY - 1, displayLength)
- inputFormat = getFormat(torInterpretor.INPUT_FORMAT)
- input = self.getstr(inputLine, xOffset, "", inputFormat, validator = validator)
- if input == None: input = ""
- input, isDone = input.strip(), False
-
- if not input:
- # terminate input when we get a blank line
- isDone = True
- else:
- try:
- self.interpretor.handleQuery(input)
- except torInterpretor.InterpretorClosed:
- # Makes our control connection check if its been closed or not
- torTools.getConn().isAlive()
-
- isDone = True
-
- if isDone:
- self.isInputMode = False
- self.redraw(True)
-
- panel.CURSES_LOCK.release()
-
- def handleKey(self, key):
- isKeystrokeConsumed = True
- if uiTools.isSelectionKey(key):
- self.prompt()
- elif uiTools.isScrollKey(key) and not self.isInputMode:
- pageHeight = self.getPreferredSize()[0] - 1
- displayLength = len(self.interpretor.getDisplayContents(PROMPT_LINE))
- newScroll = uiTools.getScrollPosition(key, self.scroll, pageHeight, displayLength)
-
- if self.scroll != newScroll:
- self.scroll = newScroll
- self.redraw(True)
- else: isKeystrokeConsumed = False
-
- return isKeystrokeConsumed
-
- def draw(self, width, height):
- # page title
- usageMsg = " (enter \"/help\" for usage or a blank line to stop)" if self.isInputMode else ""
- self.addstr(0, 0, "Control Interpretor%s:" % usageMsg, curses.A_STANDOUT)
-
- xOffset = 0
- displayContents = self.interpretor.getDisplayContents(PROMPT_LINE)
- if len(displayContents) > height - 1:
- # if we're in input mode then make sure the last line is visible
- if self.isInputMode:
- self.scroll = len(displayContents) - height + 1
-
- xOffset = 3
- self.addScrollBar(self.scroll, self.scroll + height - 1, len(displayContents), 1)
-
- # draws prior commands and output
- drawLine = 1
- for entry in displayContents[self.scroll:]:
- cursor = xOffset
-
- for msg, formatEntry in entry:
- format = getFormat(formatEntry)
- self.addstr(drawLine, cursor, msg, format)
- cursor += len(msg)
-
- drawLine += 1
- if drawLine >= height: break
-
diff --git a/src/starter.py b/src/starter.py
index 01a6086..2b5063e 100644
--- a/src/starter.py
+++ b/src/starter.py
@@ -25,7 +25,6 @@ import util.panel
import util.procTools
import util.sysTools
import util.torConfig
-import util.torInterpretor
import util.torTools
import util.uiTools
@@ -55,8 +54,8 @@ CONFIG = {"startup.controlPassword": None,
"log.configDescriptions.persistance.saveFailed": util.log.NOTICE,
"log.savingDebugLog": util.log.NOTICE}
-OPT = "gpi:s:c:dbe:vh"
-OPT_EXPANDED = ["prompt", "interface=", "socket=", "config=", "debug", "blind", "event=", "version", "help"]
+OPT = "gi:s:c:dbe:vh"
+OPT_EXPANDED = ["interface=", "socket=", "config=", "debug", "blind", "event=", "version", "help"]
HELP_MSG = """Usage arm [OPTION]
Terminal status monitor for Tor relays.
@@ -298,7 +297,6 @@ def _dumpConfig():
if __name__ == '__main__':
startTime = time.time()
param = dict([(key, None) for key in CONFIG.keys()])
- launchPrompt = False
isDebugMode = False
configPath = DEFAULT_CONFIG # path used for customized configuration
@@ -329,7 +327,6 @@ if __name__ == '__main__':
param["startup.interface.port"] = controlPort
elif opt in ("-s", "--socket"):
param["startup.interface.socket"] = arg
- elif opt in ("-p", "--prompt"): launchPrompt = True
elif opt in ("-c", "--config"): configPath = arg # sets path of user's config
elif opt in ("-d", "--debug"): isDebugMode = True # dumps all logs
elif opt in ("-b", "--blind"):
@@ -388,7 +385,7 @@ if __name__ == '__main__':
# - tor is running (otherwise it would be kinda confusing, "tor is running
# but why does arm say that it's shut down?")
- if launchPrompt or util.torTools.isTorRunning():
+ if util.torTools.isTorRunning():
config.set("features.allowDetachedStartup", "false")
# revises defaults to match user's configuration
@@ -514,8 +511,5 @@ if __name__ == '__main__':
if util.uiTools.isUnicodeAvailable():
locale.setlocale(locale.LC_ALL, "")
- if launchPrompt:
- util.torInterpretor.showPrompt()
- else:
- cli.controller.startTorMonitor(time.time() - initTime)
+ cli.controller.startTorMonitor(time.time() - initTime)
diff --git a/src/util/__init__.py b/src/util/__init__.py
index 2b8f928..973527e 100644
--- a/src/util/__init__.py
+++ b/src/util/__init__.py
@@ -4,5 +4,5 @@ application's status, making cross platform system calls, parsing tor data,
and safely working with curses (hiding some of the gory details).
"""
-__all__ = ["conf", "connections", "enum", "gtkTools", "hostnames", "log", "panel", "procTools", "procName", "sysTools", "textInput", "torConfig", "torInterpretor", "torTools", "uiTools"]
+__all__ = ["conf", "connections", "enum", "gtkTools", "hostnames", "log", "panel", "procTools", "procName", "sysTools", "textInput", "torConfig", "torTools", "uiTools"]
diff --git a/src/util/torInterpretor.py b/src/util/torInterpretor.py
deleted file mode 100644
index 5023045..0000000
--- a/src/util/torInterpretor.py
+++ /dev/null
@@ -1,1065 +0,0 @@
-"""
-Provides an interactive interpretor for working with the Tor control port. This
-adds usability features like IRC style interpretor commands and, when ran
-directly, history and tab completion.
-"""
-
-import re
-import sys
-
-import version
-
-import stem
-
-from util import connections, enum, hostnames, torConfig, torTools, uiTools
-
-COLOR_PROMPT = True # provides a colored interpretor prompt
-INFO_HOSTNAMES = False # hostname lookups in /info results
-
-# initial location /write will save to when no path is specified
-DEFAULT_WRITE_PATH = "/tmp/torInterpretor_output"
-
-INIT_MSG = """Arm %s Control Interpretor
-Enter \"/help\" for usage information and \"/quit\" to stop.
-""" % version.VERSION
-
-TERM_COLORS = ("BLACK", "RED", "GREEN", "YELLOW", "BLUE", "MAGENTA", "CYAN", "WHITE")
-
-Color = enum.Enum(*TERM_COLORS)
-BgColor = enum.Enum(*["BG_" + color for color in TERM_COLORS])
-Attr = enum.Enum("BOLD", "UNDERLINE", "HILIGHT")
-
-FG_ENCODING = dict([(Color.values()[i], str(30 + i)) for i in range(8)])
-BG_ENCODING = dict([(BgColor.values()[i], str(40 + i)) for i in range(8)])
-ATTR_ENCODING = {Attr.BOLD: "1", Attr.UNDERLINE: "4", Attr.HILIGHT: "7"}
-
-PROMPT = (">>> ", (Attr.BOLD, Color.GREEN))
-INPUT_FORMAT = (Color.CYAN, )
-INPUT_INTERPRETOR_FORMAT = (Attr.BOLD, Color.MAGENTA)
-INPUT_CMD_FORMAT = (Attr.BOLD, Color.GREEN)
-INPUT_ARG_FORMAT = (Attr.BOLD, Color.CYAN)
-OUTPUT_FORMAT = (Color.BLUE, )
-USAGE_FORMAT = (Color.CYAN, )
-HELP_FORMAT = (Color.MAGENTA, )
-ERROR_FORMAT = (Attr.BOLD, Color.RED)
-
-CSI = "\x1B[%sm"
-RESET = CSI % "0"
-
-# limits used for cropping
-BACKLOG_LIMIT = 100
-EVENTS_LIMIT = 200
-CONTENT_LIMIT = 20000
-
-MULTILINE_UNIMPLEMENTED_NOTICE = "Multi-line control options like this are not yet implemented."
-
-GENERAL_HELP = """Interpretor commands include:
- /help - provides information for interpretor and tor commands/config options
- /info - general information for a relay
- /find - searches backlog for lines with the given regex
- /events - prints events that we've received
- /write - saves backlog to a given location
- /quit - shuts down the interpretor
-
-Tor commands include:
- GETINFO - queries information from tor
- GETCONF, SETCONF, RESETCONF - show or edit a configuration option
- SIGNAL - issues control signal to the process (for resetting, stopping, etc)
- SETEVENTS - configures the events tor will notify us of
-
- USEFEATURE - enables custom behavior for the controller
- SAVECONF - writes tor's current configuration to our torrc
- LOADCONF - loads the given input like it was part of our torrc
- MAPADDRESS - replaces requests for one address with another
- POSTDESCRIPTOR - adds a relay descriptor to our cache
- EXTENDCIRCUIT - create or extend a tor circuit
- SETCIRCUITPURPOSE - configures the purpose associated with a circuit
- CLOSECIRCUIT - closes the given circuit
- ATTACHSTREAM - associates an application's stream with a tor circuit
- REDIRECTSTREAM - sets a stream's destination
- CLOSESTREAM - closes the given stream
- RESOLVE - issues an asynchronous dns or rdns request over tor
- TAKEOWNERSHIP - instructs tor to quit when this control connection is closed
- PROTOCOLINFO - queries version and controller authentication information
- QUIT - disconnect the control connection
-
-For more information use '/help [OPTION]'."""
-
-HELP_HELP = """Provides usage information for the given interpretor, tor command, or tor
-configuration option.
-
-Example:
- /help info # provides a description of the '/info' option
- /help GETINFO # usage information for tor's GETINFO controller option
- /help ExitPolicy # description of tor's ExitPolicy configuration option"""
-
-HELP_WRITE = """Writes the interpretor's backlog to the given path. If no location is
-specified then this saves to the last path specified (initially '%s').""" % DEFAULT_WRITE_PATH
-
-HELP_EVENTS = """Provides events that we've received belonging to the given event types. If
-no types are specified then this provides all the messages that we've
-received."""
-
-HELP_INFO = """Provides general information for a relay that's currently in the consensus.
-If no relay is specified then this provides information on ourselves."""
-
-HELP_FIND = """Searches the backlog for lines matching a given regular expression pattern.
-Results are deduplicated and the matching portions bolded."""
-
-HELP_QUIT = """Terminates the interpretor."""
-
-HELP_GETINFO = """Queries the tor process for information. Options are...
-"""
-
-HELP_GETCONF = """Provides the current value for a given configuration value. Options include...
-"""
-
-HELP_SETCONF = """Sets the given configuration parameters. Values can be quoted or non-quoted
-strings, and reverts the option to 0 or NULL if not provided.
-
-Examples:
- * Sets a contact address and resets our family to NULL
- SETCONF MyFamily ContactInfo=foo(a)bar.com
-
- * Sets an exit policy that only includes port 80/443
- SETCONF ExitPolicy=\"accept *:80, accept *:443, reject *:*\""""
-
-HELP_RESETCONF = """Reverts the given configuration options to their default values. If a value
-is provided then this behaves in the same way as SETCONF.
-
-Examples:
- * Returns both of our accounting parameters to their defaults
- RESETCONF AccountingMax AccountingStart
-
- * Uses the default exit policy and sets our nickname to be 'Goomba'
- RESETCONF ExitPolicy Nickname=Goomba"""
-
-HELP_SIGNAL = """Issues a signal that tells the tor process to reload its torrc, dump its
-stats, halt, etc.
-"""
-
-SIGNAL_DESCRIPTIONS = (
- ("RELOAD / HUP", "reload our torrc"),
- ("SHUTDOWN / INT", "gracefully shut down, waiting 30 seconds if we're a relay"),
- ("DUMP / USR1", "logs information about open connections and circuits"),
- ("DEBUG / USR2", "makes us log at the DEBUG runlevel"),
- ("HALT / TERM", "immediately shut down"),
- ("CLEARDNSCACHE", "clears any cached DNS results"),
- ("NEWNYM", "clears the DNS cache and uses new circuits for future connections")
-)
-
-HELP_SETEVENTS = """Sets the events that we will receive. This turns off any events that aren't
-listed so sending 'SETEVENTS' without any values will turn off all event reporting.
-
-For Tor versions between 0.1.1.9 and 0.2.2.1 adding 'EXTENDED' causes some
-events to give us additional information. After version 0.2.2.1 this is
-always on.
-
-Events include...
-"""
-
-HELP_USEFEATURE = """Customizes the behavior of the control port. Options include...
-"""
-
-HELP_SAVECONF = """Writes Tor's current configuration to its torrc."""
-
-HELP_LOADCONF = """Reads the given text like it belonged to our torrc.
-
-Example:
- +LOADCONF
- # sets our exit policy to just accept ports 80 and 443
- ExitPolicy accept *:80
- ExitPolicy accept *:443
- ExitPolicy reject *:*
- ."""
-
-HELP_MAPADDRESS = """Replaces future requests for one address with another.
-
-Example:
- MAPADDRESS 0.0.0.0=torproject.org 1.2.3.4=tor.freehaven.net"""
-
-HELP_POSTDESCRIPTOR = """Simulates getting a new relay descriptor."""
-
-HELP_EXTENDCIRCUIT = """Extends the given circuit or create a new one if the CircuitID is zero. The
-PATH is a comma separated list of fingerprints. If it isn't set then this
-uses Tor's normal path selection."""
-
-HELP_SETCIRCUITPURPOSE = """Sets the purpose attribute for a circuit."""
-
-HELP_CLOSECIRCUIT = """Closes the given circuit. If "IfUnused" is included then this only closes
-the circuit if it isn't currently being used."""
-
-HELP_ATTACHSTREAM = """Attaches a stream with the given built circuit (tor picks one on its own if
-CircuitID is zero). If HopNum is given then this hop is used to exit the
-circuit, otherwise the last relay is used."""
-
-HELP_REDIRECTSTREAM = """Sets the destination for a given stream. This can only be done after a
-stream is created but before it's attached to a circuit."""
-
-HELP_CLOSESTREAM = """Closes the given stream, the reason being an integer matching a reason as
-per section 6.3 of the tor-spec."""
-
-HELP_RESOLVE = """Performs IPv4 DNS resolution over tor, doing a reverse lookup instead if
-"mode=reverse" is included. This request is processed in the background and
-results in a ADDRMAP event with the response."""
-
-HELP_TAKEOWNERSHIP = """Instructs Tor to gracefully shut down when this control connection is closed."""
-
-HELP_PROTOCOLINFO = """Provides bootstrapping information that a controller might need when first
-starting, like Tor's version and controller authentication. This can be done
-before authenticating to the control port."""
-
-HELP_OPTIONS = {
- "HELP": ("/help [OPTION]", HELP_HELP),
- "WRITE": ("/write [PATH]", HELP_WRITE),
- "EVENTS": ("/events [types]", HELP_EVENTS),
- "INFO": ("/info [relay fingerprint, nickname, or IP address]", HELP_INFO),
- "FIND": ("/find PATTERN", HELP_FIND),
- "QUIT": ("/quit", HELP_QUIT),
- "GETINFO": ("GETINFO OPTION", HELP_GETINFO),
- "GETCONF": ("GETCONF OPTION", HELP_GETCONF),
- "SETCONF": ("SETCONF PARAM[=VALUE]", HELP_SETCONF),
- "RESETCONF": ("RESETCONF PARAM[=VALUE]", HELP_RESETCONF),
- "SIGNAL": ("SIGNAL SIG", HELP_SIGNAL),
- "SETEVENTS": ("SETEVENTS [EXTENDED] [EVENTS]", HELP_SETEVENTS),
- "USEFEATURE": ("USEFEATURE OPTION", HELP_USEFEATURE),
- "SAVECONF": ("SAVECONF", HELP_SAVECONF),
- "LOADCONF": ("LOADCONF...", HELP_LOADCONF),
- "MAPADDRESS": ("MAPADDRESS SOURCE_ADDR=DESTINATION_ADDR", HELP_MAPADDRESS),
- "POSTDESCRIPTOR": ("POSTDESCRIPTOR [purpose=general/controller/bridge] [cache=yes/no]...", HELP_POSTDESCRIPTOR),
- "EXTENDCIRCUIT": ("EXTENDCIRCUIT CircuitID [PATH] [purpose=general/controller]", HELP_EXTENDCIRCUIT),
- "SETCIRCUITPURPOSE": ("SETCIRCUITPURPOSE CircuitID purpose=general/controller", HELP_SETCIRCUITPURPOSE),
- "CLOSECIRCUIT": ("CLOSECIRCUIT CircuitID [IfUnused]", HELP_CLOSECIRCUIT),
- "ATTACHSTREAM": ("ATTACHSTREAM StreamID CircuitID [HOP=HopNum]", HELP_ATTACHSTREAM),
- "REDIRECTSTREAM": ("REDIRECTSTREAM StreamID Address [Port]", HELP_REDIRECTSTREAM),
- "CLOSESTREAM": ("CLOSESTREAM StreamID Reason [Flag]", HELP_CLOSESTREAM),
- "RESOLVE": ("RESOLVE [mode=reverse] address", HELP_RESOLVE),
- "TAKEOWNERSHIP": ("TAKEOWNERSHIP", HELP_TAKEOWNERSHIP),
- "PROTOCOLINFO": ("PROTOCOLINFO [ProtocolVersion]", HELP_PROTOCOLINFO),
-}
-
-class InterpretorClosed(Exception):
- """
- Exception raised when the interpretor should be shut down.
- """
-
- pass
-
-def format(msg, *attr):
- """
- Simple terminal text formatting, using ANSI escape sequences from:
- https://secure.wikimedia.org/wikipedia/en/wiki/ANSI_escape_code#CSI_codes
-
- toolkits providing similar capabilities:
- * django.utils.termcolors
- https://code.djangoproject.com/browser/django/trunk/django/utils/termcolors…
-
- * termcolor
- http://pypi.python.org/pypi/termcolor
-
- * colorama
- http://pypi.python.org/pypi/colorama
-
- Arguments:
- msg - string to be formatted
- attr - text attributes, this can be Color, BgColor, or Attr enums and are
- case insensitive (so strings like "red" are fine)
- """
-
- encodings = []
- for textAttr in attr:
- textAttr, encoding = enum.toCamelCase(textAttr), None
- encoding = FG_ENCODING.get(textAttr, encoding)
- encoding = BG_ENCODING.get(textAttr, encoding)
- encoding = ATTR_ENCODING.get(textAttr, encoding)
- if encoding: encodings.append(encoding)
-
- if encodings:
- return (CSI % ";".join(encodings)) + msg + RESET
- else: return msg
-
-class TorControlCompleter:
- """
- Command autocompleter, fetching the valid options from the attached Tor
- instance.
- """
-
- def __init__(self):
- self._prefix = None
- self._prefixMatches = []
-
- self.commands = []
- conn = torTools.getConn()
-
- # adds all of the valid GETINFO options
- infoOptions = conn.getInfo("info/names", None)
- if infoOptions:
- for line in infoOptions.split("\n"):
- if " " in line:
- # skipping non-existant options mentioned in:
- # https://trac.torproject.org/projects/tor/ticket/3844
-
- if line.startswith("config/*") or line.startswith("dir-usage"):
- continue
-
- infoOpt = line.split(" ", 1)[0]
-
- # strips off the ending asterisk if it accepts a value
- if infoOpt.endswith("*"): infoOpt = infoOpt[:-1]
-
- self.commands.append("GETINFO %s" % infoOpt)
- else: self.commands.append("GETINFO ")
-
- # adds all of the valid GETCONF / SETCONF / RESETCONF options
- confOptions = conn.getInfo("config/names", None)
- if confOptions:
- # individual options are '<name> <type>' pairs
- confEntries = [opt.split(" ", 1)[0] for opt in confOptions.split("\n")]
- self.commands += ["GETCONF %s" % conf for conf in confEntries]
- self.commands += ["SETCONF %s " % conf for conf in confEntries]
- self.commands += ["RESETCONF %s" % conf for conf in confEntries]
- else:
- self.commands.append("GETCONF ")
- self.commands.append("SETCONF ")
- self.commands.append("RESETCONF ")
-
- # adds all of the valid SETEVENTS options
- eventOptions = conn.getInfo("events/names", None)
- if eventOptions:
- self.commands += ["SETEVENTS %s" % event for event in eventOptions.split(" ")]
- else: self.commands.append("SETEVENTS ")
-
- # adds all of the valid USEFEATURE options
- featureOptions = conn.getInfo("features/names", None)
- if featureOptions:
- self.commands += ["USEFEATURE %s" % feature for feature in featureOptions.split(" ")]
- else: self.commands.append("USEFEATURE ")
-
- # adds all of the valid SIGNAL options
- # this can't yet be fetched dynamically, as per:
- # https://trac.torproject.org/projects/tor/ticket/3842
-
- signals = ("RELOAD", "SHUTDOWN", "DUMP", "DEBUG", "HALT", "HUP", "INT",
- "USR1", "USR2", "TERM", "NEWNYM", "CLEARDNSCACHE")
- self.commands += ["SIGNAL %s" % sig for sig in signals]
-
- # shouldn't use AUTHENTICATE since we only provide the prompt with an
- # authenticated controller connection
- #self.commands.append("AUTHENTICATE")
-
- # other options
- self.commands.append("SAVECONF")
- self.commands.append("MAPADDRESS ")
- self.commands.append("EXTENDCIRCUIT ")
- self.commands.append("SETCIRCUITPURPOSE ")
- #self.commands.append("SETROUTERPURPOSE ") # deprecated option
- self.commands.append("ATTACHSTREAM ")
- self.commands.append("+POSTDESCRIPTOR ") # TODO: needs to support multiline options for this (ugg)
- self.commands.append("REDIRECTSTREAM ")
- self.commands.append("CLOSESTREAM ")
- self.commands.append("CLOSECIRCUIT ")
- self.commands.append("RESOLVE ")
- self.commands.append("PROTOCOLINFO ")
- self.commands.append("+LOADCONF") # TODO: another multiline...
- self.commands.append("TAKEOWNERSHIP")
- self.commands.append("QUIT") # TODO: give a confirmation when the user does this?
-
- # adds interpretor commands
- for cmd in HELP_OPTIONS:
- if HELP_OPTIONS[cmd][0].startswith("/"):
- self.commands.append("/" + cmd.lower())
-
- # adds help options for the previous commands
- baseCmd = set([cmd.split(" ")[0].replace("+", "").replace("/", "") for cmd in self.commands])
- for cmd in baseCmd:
- self.commands.append("/help " + cmd)
-
- # adds /help for tor configuration options
- for opt in torConfig.getConfigOptions():
- self.commands.append("/help " + opt)
-
- def getMatches(self, text):
- """
- Provides all options that match the given input. This is case insensetive.
-
- Arguments:
- text - user input text to be matched against
- """
-
- return [cmd for cmd in self.commands if cmd.lower().startswith(text.lower())]
-
- def complete(self, text, state):
- """
- Provides case insensetive autocompletion options, acting as a functor for
- the readlines set_completer function.
- """
-
- if text != self._prefix:
- self._prefix = text
- self._prefixMatches = self.getMatches(text)
-
- # Tab completion fills in the first common prefix which can be
- # problematic if they don't all match. For instance completing "Map" will
- # result in ["MAPADDRESS", "MapAddress"], which gets truncated to the
- # common prefix of just "M" when the user presses tab.
-
- if len(self._prefixMatches) > 1:
- prefixToResult = {}
-
- for match in self._prefixMatches:
- prefix = match[:len(text)]
-
- if prefix in prefixToResult:
- prefixToResult[prefix].append(match)
- else:
- prefixToResult[prefix] = [match]
-
- if len(prefixToResult) > 1:
- # we have multiple prefixes, pick the one with the most results
- self._prefixMatches = None
-
- for matchSet in prefixToResult.values():
- if not self._prefixMatches or len(self._prefixMatches) < len(matchSet):
- self._prefixMatches = matchSet
-
- if state < len(self._prefixMatches):
- return self._prefixMatches[state]
- else: return None
-
-class ControlInterpretor:
- """
- Interpretor that handles queries to the control port, providing usability
- imporvements like irc style help optoins. This tracks input and responses.
- """
-
- def __init__(self):
- self.backlog = [] # prior requests the user has made
- self.contents = [] # (msg, format list) tuples for what's been displayed
- self.writePath = DEFAULT_WRITE_PATH # last location we've saved to
- self.eventBuffer = [] # unread event messages
- self.loggedEvents = [] # event types that we're listening for
-
- # TODO: Dropping event listening since...
- # a. it was a complete hack
- # b. said hack isn't worth porting to stem
- # c. I'm likely about to separate the interpretor from arm anyway
-
- #torTools.getConn().addEventListener(TorEventObserver(self.registerEvent))
-
- def registerEvent(self, event):
- """
- Adds the event to our buffer so it'll be in '/events' output.
- """
-
- if event.type in self.loggedEvents:
- self.eventBuffer.append(event)
-
- eventsCrop = len(self.eventBuffer) - EVENTS_LIMIT
- if eventsCrop > 0: self.eventBuffer = self.eventBuffer[eventsCrop:]
-
- def getBacklog(self):
- """
- Provides the backlog of prior user input.
- """
-
- return self.backlog
-
- def getDisplayContents(self, appendPrompt = None):
- """
- Provides a list of lines to be displayed, each being a list of (msg,
- format) tuples for the content to be displayed. This is ordered as the
- oldest to newest.
-
- Arguments:
- appendPrompt - adds the given line to the end
- """
-
- if appendPrompt:
- return self.contents + [appendPrompt]
- else: return self.contents
-
- def doHelp(self, arg, outputEntry):
- """
- Performs the '/help' operation, giving usage information for the given
- argument or a general summary if there wasn't one.
- """
-
- arg = arg.upper()
-
- # If there's multiple arguments then just take the first. This is
- # particularly likely if they're trying to query a full command (for
- # instance "/help GETINFO version")
-
- arg = arg.split(" ")[0]
-
- # strip slash if someone enters an interpretor command (ex. "/help /help")
- if arg.startswith("/"): arg = arg[1:]
-
- if arg:
- if arg in HELP_OPTIONS:
- # Provides information for the tor or interpretor argument. This bolds
- # the usage information and indents the description after it.
- usage, description = HELP_OPTIONS[arg]
-
- outputEntry.append((usage + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))
-
- for line in description.split("\n"):
- outputEntry.append((" " + line + "\n", OUTPUT_FORMAT))
-
- if arg == "GETINFO":
- # if this is the GETINFO option then also list the valid options
- infoOptions = torTools.getConn().getInfo("info/names", None)
-
- if infoOptions:
- for line in infoOptions.split("\n"):
- if line.startswith("config/*") or line.startswith("dir-usage"):
- continue
-
- lineMatch = re.match("^(.+) -- (.+)$", line)
-
- if lineMatch:
- opt, description = lineMatch.groups()
-
- outputEntry.append(("%-33s" % opt, OUTPUT_FORMAT + (Attr.BOLD, )))
- outputEntry.append((" - %s\n" % description, OUTPUT_FORMAT))
- elif arg == "GETCONF":
- # lists all of the configuration options
-
- confOptions = torTools.getConn().getInfo("config/names", None)
- if confOptions:
- confEntries = [opt.split(" ", 1)[0] for opt in confOptions.split("\n")]
-
- # displays two columns of 42 characters
- for i in range(0, len(confEntries), 2):
- lineEntries = confEntries[i : i+2]
-
- lineContent = ""
- for entry in lineEntries:
- lineContent += "%-42s" % entry
-
- outputEntry.append((lineContent + "\n", OUTPUT_FORMAT))
-
- outputEntry.append(("For more information use '/help [CONFIG OPTION]'.", OUTPUT_FORMAT + (Attr.BOLD, )))
- elif arg == "SIGNAL":
- # lists descriptions for all of the signals
- for signal, description in SIGNAL_DESCRIPTIONS:
- outputEntry.append(("%-15s" % signal, OUTPUT_FORMAT + (Attr.BOLD, )))
- outputEntry.append((" - %s\n" % description, OUTPUT_FORMAT))
- elif arg == "SETEVENTS":
- # lists all of the event types
- eventOptions = torTools.getConn().getInfo("events/names", None)
- if eventOptions:
- eventEntries = eventOptions.split()
-
- # displays four columns of 20 characters
- for i in range(0, len(eventEntries), 4):
- lineEntries = eventEntries[i : i+4]
-
- lineContent = ""
- for entry in lineEntries:
- lineContent += "%-20s" % entry
-
- outputEntry.append((lineContent + "\n", OUTPUT_FORMAT))
- elif arg == "USEFEATURE":
- # lists the feature options
- featureOptions = torTools.getConn().getInfo("features/names", None)
- if featureOptions:
- outputEntry.append((featureOptions + "\n", OUTPUT_FORMAT))
- elif arg in ("LOADCONF", "POSTDESCRIPTOR"):
- # gives a warning that this option isn't yet implemented
- outputEntry.append(("\n" + MULTILINE_UNIMPLEMENTED_NOTICE + "\n", ERROR_FORMAT))
- else:
- # check if this is a configuration option
- manEntry = torConfig.getConfigDescription(arg)
-
- if manEntry:
- # provides basic usage information in bold, followed an indented
- # copy of the man page description (wrapped to eighty characters)
-
- helpTitle = "%s %s (category: %s)\n" % (manEntry.option, manEntry.argUsage, manEntry.category)
- outputEntry.append((helpTitle, OUTPUT_FORMAT + (Attr.BOLD, )))
-
- descLines = manEntry.description.split("\n")
-
- for line in descLines:
- if not line:
- outputEntry.append(("\n", OUTPUT_FORMAT))
- else:
- while line:
- drawPortion, line = uiTools.cropStr(line, 88, 4, 4, uiTools.Ending.HYPHEN, True)
- outputEntry.append((" %s\n" % drawPortion.strip(), OUTPUT_FORMAT))
- else:
- outputEntry.append(("No help information available for '%s'..." % arg, ERROR_FORMAT))
- else:
- # provides the GENERAL_HELP with everything bolded except descriptions
- for line in GENERAL_HELP.split("\n"):
- cmdStart = line.find(" - ")
-
- if cmdStart != -1:
- outputEntry.append((line[:cmdStart], OUTPUT_FORMAT + (Attr.BOLD, )))
- outputEntry.append((line[cmdStart:] + "\n", OUTPUT_FORMAT))
- else:
- outputEntry.append((line + "\n", OUTPUT_FORMAT + (Attr.BOLD, )))
-
- def doEvents(self, arg, outputEntry):
- """
- Performs the '/events' operation, dumping the events that we've received
- belonging to the given types. If no types are specified then this provides
- all buffered events.
- """
-
- bufferedEvents = self.eventBuffer
-
- # if we have an argument then restrict it to those event types
- eventTypes = arg.upper().split()
- if eventTypes:
- bufferedEvents = [event for event in self.eventBuffer if event.type in eventTypes]
-
- for event in bufferedEvents:
- outputEntry.append((event.getDisplayMessage() + "\n", OUTPUT_FORMAT))
-
- def doWrite(self, arg, outputEntry):
- """
- Performs the '/write' operation, which attempts to save the backlog to a
- given path, defaulting to the last location we write to.
- """
-
- if arg: self.writePath = arg
- outputLines = []
-
- for line in self.contents:
- outputLines.append("".join([msg for msg, _ in line]))
-
- try:
- outputFile = open(self.writePath, "w")
- outputFile.write("\n".join(outputLines))
- outputFile.close()
- outputEntry.append(("Interpretor backlog written to: %s" % self.writePath, OUTPUT_FORMAT))
- except IOError, exc:
- outputEntry.append(("Unable to write to '%s': %s" % (self.writePath, exc), ERROR_FORMAT))
-
- def doFind(self, arg, outputEntry):
- """
- Performs the '/find' operation, which lists output from the backlog which
- matches the given regex. Results are deduplicated and matches are bolded.
- """
-
- argMatcher = None
-
- if not arg:
- outputEntry.append(("Nothing to match against", ERROR_FORMAT))
- else:
- try: argMatcher = re.compile("(%s)" % arg)
- except: outputEntry.append(("Unable to compile regex '%s'" % arg, ERROR_FORMAT))
-
- if argMatcher:
- printedLines = []
-
- for line in self.contents:
- lineText = "".join([msg for msg, _ in line])
-
- # skip if this was user input or a duplicate
- if lineText.startswith(PROMPT[0]) or lineText in printedLines:
- continue
-
- match = argMatcher.search(lineText)
- if match:
- # outputs the matching line, with the match itself bolded
- outputEntry.append((lineText[:match.start()], OUTPUT_FORMAT))
- outputEntry.append((match.group(), (OUTPUT_FORMAT + (Attr.BOLD, ))))
- outputEntry.append((lineText[match.end():] + "\n", OUTPUT_FORMAT))
- printedLines.append(lineText)
-
- def doInfo(self, arg, outputEntry):
- """
- Performs the '/info' operation, looking up a relay by fingerprint, IP
- address, or nickname and printing its descriptor and consensus entries in a
- pretty fashion.
- """
-
- fingerprint, conn = None, torTools.getConn()
-
- # TODO: also recognize <ip>:<port> entries?
-
- # determines the fingerprint, leaving it unset and adding an error message
- # if unsuccessful
- if not arg:
- # uses our fingerprint if we're a relay, otherwise gives an error
- fingerprint = conn.getInfo("fingerprint", None)
-
- if not fingerprint:
- outputEntry.append(("We aren't a relay, no information to provide", ERROR_FORMAT))
- elif len(arg) == 40 and re.match("^[0-9a-fA-F]+$", arg):
- # we got a fingerprint (fourty character hex string)
- fingerprint = arg
- elif connections.isValidIpAddress(arg):
- # we got an ip address, look up the fingerprint
- fpMatches = conn.getRelayFingerprint(arg, getAllMatches = True)
-
- if len(fpMatches) == 0:
- outputEntry.append(("No relays found at %s" % arg, ERROR_FORMAT))
- elif len(fpMatches) == 1:
- fingerprint = fpMatches[0][1]
- else:
- outputEntry.append(("Multiple relays at %s, specify which by giving a port" % arg, ERROR_FORMAT))
-
- for i in range(len(fpMatches)):
- relayEntry = outputEntry[i]
- outputEntry.append((" %i. or port: %-5s fingerprint: %s" % (i + 1, relayEntry[0], relayEntry[1]), ERROR_FORMAT))
- else:
- # we got something else, treat it as a nickname
- fingerprint = conn.getNicknameFingerprint(arg)
-
- if not fingerprint:
- outputEntry.append(("No relay with the nickname of '%s' found" % arg, ERROR_FORMAT))
-
- if fingerprint:
- consensusEntry = conn.getConsensusEntry(fingerprint)
-
- # The nickname, address, and port lookups are all based on the consensus
- # entry so if this succeeds we should be pretty confident that those
- # queries will work too.
-
- if not consensusEntry:
- outputEntry.append(("Unable to find consensus information for %s" % fingerprint, ERROR_FORMAT))
- return
-
- address, port = conn.getRelayAddress(fingerprint, (None, None))
-
- # ... but not sure enough that we won't check
- if not address or not port: return
-
- locale = conn.getInfo("ip-to-country/%s" % address, "??")
-
- if INFO_HOSTNAMES:
- hostname = hostnames.resolve(address, 10)
- else:
- hostname = None
-
- # TODO: Most of the following is copied from the _getDetailContent method
- # of cli/connections/connEntry.py - useful bits should be refactored.
- consensusLines = consensusEntry.split("\n")
-
- firstLineComp = consensusLines[0].split(" ")
- if len(firstLineComp) >= 9:
- _, nickname, _, _, pubDate, pubTime, _, orPort, _ = firstLineComp[:9]
- else: nickname, pubDate, pubTime, orPort = "", "", "", ""
-
- flags = "unknown"
- if len(consensusLines) >= 2 and consensusLines[1].startswith("s "):
- flags = consensusLines[1][2:]
-
- exitPolicy = conn.getRelayExitPolicy(fingerprint)
-
- if exitPolicy: policyLabel = exitPolicy.getSummary()
- else: policyLabel = "unknown"
-
- # fetches information from the descriptor if it's available
- torVersion, platform, contact = "", "", ""
- descriptorEntry = conn.getDescriptorEntry(fingerprint)
-
- if descriptorEntry:
- for descLine in descriptorEntry.split("\n"):
- if descLine.startswith("platform"):
- # has the tor version and platform, ex:
- # platform Tor 0.2.1.29 (r318f470bc5f2ad43) on Linux x86_64
-
- torVersion = descLine[13:descLine.find(" ", 13)]
- platform = descLine[descLine.rfind(" on ") + 4:]
- elif descLine.startswith("contact"):
- contact = descLine[8:]
-
- # clears up some highly common obscuring
- for alias in (" at ", " AT "): contact = contact.replace(alias, "@")
- for alias in (" dot ", " DOT "): contact = contact.replace(alias, ".")
-
- break # contact lines come after the platform
-
- headingAttr, infoAttr = (Attr.BOLD, Color.BLUE), ()
-
- outputEntry.append(("%s (%s)\n" % (nickname, fingerprint), infoAttr))
-
- hostnameLabel = ", %s" % hostname if hostname else ""
- outputEntry.append(("address: ", headingAttr))
- outputEntry.append(("%s:%s (%s%s)\n" % (address, port, locale, hostnameLabel), infoAttr))
-
- outputEntry.append(("published: ", headingAttr))
- outputEntry.append(("%s %s" % (pubTime, pubDate) + "\n", infoAttr))
-
- if torVersion and platform:
- outputEntry.append(("os: ", headingAttr))
- outputEntry.append((platform + "\n", infoAttr))
-
- outputEntry.append(("version: ", headingAttr))
- outputEntry.append((torVersion + "\n", infoAttr))
-
- outputEntry.append(("flags: ", headingAttr))
- outputEntry.append((flags.replace(" ", ", ") + "\n", infoAttr))
-
- outputEntry.append(("exit policy: ", headingAttr))
- outputEntry.append((policyLabel + "\n", infoAttr))
-
- if contact:
- outputEntry.append(("contact: ", headingAttr))
- outputEntry.append((contact + "\n", infoAttr))
-
- def handleQuery(self, input):
- """
- Processes the given input. Requests starting with a '/' are special
- commands to the interpretor, and anything else is sent to the control port.
- This returns an input/output tuple, each entry being a list of lines, each
- line having a list of (msg, format) tuples for the content to be displayed.
- This raises a InterpretorClosed if the interpretor should be shut down.
-
- Arguments:
- input - user input to be processed
- """
-
- conn = torTools.getConn()
-
- # abort if the control connection has been severed
- if not conn.isAlive():
- raise InterpretorClosed("Control connection has been closed")
-
- input = input.strip()
-
- # appends new input, cropping if too long
- self.backlog.append(input)
- backlogCrop = len(self.backlog) - BACKLOG_LIMIT
- if backlogCrop > 0: self.backlog = self.backlog[backlogCrop:]
-
- inputEntry, outputEntry = [PROMPT], []
-
- # input falls into three general categories:
- # - interpretor command which starts with a '/'
- # - controller commands handled by torTools (this allows for caching,
- # proper handling by the rest of arm, etc)
- # - unrecognized controller command, this has the possability of confusing
- # arm...
-
- if " " in input: cmd, arg = input.split(" ", 1)
- else: cmd, arg = input, ""
-
- if cmd.startswith("/"):
- # interpretor command
- inputEntry.append((input, INPUT_INTERPRETOR_FORMAT))
-
- if cmd == "/quit": raise InterpretorClosed()
- elif cmd == "/help": self.doHelp(arg, outputEntry)
- elif cmd == "/write": self.doWrite(arg, outputEntry)
- elif cmd == "/find": self.doFind(arg, outputEntry)
- elif cmd == "/events": self.doEvents(arg, outputEntry)
- elif cmd == "/info": self.doInfo(arg, outputEntry)
- else:
- outputEntry.append(("Not yet implemented...", ERROR_FORMAT)) # TODO: implement
-
- # appends a newline so all interpretor commands have a blank before the prompt
- if outputEntry:
- lastEntry = outputEntry[-1]
- outputEntry[-1] = (lastEntry[0].rstrip() + "\n", lastEntry[1])
-
- # TODO: add /help option
- else:
- # controller command
- cmd = cmd.upper() # makes commands uppercase to match the spec
-
- inputEntry.append((cmd + " ", INPUT_CMD_FORMAT))
- if arg: inputEntry.append((arg, INPUT_ARG_FORMAT))
-
- if cmd == "GETINFO":
- try:
- for param in arg.split():
- response = conn.getInfo(param)
- outputEntry.append((response + "\n", OUTPUT_FORMAT))
- except Exception, exc:
- outputEntry.append((str(exc), ERROR_FORMAT))
- elif cmd == "SETCONF" or cmd == "RESETCONF":
- # arguments can either be '<param>', '<param>=<value>', or
- # '<param>="<value>"' entries
- paramList = []
-
- while arg:
- # TODO: I'm a little dubious of this for LineList values (like the
- # ExitPolicy) since they're parsed as a single value. However, tor
- # seems to be happy to get a single comma separated string (though it
- # echos back faithfully rather than being parsed) so leaving this
- # alone for now.
-
- quotedMatch = re.match(r'^(\S+)=\"([^"]+)\"', arg)
- nonquotedMatch = re.match(r'^(\S+)=(\S+)', arg)
-
- if quotedMatch:
- # we're dealing with a '<param>="<value>"' entry
- param, value = quotedMatch.groups()
-
- paramList.append((param, value))
- arg = arg[len(param) + len(value) + 3:].strip()
- elif nonquotedMatch:
- # we're dealing with a '<param>=<value>' entry
- param, value = nonquotedMatch.groups()
-
- paramList.append((param, value))
- arg = arg[len(param) + len(value) + 1:].strip()
- else:
- # starts with just a param
- param = arg.split()[0]
- paramList.append((param, None))
- arg = arg[len(param):].strip()
-
- try:
- isReset = cmd == "RESETCONF"
- conn.setOptions(paramList, isReset)
- except Exception, exc:
- outputEntry.append((str(exc), ERROR_FORMAT))
- elif cmd == "SETEVENTS":
- return ([], []) # dropping support, see the comment for the event listener above for why
-
- self.loggedEvents = arg.split()
- #allEvents = torTools.getConn().setControllerEvents(self.loggedEvents)
- setEvents = set(self.loggedEvents).intersection(allEvents)
-
- if setEvents:
- outputEntry.append(("Successfully set event listening to: %s\n" % ", ".join(setEvents), OUTPUT_FORMAT))
- else:
- if not self.loggedEvents:
- # we purposefully disabled event listening
- outputEntry.append(("Disabled event listening\n", OUTPUT_FORMAT))
- else:
- # we tried to set events but they all failed
- outputEntry.append(("Unable to set any of the events. Event listening disabled.\n", ERROR_FORMAT))
- elif cmd.replace("+", "") in ("LOADCONF", "POSTDESCRIPTOR"):
- # provides a notice that multi-line controller input isn't yet implemented
- outputEntry.append((MULTILINE_UNIMPLEMENTED_NOTICE + "\n", ERROR_FORMAT))
- else:
- try:
- controller = conn.controller
- if controller is None: raise stem.SocketClosed()
-
- response = controller.msg(input)
-
- if cmd == "QUIT":
- raise InterpretorClosed("Closing the connection")
-
- for _, _, content in response.content():
- outputEntry.append((content + '\n', OUTPUT_FORMAT))
- except Exception, exc:
- if isinstance(exc, InterpretorClosed):
- raise exc
- else:
- outputEntry.append((str(exc), ERROR_FORMAT))
-
- # converts to lists split on newlines
- inputLines = _splitOnNewlines(inputEntry)
- outputLines = _splitOnNewlines(outputEntry)
-
- # appends new contents, cropping if too long
- # TODO: it would be nice if InterpretorClosed exceptions were added to the content too
- self.contents += inputLines + outputLines
- cropLines = len(self.contents) - CONTENT_LIMIT
- if cropLines > 0: self.contents = self.contents[cropLines:]
-
- return (inputLines, outputLines)
-
-def showPrompt():
- # When displaying the prompt we want the readline module to be imported so
- # we get its neat features like history scrollback. HOWEVER, importing
- # readline prior to initializing curses causes terminal bugs, most noticeably
- # screen resizing.
-
- import readline
-
- # For Python 2.6 and earlier cycling history via the readline module with
- # up/down is buggy with a color prompt. For more information see:
- # http://bugs.python.org/issue12972
- #
- # To work around this while keeping a color prompt I'm padding the prompt
- # with extra reset encodings so its length is non-rendered higher (around
- # sixty characters). There's two ways that this can go wrong...
- # - if the user uses up/down to display input longer than this non-rendered
- # length then the original bug will manifest (screwed up prompt)
- # - if the terminal's width is smaller than the non-rendered prompt length
- # then the cursor and some movement will be displaced
-
- if COLOR_PROMPT:
- prompt = format(">>> ", Color.GREEN, Attr.BOLD)
-
- majorVersion = sys.version_info[0]
- minorVersion = sys.version_info[1]
-
- if majorVersion <= 2 and minorVersion <= 6:
- prompt += "\x1b[0m" * 10
- else:
- prompt = ">>> "
-
- input = ""
-
- # sets up tab autocompetion
- torCommands = TorControlCompleter()
- readline.parse_and_bind("tab: complete")
- readline.set_completer(torCommands.complete)
-
- # Essentially disables autocompletion by word delimiters. This is because
- # autocompletion options are full commands (ex. "GETINFO version") so we want
- # "GETINFO" to match to all the options rather than be treated as a complete
- # command by itself.
-
- readline.set_completer_delims("\n")
- interpretor = ControlInterpretor()
-
- print INIT_MSG
-
- while True:
- try:
- input = raw_input(prompt)
- _, outputEntry = interpretor.handleQuery(input)
- except (KeyboardInterrupt, Exception), exc:
- if isinstance(exc, InterpretorClosed) and str(exc):
- print format(str(exc), *ERROR_FORMAT)
-
- # moves cursor to the next line and terminates (most commonly
- # KeyboardInterrupt and EOFErro)
- print
-
- torTools.NO_SPAWN = True
- torTools.getConn().close()
-
- # stop daemons
- hostnames.stop()
-
- break
-
- for line in outputEntry:
- outputLine = ""
-
- for msg, msgFormat in line:
- outputLine += format(msg, *msgFormat)
-
- print outputLine
-
-def _splitOnNewlines(entry):
- """
- Splits a list of (msg, format) tuples on newlines into a list of lines.
-
- Arguments:
- entry - list of display tuples
- """
-
- results, tmpLine = [], []
- entry = list(entry) # shallow copy
-
- while entry:
- msg, msgFormat = entry.pop(0)
-
- if "\n" in msg:
- msg, remainder = msg.split("\n", 1)
- entry.insert(0, (remainder, msgFormat))
-
- tmpLine.append((msg, msgFormat))
- results.append(tmpLine)
- tmpLine = []
- else:
- tmpLine.append((msg, msgFormat))
-
- if tmpLine: results.append(tmpLine)
- return results
-
1
0

r25950: {website} try an experiment in donations. (website/trunk/donate/en)
by Andrew Lewman 18 Dec '12
by Andrew Lewman 18 Dec '12
18 Dec '12
Author: phobos
Date: 2012-12-18 15:40:53 +0000 (Tue, 18 Dec 2012)
New Revision: 25950
Modified:
website/trunk/donate/en/donate.wml
Log:
try an experiment in donations.
Modified: website/trunk/donate/en/donate.wml
===================================================================
--- website/trunk/donate/en/donate.wml 2012-12-18 04:42:41 UTC (rev 25949)
+++ website/trunk/donate/en/donate.wml 2012-12-18 15:40:53 UTC (rev 25950)
@@ -99,9 +99,9 @@
<li><input type="radio" class="radio" name="amount" value="1000.00"><label for="1000">1000</label></li>
<li><input type="radio" class="radio" name="amount" value="500.00"><label for="500">500</label></li>
<li><input type="radio" class="radio" name="amount" value="250.00"><label for="250">250</label></li>
- <li><input type="radio" class="radio" name="amount" value="100.00"><label for="100">100</label></li>
+ <li><input type="radio" class="radio" name="amount" value="100.00" checked="checked"><label for="100">100</label></li>
<li><input type="radio" class="radio" name="amount" value="50.00"><label for="50">50</label></li>
- <li><input type="radio" class="radio" name="amount" value="20.00" checked="checked"><label for="20">20</label></li>
+ <li><input type="radio" class="radio" name="amount" value="20.00"><label for="20">20</label></li>
<li><input type="radio" class="radio" name="amount" value="10.00"><label for="10">10</label></li>
<li><input type="radio" class="radio" name="amount" value="5.00"><label for="5">5</label></li>
</ul>
1
0

[translation/tails-greeter_completed] Update translations for tails-greeter_completed
by translation@torproject.org 18 Dec '12
by translation@torproject.org 18 Dec '12
18 Dec '12
commit da2d110d32415d648967885d4a3935c705e413d3
Author: Translation commit bot <translation(a)torproject.org>
Date: Tue Dec 18 15:16:04 2012 +0000
Update translations for tails-greeter_completed
---
ru/ru.po | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ru/ru.po b/ru/ru.po
index 44d1ea2..5d16ca1 100644
--- a/ru/ru.po
+++ b/ru/ru.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: https://trac.torproject.org/projects/tor\n"
"POT-Creation-Date: 2012-06-01 15:10+0200\n"
-"PO-Revision-Date: 2012-12-02 21:41+0000\n"
+"PO-Revision-Date: 2012-12-18 15:00+0000\n"
"Last-Translator: ddark008 <i(a)ddark008.ru>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/torproject/language/ru/)\n"
"MIME-Version: 1.0\n"
@@ -41,7 +41,7 @@ msgstr "Нет"
#: ../glade/persistencewindow.glade.h:6
msgid "Passphrase:"
-msgstr "Пароль"
+msgstr "Пароль:"
#: ../glade/persistencewindow.glade.h:7
msgid "Read-Only?"
1
0