Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
autonym
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
putro
autonym
Commits
e93eeebb
Commit
e93eeebb
authored
Oct 11, 2016
by
putro
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
pep8 improvements
parent
7a0a7848
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
97 additions
and
75 deletions
+97
-75
autonym.py
autonym.py
+10
-12
checks.py
checks.py
+5
-8
gpgfuncts.py
gpgfuncts.py
+26
-13
stats.py
stats.py
+20
-18
utils.py
utils.py
+19
-9
validate.py
validate.py
+17
-15
No files found.
autonym.py
View file @
e93eeebb
...
...
@@ -3,11 +3,11 @@
import
os
import
sys
import
checks
from
config
import
config
,
nyms
,
nymList
,
printNymList
,
configSection
,
writeConfig
,
delSection
from
config
import
config
,
nyms
,
nymList
,
configSection
,
writeConfig
,
delSection
import
traceback
import
re
from
stats
import
parse_stats
,
stats_m
,
uptime_sort_m
,
format_stats
from
utils
import
pressKey
,
validateChoice
,
chooseList
,
askPassphrase
,
askSomething
,
askYesNo
from
utils
import
pressKey
,
chooseList
,
askPassphrase
,
askSomething
,
askYesNo
from
utils
import
chooseSubjType
,
selectServer
,
getDomain
,
validateEmail
,
isGpg
,
isAscii
,
X_is_running
,
editMessage
import
gpgfuncts
import
easygui
...
...
@@ -15,8 +15,6 @@ import string
import
subprocess
class
bcolors
:
"""
Define colors for outputs
...
...
@@ -27,7 +25,6 @@ class bcolors:
ENDC
=
'
\033
[0m'
def
disable
(
self
):
self
.
GREEN
=
''
self
.
YELLOW
=
''
self
.
RED
=
''
...
...
@@ -95,6 +92,7 @@ def menu():
pressKey
()
menu
()
def
askNym
():
""" set active nym """
nymlist
=
nymList
(
nyms
)
...
...
@@ -130,6 +128,7 @@ def createNym():
menu
()
sendMixMsg
(
dest
,
emsg
)
def
updateNym
(
delete
=
False
):
print
"Choose nym to update/delete:"
askNym
()
...
...
@@ -241,7 +240,6 @@ def sendMixMsg(dest, msg, subj="subject", test=False):
raise
MixError
(
'Could not find mixmaster binary.'
)
def
writeMessage
():
"""
Create a message to be sent from nym
...
...
@@ -261,7 +259,6 @@ def writeMessage():
pressKey
()
continue
recipient
=
raw_input
(
"Insert recipient email address or newsgroup name: "
)
if
X_is_running
():
...
...
@@ -307,7 +304,7 @@ def modifyConfig():
pass
if
checks
.
OptionsCheck
(
config
)
==
True
:
if
checks
.
OptionsCheck
(
config
)
is
True
:
config
[
'last_message'
]
=
"OK, initial check passed"
if
not
nyms
.
sections
:
...
...
@@ -352,7 +349,7 @@ def validateChain(chain):
remailer_chain
.
append
(
uptime_sort_m
()[
n
])
# check if last remailer is middleman
if
checkMiddle
(
remailer_chain
[
len
(
remailer_chain
)
-
1
]):
if
checkMiddle
(
remailer_chain
[
len
(
remailer_chain
)
-
1
]):
print
bcolors
.
RED
+
"ERROR - remailer
%
s is middleman, it cannot be last remailer in chain"
%
remailer_chain
[
len
(
remailer_chain
)
-
1
]
+
bcolors
.
ENDC
pressKey
()
chooseChain
()
...
...
@@ -372,12 +369,13 @@ def checkMiddle(remailer):
else
:
return
False
def
checkBrokenChains
(
remailer_chain
):
""" check for broken remailer chains """
for
i
in
range
(
0
,
len
(
remailer_chain
)
-
1
):
for
i
in
range
(
0
,
len
(
remailer_chain
)
-
1
):
cur
=
remailer_chain
[
i
]
next
=
remailer_chain
[
i
+
1
]
if
next
in
stats_m
[
cur
]
.
broken
:
next
rem
=
remailer_chain
[
i
+
1
]
if
next
rem
in
stats_m
[
cur
]
.
broken
:
return
True
else
:
return
False
...
...
checks.py
View file @
e93eeebb
...
...
@@ -5,13 +5,14 @@ import sys
from
utils
import
download
import
subprocess
class
MixError
(
Exception
):
pass
def
checkAscii
(
file
):
""" Check if the a file is pure ascii """
import
codecs
bad
=
0
f
=
codecs
.
open
(
file
,
encoding
=
'ascii'
)
lines
=
open
(
file
)
.
readlines
()
...
...
@@ -28,10 +29,8 @@ def checkAscii(file):
return
bad
def
OptionsCheck
(
config
):
""" performs some checks """
try
:
f
=
open
(
os
.
path
.
dirname
(
sys
.
argv
[
0
])
+
"/check"
,
"w"
)
except
:
...
...
@@ -46,12 +45,10 @@ def OptionsCheck(config):
except
:
print
"Error: cannot download fresh remailer stats"
try
:
mix
=
subprocess
.
Popen
(
config
[
'options'
][
'mixmaster'
],
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
mix
=
subprocess
.
Popen
(
config
[
'options'
][
'mixmaster'
],
stdin
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
except
OSError
:
# usally means that mixmaster could not be executed
raise
MixError
(
'Could not find mixmaster binary.'
)
return
True
return
True
\ No newline at end of file
gpgfuncts.py
View file @
e93eeebb
...
...
@@ -6,7 +6,6 @@ from config import config
from
utils
import
pressKey
try
:
global
gpg
gpg
=
gnupg
.
GPG
()
...
...
@@ -14,6 +13,7 @@ except:
print
"Error, problems with your keyring directory, exiting"
sys
.
exit
(
0
)
def
createKey
(
email
,
name
,
comment
=
""
,
key_type
=
"RSA"
,
key_length
=
4096
):
""" Create a gpg key """
while
True
:
...
...
@@ -21,8 +21,11 @@ def createKey(email, name, comment="", key_type="RSA", key_length=4096):
pwd2
=
getpass
.
getpass
(
"type again the password: "
)
if
pwd
==
pwd2
:
break
print
"wait, I'm generating the key, move the mouse, press keys, it could take some times......
\n
"
input_data
=
gpg
.
gen_key_input
(
name_real
=
name
,
name_email
=
email
,
name_comment
=
""
,
passphrase
=
pwd
,
key_type
=
key_type
,
key_length
=
key_length
,
)
print
"wait, I'm generating the key, move the mouse, "
\
"press keys, it could take some times......
\n
"
input_data
=
gpg
.
gen_key_input
(
name_real
=
name
,
name_email
=
email
,
name_comment
=
""
,
passphrase
=
pwd
,
key_type
=
key_type
,
key_length
=
key_length
,
)
key
=
gpg
.
gen_key
(
input_data
)
...
...
@@ -42,13 +45,16 @@ def selectKey(email, secret=False):
""" if match found, select key """
if
m
:
"""check if key is valid"""
if
encrypt
(
"test"
,
k
[
"fingerprint"
],
test
=
True
)
.
status
==
"encryption ok"
:
if
encrypt
(
"test"
,
k
[
"fingerprint"
],
test
=
True
)
.
status
==
"encryption ok"
:
key
=
True
config
[
'last_message'
]
=
"OK - key choosed: keyID=
%
s"
%
getKeyID
(
k
[
"fingerprint"
])
config
[
'last_message'
]
=
"OK - key choosed: keyID=
%
s"
\
%
getKeyID
(
k
[
"fingerprint"
])
break
else
:
config
[
'last_message'
]
=
"ERROR - invalid key for recipient:
%
s"
%
u
config
[
'last_message'
]
=
"ERROR - invalid key for "
\
"recipient:
%
s"
%
u
key
=
False
if
key
:
break
...
...
@@ -59,6 +65,7 @@ def selectKey(email, secret=False):
return
False
return
k
[
"fingerprint"
]
def
listSecKeys
():
""" Print list of secret keys in keyring"""
seckeys
=
gpg
.
list_keys
(
secret
=
True
)
...
...
@@ -69,6 +76,7 @@ def listSecKeys():
print
[
x
for
x
in
k
[
"uids"
]]
return
seckeys
def
selectSecKey
(
email
):
""" Select the key fingerprint of an email address"""
global
last_message
...
...
@@ -89,7 +97,8 @@ def selectSecKey(email):
config
[
'nym_fp'
]
=
k
[
"fingerprint"
]
config
[
'nym_keyid'
]
=
getKeyID
(
k
[
"fingerprint"
])
key
=
True
last_message
=
"OK - secret key choosed: keyID=
%
s"
%
config
[
'nym_keyid'
]
last_message
=
"OK - secret key choosed: keyID=
%
s"
\
%
config
[
'nym_keyid'
]
break
if
key
:
break
...
...
@@ -98,17 +107,20 @@ def selectSecKey(email):
return
False
return
k
[
"fingerprint"
]
def
getKeyID
(
fp
):
""" Get keyid from fingeprint """
# works only with v4 keys
return
fp
[
-
8
:]
def
exportPubKey
(
id
):
""" Export public key
id can be the fingerprint """
ascii_armored_public_keys
=
stripVersion
(
str
(
gpg
.
export_keys
(
id
)))
return
ascii_armored_public_keys
def
defineSecKey
():
""" Select which secret key to use """
ls
=
listSecKeys
()
...
...
@@ -127,12 +139,14 @@ def defineSecKey():
print
"you can't enter a key number lower than 1"
continue
elif
a
>
numkeys
:
print
"there are only
%
d keys, select key number again"
%
numkeys
print
"there are only
%
d keys, select key number again"
\
%
numkeys
continue
break
a
-=
1
return
ls
[
a
]
def
deleteKey
(
fp
):
""" Delete secret and public key """
global
last_message
...
...
@@ -141,6 +155,7 @@ def deleteKey(fp):
str
(
gpg
.
delete_keys
(
fp
))
last_message
=
"OK - key with fingerprint
%
s deleted"
%
fp
def
stripVersion
(
pgptext
):
"""Version strings in PGP blocks can weaken anonymity by partitioning users
into subsets. This function replaces the Version string with 'N/A'."""
...
...
@@ -154,12 +169,10 @@ def encrypt(message, recipient, sign=False, passphrase=False, test=False):
sign
=
sign
,
passphrase
=
passphrase
)
if
not
test
:
if
encrypted_ascii_data
.
status
!=
"encryption ok"
:
config
[
'last_message'
]
=
"ERROR "
+
encrypted_ascii_data
.
status
+
" (recipient:
%
s)"
%
recipient
config
[
'last_message'
]
=
"ERROR "
+
encrypted_ascii_data
.
status
+
\
" (recipient:
%
s)"
%
recipient
return
"ERROR"
else
:
return
stripVersion
(
str
(
encrypted_ascii_data
))
else
:
return
encrypted_ascii_data
return
encrypted_ascii_data
\ No newline at end of file
stats.py
View file @
e93eeebb
...
...
@@ -27,14 +27,13 @@ class Remailer():
self
.
name
=
name
class
RemailerStats
(
Remailer
):
def
__init__
(
self
,
name
,
latency
,
uptime
):
def
__init__
(
self
,
name
,
latency
,
uptime
):
self
.
name
=
name
self
.
latency
=
latency
self
.
uptime
=
uptime
self
.
broken
=
[]
self
.
allow_from
=
False
# by default no remailers accept from headers
self
.
allow_from
=
False
# by default no remailers accept from headers
self
.
middleman
=
False
self
.
post
=
False
...
...
@@ -42,15 +41,16 @@ stats_m = {}
bad_mail2news
=
{}
def
parse_stats
():
try
:
_read_mlist
()
try
:
_read_allow_from
()
except
IOError
:
# Error reading from.html
except
IOError
:
# Error reading from.html
pass
except
IOError
:
# Could not read mlist2.txt
except
IOError
:
# Could not read mlist2.txt
stats
.
clear
()
...
...
@@ -66,43 +66,46 @@ def _read_mlist():
in_stats_block
=
True
elif
in_stats_block
:
elems
=
line
.
split
()
if
len
(
elems
)
==
0
:
# blank line, we are out of the stats block
if
len
(
elems
)
==
0
:
# blank line, we are out of the stats block
in_stats_block
=
False
else
:
name
=
elems
[
0
]
latency
=
elems
[
2
]
uptime
=
float
(
elems
[
4
]
.
strip
(
'
%
'
))
stats_m
[
name
]
=
RemailerStats
(
name
,
latency
,
uptime
)
stats_m
[
name
]
=
RemailerStats
(
name
,
latency
,
uptime
)
elif
"Broken type-II remailer chains"
in
line
:
in_broken_block
=
True
elif
in_broken_block
:
elems
=
line
.
strip
()
.
strip
(
'()'
)
.
split
()
if
len
(
elems
)
==
0
:
# blank line, we are out of the broken chains block
if
len
(
elems
)
==
0
:
# blank line, we are out of the broken chains block
in_broken_block
=
False
else
:
rem_from
=
elems
[
0
]
rem_to
=
elems
[
1
]
if
rem_from
==
'*'
:
# This remailer doesn't accept messages
if
rem_from
==
'*'
:
# This remailer doesn't accept messages
completely_broken
.
add
(
rem_to
)
elif
rem_to
==
'*'
:
# This remailer doesn't send messages to anyone
elif
rem_to
==
'*'
:
# This remailer doesn't send messages to anyone
completely_broken
.
add
(
rem_from
)
else
:
stats_m
[
rem_from
]
.
broken
.
append
(
rem_to
)
elif
'='
in
line
:
# we are now looking for the remailer capabilities block
name
=
line
[
11
:]
.
split
(
'"'
,
1
)[
0
]
elif
'='
in
line
:
# we are now looking for the remailer capabilities block
name
=
line
[
11
:]
.
split
(
'"'
,
1
)[
0
]
rem_stats
=
stats_m
[
name
]
rem_stats
.
middleman
=
(
"middle"
in
line
)
rem_stats
.
post
=
(
"post"
in
line
)
rem_stats
.
address
=
line
[
25
:]
.
split
(
'>'
,
1
)[
0
]
rem_stats
.
address
=
line
[
25
:]
.
split
(
'>'
,
1
)[
0
]
# cleanup up completely useless remailers
for
remailer
in
completely_broken
:
del
stats_m
[
remailer
]
def
_read_allow_from
():
with
open
(
config
[
'stats'
][
'allow_from'
],
'r'
)
as
f
:
# find out those that do accept from headers
with
open
(
config
[
'stats'
][
'allow_from'
],
'r'
)
as
f
:
# find out those that do accept from headers
in_from_block
=
False
m
=
re
.
compile
(
"<td>(
\
w+)</td>"
)
for
line
in
f
:
...
...
@@ -136,7 +139,7 @@ def format_stats(name):
if
rem
.
broken
:
s
+=
" (breaks: "
for
remailer
in
rem
.
broken
:
s
+=
remailer
+
","
s
+=
remailer
+
","
s
=
s
.
rstrip
(
','
)
s
+=
')'
return
s
...
...
@@ -144,10 +147,9 @@ def format_stats(name):
def
uptime_sort_m
():
remailers
=
stats_m
.
keys
()
remailers
.
sort
(
cmp
=
lambda
x
,
y
:
cmp
(
stats_m
[
y
]
.
uptime
,
stats_m
[
x
]
.
uptime
))
remailers
.
sort
(
cmp
=
lambda
x
,
y
:
cmp
(
stats_m
[
y
]
.
uptime
,
stats_m
[
x
]
.
uptime
))
n
=
1
for
r
in
remailers
:
stats_m
[
r
]
.
number
=
str
(
n
)
n
+=
1
return
remailers
utils.py
View file @
e93eeebb
...
...
@@ -7,6 +7,7 @@ import getpass
class
InputError
(
Exception
):
pass
def
download
(
file
,
config
):
""" Download remailer stats file """
print
"downloading
%
s....... please wait"
%
file
...
...
@@ -35,6 +36,7 @@ def pressKey():
""" wait user to press RETURN """
raw_input
(
"press RETURN to continue
\n
"
)
def
validateEmail
(
email
):
""" check if the recipient is a valid email address"""
if
re
.
match
(
r'[\w\-][\w\-\.]*@[\w\-][\w\-\.]+[a-zA-Z]{1,4}'
,
email
):
...
...
@@ -42,6 +44,7 @@ def validateEmail(email):
else
:
return
False
def
validateChoice
(
choice
,
list
):
try
:
i
=
(
int
(
choice
))
...
...
@@ -63,10 +66,12 @@ def askPassphrase():
pwd
=
getpass
.
getpass
(
"insert passphrase for nym secret key: "
)
return
pwd
def
askSomething
(
question
):
usr_input
=
raw_input
(
question
)
return
usr_input
def
askYesNo
(
question
,
default
=
"yes"
):
"""Ask a yes/no question via raw_input() and return their answer.
...
...
@@ -79,7 +84,7 @@ def askYesNo(question, default="yes"):
"""
valid
=
{
"yes"
:
True
,
"y"
:
True
,
"ye"
:
True
,
"no"
:
False
,
"n"
:
False
}
if
default
==
None
:
if
default
is
None
:
prompt
=
" [y/n] "
elif
default
==
"yes"
:
prompt
=
" [Y/n] "
...
...
@@ -96,15 +101,17 @@ def askYesNo(question, default="yes"):
elif
choice
in
valid
:
return
valid
[
choice
]
else
:
sys
.
stdout
.
write
(
"Please respond with 'yes' or 'no' "
\
sys
.
stdout
.
write
(
"Please respond with 'yes' or 'no' "
"(or 'y' or 'n').
\n
"
)
def
printList
(
list
):
counter
=
1
for
i
in
list
:
print
counter
,
"-"
,
i
counter
+=
1
def
chooseList
(
list
):
printList
(
list
)
usr_input
=
''
...
...
@@ -119,7 +126,7 @@ def chooseList(list):
def
selectServer
(
config
):
servers
=
config
[
'options'
][
'nymservers'
]
for
idx
,
val
in
enumerate
(
servers
):
print
idx
+
1
,
val
print
idx
+
1
,
val
while
True
:
choice
=
raw_input
(
"Enter the nymserver you want to use: "
)
...
...
@@ -133,7 +140,7 @@ def selectServer(config):
def
chooseSubjType
(
config
):
subject_types
=
config
[
'options'
][
'subj_types'
]
for
idx
,
val
in
enumerate
(
subject_types
):
print
idx
+
1
,
"-"
,
val
print
idx
+
1
,
"-"
,
val
while
True
:
choice
=
raw_input
(
"Enter what kind of subject you want: "
)
...
...
@@ -143,10 +150,12 @@ def chooseSubjType(config):
subj_type
=
subject_types
[
int
(
choice
)
-
1
]
return
subj_type
def
getDomain
(
email
):
domain
=
email
.
split
(
"@"
)[
1
]
return
domain
def
isGpg
(
message
):
m
=
re
.
compile
(
'^-----BEGIN PGP MESSAGE-----'
)
line
=
message
.
split
(
"
\n
"
,
1
)[
0
]
...
...
@@ -157,16 +166,18 @@ def isGpg(message):
else
:
return
False
def
isAscii
(
text
):
return
all
(
ord
(
c
)
<
128
for
c
in
text
)
def
editMessage
():
import
sys
,
tempfile
,
os
import
tempfile
,
os
from
subprocess
import
call
EDITOR
=
os
.
environ
.
get
(
'EDITOR'
,
'vim'
)
EDITOR
=
os
.
environ
.
get
(
'EDITOR'
,
'vim'
)
initial_message
=
""
# if you want to set up the file somehow
initial_message
=
""
# if you want to set up the file somehow
with
tempfile
.
NamedTemporaryFile
(
suffix
=
".tmp"
)
as
tempfile
:
tempfile
.
write
(
initial_message
)
...
...
@@ -174,7 +185,7 @@ def editMessage():
call
([
EDITOR
,
tempfile
.
name
])
f
=
open
(
tempfile
.
name
,
"r"
)
data
=
f
.
read
()
data
=
f
.
read
()
f
.
close
()
return
data
...
...
@@ -185,4 +196,3 @@ def X_is_running():
p
=
Popen
([
"xset"
,
"-q"
],
stdout
=
PIPE
,
stderr
=
PIPE
)
p
.
communicate
()
return
p
.
returncode
==
0
validate.py
View file @
e93eeebb
...
...
@@ -28,6 +28,7 @@ from config import conf, NONE, RANDOM, POST
class
InputError
(
Exception
):
pass
def
val_email_local
(
local
):
ac
=
"[a-zA-Z0-9_!#
\
$
%
&'*+
\
-=?
\
^`{|}~]"
m
=
re
.
compile
(
'^('
+
ac
+
'+
\
.)*'
+
ac
+
'+$'
)
...
...
@@ -44,7 +45,7 @@ def val_dot_seq(server):
def
val_email
(
addr
):
m
=
re
.
compile
(
'^("?[
\
w ]*"? *<)?([^>]+)>?$'
)
ok
=
m
.
match
(
addr
)
ok
=
m
.
match
(
addr
)
try
:
if
not
ok
:
raise
InputError
()
...
...
@@ -56,7 +57,7 @@ def val_email(addr):
val_dot_seq
(
parts
[
1
])
except
InputError
:
if
addr
:
raise
InputError
(
addr
+
' is not a valid email address.'
)
raise
InputError
(
addr
+
' is not a valid email address.'
)
raise
InputError
(
'Please enter an email address.'
)
...
...
@@ -73,9 +74,9 @@ def val_references(refs):
ok
=
m
.
match
(
ref
)
if
not
ok
:
raise
InputError
()
val_email
(
ok
.
group
(
1
)
)
val_email
(
ok
.
group
(
1
)
)
except
:
raise
InputError
(
ref
+
' is not a valid reference.'
)
raise
InputError
(
ref
+
' is not a valid reference.'
)
def
val_mail2news
(
mail2news
):
...
...
@@ -83,13 +84,14 @@ def val_mail2news(mail2news):
try
:
val_email
(
addr
)
except
InputError
:
raise
InputError
(
mail2news
+
' is not a valid mail2news gateway'
)
raise
InputError
(
mail2news
+
' is not a valid mail2news gateway'
)
def
val_hashcash
(
hc
):
m
=
re
.
compile
(
'[a-zA-Z0-9_:+
\
-=; ]+'
)
if
not
m
.
match
(
hc
):
raise
InputError
(
hc
+
' is not a valid hashcash.'
)
raise
InputError
(
hc
+
' is not a valid hashcash.'
)
def
val_n_copies
(
n_copies
):
if
n_copies
<=
0
or
n_copies
>
conf
.
max_copies
:
...
...
@@ -138,16 +140,16 @@ def parse_chain(fs,m2n=None):
last
=
chain
[
len
(
chain
)
-
1
]
if
stats
[
last
]
.
middleman
:
# make sure that final remailer is not a middleman
raise
InputError
(
'Cannot select a "middleman" remailer as your final remailer.'
)
if
m2n
:
# this is a news article
if
m2n
==
POST
and
not
stats
[
last
]
.
post
:
# make sure that final remailer can post to newsgroups
raise
InputError
(
last
+
'does not have the "post" option configured.'
)
elif
m2n
in
bad_mail2news
:
# make sure that we are not using an incompatible exit node and mail2news gateway
if
m2n
:
# this is a news article
if
m2n
==
POST
and
not
stats
[
last
]
.
post
:
# make sure that final remailer can post to newsgroups
raise
InputError
(
last
+
'does not have the "post" option configured.'
)
elif
m2n
in
bad_mail2news
:
# make sure that we are not using an incompatible exit node and mail2news gateway
if
last
in
bad_mail2news
[
m2n
]:
raise
InputError
(
last
+
' is not compatible with the mail2news gateway '
+
m2n
+
'.'
)
raise
InputError
(
last
+
' is not compatible with the mail2news gateway '
+
m2n
+
'.'
)
# check for bad chains
for
i
in
range
(
0
,
len
(
chain
)
-
1
):
for
i
in
range
(
0
,
len
(
chain
)
-
1
):
cur
=
chain
[
i
]
next
=
chain
[
i
+
1
]
if
next
in
stats
[
cur
]
.
broken
:
raise
InputError
(
'Cannot use known broken chain: ('
+
cur
+
' '
+
next
+
').'
)
next
c
=
chain
[
i
+
1
]
if
next
c
in
stats
[
cur
]
.
broken
:
raise
InputError
(
'Cannot use known broken chain: ('
+
cur
+
' '
+
next
+
').'
)
return
chain
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment