mirror of
https://github.com/r4sas/py-i2phosts
synced 2025-01-22 04:24:15 +00:00
Convert tabs to spaces
This commit is contained in:
parent
75de76cf4c
commit
6e3862d7ab
@ -7,32 +7,32 @@ import configobj
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Hosts builder for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Hosts builder for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/builder.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
parser.add_argument('-f', '--file',
|
||||
help='write hosts into specified file')
|
||||
help='write hosts into specified file')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='write debug messages to stdout')
|
||||
help='write debug messages to stdout')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read config
|
||||
spec = '''
|
||||
hostsfile = string(default=None)
|
||||
'''
|
||||
hostsfile = string(default=None)
|
||||
'''
|
||||
spec = spec.split('\n')
|
||||
config = configobj.ConfigObj(args.config_file, configspec=spec, file_error=True)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.postkey.models import i2phost
|
||||
@ -43,12 +43,12 @@ validate_config(config)
|
||||
|
||||
# result hosts.txt
|
||||
if args.file:
|
||||
hostsfile = args.file
|
||||
hostsfile = args.file
|
||||
elif config['hostsfile'] != None:
|
||||
hostsfile = config['hostsfile']
|
||||
hostsfile = config['hostsfile']
|
||||
else:
|
||||
sys.stderr.write('Please specify "-f" or define "hostsfile" in config\n')
|
||||
sys.exit(1)
|
||||
sys.stderr.write('Please specify "-f" or define "hostsfile" in config\n')
|
||||
sys.exit(1)
|
||||
|
||||
f = open(hostsfile, 'w')
|
||||
# get activated hosts
|
||||
@ -57,5 +57,5 @@ qs = i2phost.objects.filter(activated=True)
|
||||
l = qs.values('name', 'b64hash')
|
||||
# write final hosts.txt-format file
|
||||
for entry in l:
|
||||
f.write(entry['name'] + '=' + entry['b64hash'] + '\n')
|
||||
f.write(entry['name'] + '=' + entry['b64hash'] + '\n')
|
||||
f.close()
|
||||
|
@ -10,34 +10,34 @@ import time
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Hosts checker for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Hosts checker for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='set loglevel to debug and write messages to stdout'),
|
||||
help='set loglevel to debug and write messages to stdout'),
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='set loglevel to info and write messages to stdout'),
|
||||
help='set loglevel to info and write messages to stdout'),
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/checker.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read config
|
||||
spec = '''
|
||||
log_file = string(default='/var/log/py-i2phosts/master.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
lookup_retries = integer(1, 20, default=2)
|
||||
'''
|
||||
log_file = string(default='/var/log/py-i2phosts/master.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
lookup_retries = integer(1, 20, default=2)
|
||||
'''
|
||||
spec = spec.split('\n')
|
||||
config = configobj.ConfigObj(args.config_file, configspec=spec)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.postkey.models import i2phost
|
||||
@ -50,22 +50,22 @@ validate_config(config)
|
||||
|
||||
# configure logger
|
||||
if args.debug == True:
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
elif args.verbose == True:
|
||||
log_level = 'info'
|
||||
log_file = None
|
||||
log_level = 'info'
|
||||
log_file = None
|
||||
else:
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log = get_logger(filename=log_file, log_level=log_level)
|
||||
|
||||
# determine BOB interface address
|
||||
if 'bob_addr' in config:
|
||||
bob_addr = config['bob_addr']
|
||||
bob_addr = config['bob_addr']
|
||||
else:
|
||||
log.warning('BOB address isn\'t specified in config, falling back to localhost')
|
||||
bob_addr = '127.0.0.1:2827'
|
||||
log.warning('BOB address isn\'t specified in config, falling back to localhost')
|
||||
bob_addr = '127.0.0.1:2827'
|
||||
|
||||
# split bob_addr to ip and port
|
||||
bob_ip, bob_port = bob_addr.split(':')
|
||||
@ -73,37 +73,37 @@ bob_ip, bob_port = bob_addr.split(':')
|
||||
# connect to BOB
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect((bob_ip, int(bob_port)))
|
||||
# just receive BOB's greeting
|
||||
time.sleep(1)
|
||||
data = s.recv(512)
|
||||
# make file object
|
||||
f = s.makefile('r')
|
||||
s.connect((bob_ip, int(bob_port)))
|
||||
# just receive BOB's greeting
|
||||
time.sleep(1)
|
||||
data = s.recv(512)
|
||||
# make file object
|
||||
f = s.makefile('r')
|
||||
except socket.error, e:
|
||||
log.error('failed to connect to BOB: %s', e)
|
||||
sys.exit(1)
|
||||
log.error('failed to connect to BOB: %s', e)
|
||||
sys.exit(1)
|
||||
|
||||
all_hosts = i2phost.objects.all().order_by('-activated', '-last_seen')
|
||||
log.info('starting check')
|
||||
for host in all_hosts:
|
||||
log.debug('%s: testing...', host.name)
|
||||
# get b32 address from full dest key
|
||||
dest = host.b64hash
|
||||
b32dest = get_b32(dest)
|
||||
# do name lookup query with b32 address
|
||||
# it success only if host is alive
|
||||
for i in range(config['lookup_retries']):
|
||||
s.send('lookup %s\n' % b32dest)
|
||||
data = f.readline().rstrip('\n')
|
||||
if data == 'ERROR Address Not found.':
|
||||
log.debug('%s: unable to resolve, try: %s', host.name, i)
|
||||
elif data == 'OK ' + host.b64hash:
|
||||
log.info('alive host: %s', host.name)
|
||||
# update lastseen timestamp
|
||||
host.last_seen = datetime.datetime.utcnow()
|
||||
host.save()
|
||||
break
|
||||
else:
|
||||
log.warning('unexpected reply: %s', data)
|
||||
log.debug('%s: testing...', host.name)
|
||||
# get b32 address from full dest key
|
||||
dest = host.b64hash
|
||||
b32dest = get_b32(dest)
|
||||
# do name lookup query with b32 address
|
||||
# it success only if host is alive
|
||||
for i in range(config['lookup_retries']):
|
||||
s.send('lookup %s\n' % b32dest)
|
||||
data = f.readline().rstrip('\n')
|
||||
if data == 'ERROR Address Not found.':
|
||||
log.debug('%s: unable to resolve, try: %s', host.name, i)
|
||||
elif data == 'OK ' + host.b64hash:
|
||||
log.info('alive host: %s', host.name)
|
||||
# update lastseen timestamp
|
||||
host.last_seen = datetime.datetime.utcnow()
|
||||
host.save()
|
||||
break
|
||||
else:
|
||||
log.warning('unexpected reply: %s', data)
|
||||
s.close()
|
||||
log.info('check finished')
|
||||
|
@ -15,32 +15,32 @@ import socket
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Hosts fetcher for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Hosts fetcher for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='write debug messages to stdout instead of log file'),
|
||||
help='write debug messages to stdout instead of log file'),
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/fetcher.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read config
|
||||
spec = '''
|
||||
proxyurl = string(default='http://localhost:4444/')
|
||||
log_file = string(default='/var/log/py-i2phosts/fetcher.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
'''
|
||||
proxyurl = string(default='http://localhost:4444/')
|
||||
log_file = string(default='/var/log/py-i2phosts/fetcher.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
'''
|
||||
spec = spec.split('\n')
|
||||
config = configobj.ConfigObj(args.config_file, configspec=spec, file_error=True)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.lib.utils import get_logger
|
||||
@ -52,11 +52,11 @@ validate_config(config)
|
||||
|
||||
# configure logger
|
||||
if args.debug == True:
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
else:
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log = get_logger(filename=log_file, log_level=log_level)
|
||||
|
||||
# we want open urls through proxy
|
||||
@ -66,70 +66,70 @@ opener = urllib2.build_opener(proxy_handler)
|
||||
all_sources = ExternalSource.objects.filter(active=True)
|
||||
|
||||
for source in all_sources:
|
||||
log.debug('%s: starting work', source.name)
|
||||
if source.last_modified:
|
||||
last_modified = source.last_modified.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
# prevent redownloading of hosts-file by passing If-Modified-Since http header
|
||||
opener.addheaders = [('If-Modified-Since', last_modified)]
|
||||
log.debug('%s: appending If-Modified-Since: %s', source.name, last_modified)
|
||||
if source.etag:
|
||||
opener.addheaders = [('If-None-Match', source.etag)]
|
||||
log.debug('%s: appending If-None-Match: %s', source.name, source.etag)
|
||||
try:
|
||||
log.debug('%s: sending GET...', source.name)
|
||||
resp = opener.open(source.url, timeout=60)
|
||||
except socket.timeout:
|
||||
log.warning('%s: socket timeout', source.name)
|
||||
continue
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code == 304:
|
||||
log.info('%s: not modified', source.name)
|
||||
source.last_success = datetime.datetime.utcnow()
|
||||
source.save()
|
||||
else:
|
||||
log.warning('%s: can\'t finish the request, error code: %s, reason: %s', source.name, e.code, e.reason)
|
||||
continue
|
||||
except urllib2.URLError, e:
|
||||
log.warning('%s: failed to reach server, reason: %s', source.name, e.reason)
|
||||
continue
|
||||
# read data from remote and write it to local file
|
||||
try:
|
||||
log.debug('%s: reading response data', source.name)
|
||||
content = resp.read()
|
||||
except:
|
||||
log.warning('%s: failed to read data', source.name)
|
||||
continue
|
||||
# save fetched content into temporary file
|
||||
fd, tmpfile = tempfile.mkstemp(text=True)
|
||||
f = os.fdopen(fd, 'w')
|
||||
f.write(content)
|
||||
f.close()
|
||||
# get last-modified info from header
|
||||
lm = resp.headers.get('Last-Modified')
|
||||
if lm:
|
||||
log.debug('%s: Last-Modified: %s', source.name, lm)
|
||||
source.last_modified = datetime.datetime.strptime(lm, '%a, %d %b %Y %H:%M:%S GMT')
|
||||
# get ETag
|
||||
etag = resp.headers.get('ETag')
|
||||
if etag:
|
||||
log.debug('%s: ETag: %s', source.name, etag)
|
||||
source.etag = etag
|
||||
# form command-line for invoke injector
|
||||
log.info('%s: adding hosts...', source.name)
|
||||
sp_args = ['py-i2phosts-injector', '-s', '-f', tmpfile, '-d',
|
||||
'Auto-added from ' + source.name]
|
||||
try:
|
||||
p = subprocess.Popen(sp_args, shell=False, stdin=None,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
except OSError, e:
|
||||
log.error('failed to exec py-i2phosts-injector: %s', e)
|
||||
if e.errno == errno.ENOENT:
|
||||
log.error('check your PATH environment variable')
|
||||
sys.exit(1)
|
||||
out = p.communicate()[0]
|
||||
os.remove(tmpfile)
|
||||
log.info('%s: injector output: \n%s', source.name, out)
|
||||
# update last_success
|
||||
source.last_success = datetime.datetime.utcnow()
|
||||
log.debug('%s: updating last_success timestamp: %s', source.name, source.last_success)
|
||||
source.save()
|
||||
log.debug('%s: starting work', source.name)
|
||||
if source.last_modified:
|
||||
last_modified = source.last_modified.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
||||
# prevent redownloading of hosts-file by passing If-Modified-Since http header
|
||||
opener.addheaders = [('If-Modified-Since', last_modified)]
|
||||
log.debug('%s: appending If-Modified-Since: %s', source.name, last_modified)
|
||||
if source.etag:
|
||||
opener.addheaders = [('If-None-Match', source.etag)]
|
||||
log.debug('%s: appending If-None-Match: %s', source.name, source.etag)
|
||||
try:
|
||||
log.debug('%s: sending GET...', source.name)
|
||||
resp = opener.open(source.url, timeout=60)
|
||||
except socket.timeout:
|
||||
log.warning('%s: socket timeout', source.name)
|
||||
continue
|
||||
except urllib2.HTTPError, e:
|
||||
if e.code == 304:
|
||||
log.info('%s: not modified', source.name)
|
||||
source.last_success = datetime.datetime.utcnow()
|
||||
source.save()
|
||||
else:
|
||||
log.warning('%s: can\'t finish the request, error code: %s, reason: %s', source.name, e.code, e.reason)
|
||||
continue
|
||||
except urllib2.URLError, e:
|
||||
log.warning('%s: failed to reach server, reason: %s', source.name, e.reason)
|
||||
continue
|
||||
# read data from remote and write it to local file
|
||||
try:
|
||||
log.debug('%s: reading response data', source.name)
|
||||
content = resp.read()
|
||||
except:
|
||||
log.warning('%s: failed to read data', source.name)
|
||||
continue
|
||||
# save fetched content into temporary file
|
||||
fd, tmpfile = tempfile.mkstemp(text=True)
|
||||
f = os.fdopen(fd, 'w')
|
||||
f.write(content)
|
||||
f.close()
|
||||
# get last-modified info from header
|
||||
lm = resp.headers.get('Last-Modified')
|
||||
if lm:
|
||||
log.debug('%s: Last-Modified: %s', source.name, lm)
|
||||
source.last_modified = datetime.datetime.strptime(lm, '%a, %d %b %Y %H:%M:%S GMT')
|
||||
# get ETag
|
||||
etag = resp.headers.get('ETag')
|
||||
if etag:
|
||||
log.debug('%s: ETag: %s', source.name, etag)
|
||||
source.etag = etag
|
||||
# form command-line for invoke injector
|
||||
log.info('%s: adding hosts...', source.name)
|
||||
sp_args = ['py-i2phosts-injector', '-s', '-f', tmpfile, '-d',
|
||||
'Auto-added from ' + source.name]
|
||||
try:
|
||||
p = subprocess.Popen(sp_args, shell=False, stdin=None,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
except OSError, e:
|
||||
log.error('failed to exec py-i2phosts-injector: %s', e)
|
||||
if e.errno == errno.ENOENT:
|
||||
log.error('check your PATH environment variable')
|
||||
sys.exit(1)
|
||||
out = p.communicate()[0]
|
||||
os.remove(tmpfile)
|
||||
log.info('%s: injector output: \n%s', source.name, out)
|
||||
# update last_success
|
||||
source.last_success = datetime.datetime.utcnow()
|
||||
log.debug('%s: updating last_success timestamp: %s', source.name, source.last_success)
|
||||
source.save()
|
||||
|
@ -10,34 +10,34 @@ from django.core.exceptions import ValidationError
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Hosts injector for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Hosts injector for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/injector.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
parser.add_argument('-f', '--file', dest='hostsfile',
|
||||
help='hosts.txt for parsing')
|
||||
help='hosts.txt for parsing')
|
||||
parser.add_argument('-d', '--description', default='Auto-added from external hosts.txt',
|
||||
help='provide custom description message')
|
||||
help='provide custom description message')
|
||||
parser.add_argument('-a', '--approve', action='store_true',
|
||||
help='add hosts as approved')
|
||||
help='add hosts as approved')
|
||||
parser.add_argument('-s', '--supress', action='store_true',
|
||||
help='supress warnings about already existed hostnames'),
|
||||
help='supress warnings about already existed hostnames'),
|
||||
parser.add_argument('-q', '--quiet', action='store_true',
|
||||
help='be completely quiet, print only errors')
|
||||
help='be completely quiet, print only errors')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read config
|
||||
config = configobj.ConfigObj(args.config_file)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.postkey.models import i2phost
|
||||
@ -46,57 +46,57 @@ from pyi2phosts.lib.validation import validate_b64hash
|
||||
|
||||
# determine approve hosts or not
|
||||
if args.approve or config.as_bool('approve'):
|
||||
approved = True
|
||||
approved = True
|
||||
else:
|
||||
approved = False
|
||||
approved = False
|
||||
|
||||
# turn on output supressing if quiet
|
||||
if args.quiet:
|
||||
args.supress = True
|
||||
args.supress = True
|
||||
|
||||
# determine what hosts.txt file we should parse
|
||||
if args.hostsfile:
|
||||
hostsfile = args.hostsfile
|
||||
hostsfile = args.hostsfile
|
||||
else:
|
||||
env = os.environ
|
||||
if 'HOME' in env:
|
||||
hostsfile = os.environ['HOME'] + '/.i2p/hosts.txt'
|
||||
else:
|
||||
sys.stderr.write('unable to determine hosts file for parsing\n')
|
||||
sys.exit(1)
|
||||
env = os.environ
|
||||
if 'HOME' in env:
|
||||
hostsfile = os.environ['HOME'] + '/.i2p/hosts.txt'
|
||||
else:
|
||||
sys.stderr.write('unable to determine hosts file for parsing\n')
|
||||
sys.exit(1)
|
||||
|
||||
f = open(args.hostsfile, 'r')
|
||||
for line in f:
|
||||
# ignore comments and empty lines
|
||||
if line.startswith('#') or line.isspace():
|
||||
continue
|
||||
if line.find('=') == -1:
|
||||
sys.stdout.write('Invalid line: %s\n' % line)
|
||||
continue
|
||||
# strip trailing '\n'
|
||||
line = line.rstrip('\n')
|
||||
entry = line.split('=', 1)
|
||||
try:
|
||||
hostname = validate_hostname(entry[0])
|
||||
base64 = validate_b64hash(entry[1], check_uniq=False) # don't require uniqueness
|
||||
except ValidationError, e:
|
||||
sys.stdout.write('validation error: %s: %s\n\n' % (e, line))
|
||||
else:
|
||||
# Check for already existed hosts in database to avoid unneeded INSERTs
|
||||
# beacuse they will fail anyway.
|
||||
try:
|
||||
h = i2phost.objects.get(name=hostname)
|
||||
except i2phost.DoesNotExist:
|
||||
if not args.quiet:
|
||||
sys.stdout.write('Adding %s\n' % hostname)
|
||||
host = i2phost(name=hostname, b64hash=base64,
|
||||
description=args.description,
|
||||
date_added=datetime.datetime.utcnow(),
|
||||
activated=False, external=True, approved=approved)
|
||||
host.save()
|
||||
else:
|
||||
if not args.supress:
|
||||
sys.stdout.write('Host %s already exists\n' % hostname)
|
||||
if h.b64hash != base64:
|
||||
sys.stdout.write('Key conflict for host: %s\n' % hostname)
|
||||
# ignore comments and empty lines
|
||||
if line.startswith('#') or line.isspace():
|
||||
continue
|
||||
if line.find('=') == -1:
|
||||
sys.stdout.write('Invalid line: %s\n' % line)
|
||||
continue
|
||||
# strip trailing '\n'
|
||||
line = line.rstrip('\n')
|
||||
entry = line.split('=', 1)
|
||||
try:
|
||||
hostname = validate_hostname(entry[0])
|
||||
base64 = validate_b64hash(entry[1], check_uniq=False) # don't require uniqueness
|
||||
except ValidationError, e:
|
||||
sys.stdout.write('validation error: %s: %s\n\n' % (e, line))
|
||||
else:
|
||||
# Check for already existed hosts in database to avoid unneeded INSERTs
|
||||
# beacuse they will fail anyway.
|
||||
try:
|
||||
h = i2phost.objects.get(name=hostname)
|
||||
except i2phost.DoesNotExist:
|
||||
if not args.quiet:
|
||||
sys.stdout.write('Adding %s\n' % hostname)
|
||||
host = i2phost(name=hostname, b64hash=base64,
|
||||
description=args.description,
|
||||
date_added=datetime.datetime.utcnow(),
|
||||
activated=False, external=True, approved=approved)
|
||||
host.save()
|
||||
else:
|
||||
if not args.supress:
|
||||
sys.stdout.write('Host %s already exists\n' % hostname)
|
||||
if h.b64hash != base64:
|
||||
sys.stdout.write('Key conflict for host: %s\n' % hostname)
|
||||
f.close()
|
||||
|
@ -10,37 +10,37 @@ import configobj
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Hosts maintainer for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Hosts maintainer for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='write debug messages to stdout')
|
||||
help='write debug messages to stdout')
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/maintainer.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read config
|
||||
spec = '''
|
||||
log_file = string(default='/var/log/py-i2phosts/maintainer.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
external_inactive_max = integer(default=365)
|
||||
internal_inactive_max = integer(default=14)
|
||||
external_expires = integer(default=30)
|
||||
internal_expires = integer(default=30)
|
||||
activate_min_delay = integer(default=3)
|
||||
keep_expired = integer(default=730)
|
||||
'''
|
||||
log_file = string(default='/var/log/py-i2phosts/maintainer.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
external_inactive_max = integer(default=365)
|
||||
internal_inactive_max = integer(default=14)
|
||||
external_expires = integer(default=30)
|
||||
internal_expires = integer(default=30)
|
||||
activate_min_delay = integer(default=3)
|
||||
keep_expired = integer(default=730)
|
||||
'''
|
||||
spec = spec.split('\n')
|
||||
config = configobj.ConfigObj(args.config_file, configspec=spec)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.postkey.models import i2phost
|
||||
@ -52,68 +52,68 @@ validate_config(config)
|
||||
|
||||
# configure logger
|
||||
if args.debug == True:
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
else:
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log = get_logger(filename=log_file, log_level=log_level)
|
||||
|
||||
all_hosts = i2phost.objects.all()
|
||||
log.info('starting maintenance')
|
||||
for host in all_hosts:
|
||||
# how long host was added
|
||||
dl = datetime.datetime.utcnow() - host.date_added
|
||||
if host.last_seen == None:
|
||||
# delete external hosts which we never seen after X days of inactivity
|
||||
if host.external == True:
|
||||
if dl > datetime.timedelta(days=config['external_inactive_max']):
|
||||
log.info('deleting %s, reason: external host, never seen for %s days',
|
||||
host.name, config['external_inactive_max'])
|
||||
host.delete()
|
||||
continue
|
||||
# delete hosts added by us and never seen after X days of inactivity
|
||||
else:
|
||||
if dl > datetime.timedelta(days=config['internal_inactive_max']):
|
||||
log.info('deleting %s, reason: internal host, never seen for %s days',
|
||||
host.name, config['internal_inactive_max'])
|
||||
host.delete()
|
||||
continue
|
||||
else:
|
||||
# configure registration period for hosts
|
||||
if host.external == True:
|
||||
timedelta = datetime.timedelta(days=config['external_expires'])
|
||||
else:
|
||||
timedelta = datetime.timedelta(days=config['internal_expires'])
|
||||
# get current host expiration date from database
|
||||
if host.expires == None:
|
||||
# workaround for situation when we updating expires first time
|
||||
expires_current = datetime.datetime.utcnow().date()
|
||||
else:
|
||||
expires_current = host.expires
|
||||
# calculate new expiration date
|
||||
expires_new = host.last_seen + timedelta
|
||||
expires_new = expires_new.date()
|
||||
# update expiration date only if changed
|
||||
if expires_new > expires_current:
|
||||
log.debug('updating expires for %s', host.name)
|
||||
host.expires = expires_new
|
||||
# deactivate if expired
|
||||
min_dl = datetime.timedelta(days=config['activate_min_delay'])
|
||||
if host.expires < datetime.datetime.utcnow().date():
|
||||
if host.activated == True:
|
||||
log.info('deactivating %s, reason: expired', host.name)
|
||||
host.activated = False
|
||||
# if not expired and added more than X days ago and approved then activate
|
||||
elif dl > min_dl and host.activated == False and host.approved == True:
|
||||
log.info('activating %s, reason: host up and added more than %s days ago',
|
||||
host.name, config['activate_min_delay'])
|
||||
host.activated = True
|
||||
# if expired X days ago then delete
|
||||
dl_e = datetime.datetime.utcnow().date() - host.expires
|
||||
if dl_e > datetime.timedelta(days=config['keep_expired']):
|
||||
log.info('deleting %s, reason: expired %s days ago',
|
||||
host.name, config['keep_expired'])
|
||||
host.delete()
|
||||
continue
|
||||
host.save()
|
||||
# how long host was added
|
||||
dl = datetime.datetime.utcnow() - host.date_added
|
||||
if host.last_seen == None:
|
||||
# delete external hosts which we never seen after X days of inactivity
|
||||
if host.external == True:
|
||||
if dl > datetime.timedelta(days=config['external_inactive_max']):
|
||||
log.info('deleting %s, reason: external host, never seen for %s days',
|
||||
host.name, config['external_inactive_max'])
|
||||
host.delete()
|
||||
continue
|
||||
# delete hosts added by us and never seen after X days of inactivity
|
||||
else:
|
||||
if dl > datetime.timedelta(days=config['internal_inactive_max']):
|
||||
log.info('deleting %s, reason: internal host, never seen for %s days',
|
||||
host.name, config['internal_inactive_max'])
|
||||
host.delete()
|
||||
continue
|
||||
else:
|
||||
# configure registration period for hosts
|
||||
if host.external == True:
|
||||
timedelta = datetime.timedelta(days=config['external_expires'])
|
||||
else:
|
||||
timedelta = datetime.timedelta(days=config['internal_expires'])
|
||||
# get current host expiration date from database
|
||||
if host.expires == None:
|
||||
# workaround for situation when we updating expires first time
|
||||
expires_current = datetime.datetime.utcnow().date()
|
||||
else:
|
||||
expires_current = host.expires
|
||||
# calculate new expiration date
|
||||
expires_new = host.last_seen + timedelta
|
||||
expires_new = expires_new.date()
|
||||
# update expiration date only if changed
|
||||
if expires_new > expires_current:
|
||||
log.debug('updating expires for %s', host.name)
|
||||
host.expires = expires_new
|
||||
# deactivate if expired
|
||||
min_dl = datetime.timedelta(days=config['activate_min_delay'])
|
||||
if host.expires < datetime.datetime.utcnow().date():
|
||||
if host.activated == True:
|
||||
log.info('deactivating %s, reason: expired', host.name)
|
||||
host.activated = False
|
||||
# if not expired and added more than X days ago and approved then activate
|
||||
elif dl > min_dl and host.activated == False and host.approved == True:
|
||||
log.info('activating %s, reason: host up and added more than %s days ago',
|
||||
host.name, config['activate_min_delay'])
|
||||
host.activated = True
|
||||
# if expired X days ago then delete
|
||||
dl_e = datetime.datetime.utcnow().date() - host.expires
|
||||
if dl_e > datetime.timedelta(days=config['keep_expired']):
|
||||
log.info('deleting %s, reason: expired %s days ago',
|
||||
host.name, config['keep_expired'])
|
||||
host.delete()
|
||||
continue
|
||||
host.save()
|
||||
|
@ -13,119 +13,125 @@ import threading
|
||||
import daemon
|
||||
# workaround for python-daemon >= 1.6
|
||||
try:
|
||||
<<<<<<< HEAD
|
||||
import daemon.pidlockfile as pidfile
|
||||
except ImportError:
|
||||
import daemon.pidfile as pidfile
|
||||
=======
|
||||
import daemon.pidlockfile as pidfile
|
||||
except ImportError:
|
||||
import daemon.pidfile as pidfile
|
||||
>>>>>>> 923b94f... Convert tabs to spaces
|
||||
|
||||
class Thread(threading.Thread):
|
||||
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
|
||||
|
||||
class FetcherThread(Thread):
|
||||
""" Run py-i2phosts-fetcher periodically """
|
||||
""" Run py-i2phosts-fetcher periodically """
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
run_prog('py-i2phosts-fetcher')
|
||||
time.sleep(float(config['fetch_interval']))
|
||||
def run(self):
|
||||
while True:
|
||||
run_prog('py-i2phosts-fetcher')
|
||||
time.sleep(float(config['fetch_interval']))
|
||||
|
||||
|
||||
class CheckerThread(Thread):
|
||||
""" Run py-i2phosts-checker, py-i2phosts-maint, py-i2phosts-builder periodically """
|
||||
""" Run py-i2phosts-checker, py-i2phosts-maint, py-i2phosts-builder periodically """
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
run_prog('py-i2phosts-checker')
|
||||
run_prog('py-i2phosts-maint')
|
||||
run_prog('py-i2phosts-builder')
|
||||
time.sleep(float(config['check_interval']))
|
||||
def run(self):
|
||||
while True:
|
||||
run_prog('py-i2phosts-checker')
|
||||
run_prog('py-i2phosts-maint')
|
||||
run_prog('py-i2phosts-builder')
|
||||
time.sleep(float(config['check_interval']))
|
||||
|
||||
|
||||
def run_prog(prog):
|
||||
try:
|
||||
log.info('starting: %s', prog)
|
||||
sp_args = [prog]
|
||||
if args.debug or args.verbose:
|
||||
sp_args.append('-d')
|
||||
p = subprocess.Popen(sp_args, shell=False)
|
||||
except OSError, e:
|
||||
log.error('failed to exec %s: %s', prog, e)
|
||||
if e.errno == errno.ENOENT:
|
||||
log.error(' maybe it isn\'t in PATH?')
|
||||
else:
|
||||
p.wait()
|
||||
log.info('finished: %s', prog)
|
||||
try:
|
||||
log.info('starting: %s', prog)
|
||||
sp_args = [prog]
|
||||
if args.debug or args.verbose:
|
||||
sp_args.append('-d')
|
||||
p = subprocess.Popen(sp_args, shell=False)
|
||||
except OSError, e:
|
||||
log.error('failed to exec %s: %s', prog, e)
|
||||
if e.errno == errno.ENOENT:
|
||||
log.error(' maybe it isn\'t in PATH?')
|
||||
else:
|
||||
p.wait()
|
||||
log.info('finished: %s', prog)
|
||||
|
||||
|
||||
def main():
|
||||
def run_fetcher():
|
||||
fetcher = FetcherThread()
|
||||
fetcher.start()
|
||||
return fetcher
|
||||
def run_fetcher():
|
||||
fetcher = FetcherThread()
|
||||
fetcher.start()
|
||||
return fetcher
|
||||
|
||||
def run_checker():
|
||||
checker = CheckerThread()
|
||||
checker.start()
|
||||
return checker
|
||||
def run_checker():
|
||||
checker = CheckerThread()
|
||||
checker.start()
|
||||
return checker
|
||||
|
||||
# if we're just started, wait while fetcher get some new hosts
|
||||
fetcher = run_fetcher()
|
||||
log.debug('just started, delaying checker run for 300 secs')
|
||||
fetcher.join(300) # wait for 5 mins
|
||||
# start checker and other
|
||||
checker = run_checker()
|
||||
# if we're just started, wait while fetcher get some new hosts
|
||||
fetcher = run_fetcher()
|
||||
log.debug('just started, delaying checker run for 300 secs')
|
||||
fetcher.join(300) # wait for 5 mins
|
||||
# start checker and other
|
||||
checker = run_checker()
|
||||
|
||||
while True:
|
||||
log.debug('checking fetcher and checker threads status')
|
||||
if fetcher.isAlive() == False:
|
||||
log.warning('fetcher thread is dead, respawning...')
|
||||
fetcher = run_fetcher()
|
||||
else:
|
||||
log.debug('fetcher thread: alive')
|
||||
if checker.isAlive() == False:
|
||||
log.warning('checker thread is dead, respawning...')
|
||||
checker = run_checker()
|
||||
else:
|
||||
log.debug('checker thread: alive')
|
||||
# do check every 30 mins
|
||||
time.sleep(1800)
|
||||
while True:
|
||||
log.debug('checking fetcher and checker threads status')
|
||||
if fetcher.isAlive() == False:
|
||||
log.warning('fetcher thread is dead, respawning...')
|
||||
fetcher = run_fetcher()
|
||||
else:
|
||||
log.debug('fetcher thread: alive')
|
||||
if checker.isAlive() == False:
|
||||
log.warning('checker thread is dead, respawning...')
|
||||
checker = run_checker()
|
||||
else:
|
||||
log.debug('checker thread: alive')
|
||||
# do check every 30 mins
|
||||
time.sleep(1800)
|
||||
|
||||
# parse command line options
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Master daemon for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
description='Master daemon for py-i2phosts.',
|
||||
epilog='Report bugs to http://zzz.i2p/topics/733')
|
||||
parser.add_argument('-d', '--debug', action='store_true',
|
||||
help='run in debug mode without detaching from terminal'),
|
||||
help='run in debug mode without detaching from terminal'),
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='run in verbose mode without detaching from terminal'),
|
||||
help='run in verbose mode without detaching from terminal'),
|
||||
parser.add_argument('-c', '--config', default='/etc/py-i2phosts/master.conf', dest='config_file',
|
||||
help='config file to use')
|
||||
help='config file to use')
|
||||
args = parser.parse_args()
|
||||
|
||||
# read and validate config
|
||||
spec = '''
|
||||
log_file = string(default='/var/log/py-i2phosts/master.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
pid_file = string(default='/var/run/py-i2phosts/master.pid')
|
||||
runas = string(default='_pyi2phosts')
|
||||
check_interval = integer(default=43200)
|
||||
fetch_interval = integer(default=1800)
|
||||
'''
|
||||
log_file = string(default='/var/log/py-i2phosts/master.log')
|
||||
log_level = option('debug', 'info', 'warning', 'error', 'critical', default='info')
|
||||
pid_file = string(default='/var/run/py-i2phosts/master.pid')
|
||||
runas = string(default='_pyi2phosts')
|
||||
check_interval = integer(default=43200)
|
||||
fetch_interval = integer(default=1800)
|
||||
'''
|
||||
spec = spec.split('\n')
|
||||
config = configobj.ConfigObj(args.config_file, configspec=spec)
|
||||
if 'include' in config:
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
config_included = configobj.ConfigObj(config['include'])
|
||||
config.merge(config_included)
|
||||
|
||||
# django setup
|
||||
DJANGO_SETTINGS_MODULE = 'pyi2phosts.settings'
|
||||
if 'DJANGO_PROJECT_PATH' in config:
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
DJANGO_PROJECT_PATH = config['DJANGO_PROJECT_PATH']
|
||||
else:
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
DJANGO_PROJECT_PATH = os.path.dirname(sys.argv[0]) + '/..'
|
||||
sys.path.insert(1, DJANGO_PROJECT_PATH)
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = DJANGO_SETTINGS_MODULE
|
||||
from pyi2phosts.lib.utils import get_logger
|
||||
@ -136,32 +142,32 @@ validate_config(config)
|
||||
|
||||
# configure logger
|
||||
if args.debug == True:
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
log_level = 'debug'
|
||||
log_file = None
|
||||
elif args.verbose == True:
|
||||
log_level = 'info'
|
||||
log_file = None
|
||||
log_level = 'info'
|
||||
log_file = None
|
||||
else:
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
log_level = config['log_level']
|
||||
log_file = config['log_file']
|
||||
|
||||
if not args.debug and not args.verbose:
|
||||
# get pid object for daemon
|
||||
pid = pidfile.TimeoutPIDLockFile(config['pid_file'], 10)
|
||||
# create daemon context
|
||||
d = daemon.DaemonContext(pidfile=pid, umask=077)
|
||||
# write stderr to logfile # FIXME: and how we will deal with log rotation?
|
||||
logfile = open(config['log_file'], 'a')
|
||||
d.stderr = logfile
|
||||
d.stdout = logfile
|
||||
# drop privileges when started as root
|
||||
if os.getuid() == 0:
|
||||
runas = config['runas']
|
||||
pw_entry = pwd.getpwnam(runas)
|
||||
d.uid = pw_entry[2]
|
||||
d.gid = pw_entry[3]
|
||||
os.chown(config['log_file'], d.uid, d.gid)
|
||||
d.open() # become daemon
|
||||
# get pid object for daemon
|
||||
pid = pidfile.TimeoutPIDLockFile(config['pid_file'], 10)
|
||||
# create daemon context
|
||||
d = daemon.DaemonContext(pidfile=pid, umask=077)
|
||||
# write stderr to logfile # FIXME: and how we will deal with log rotation?
|
||||
logfile = open(config['log_file'], 'a')
|
||||
d.stderr = logfile
|
||||
d.stdout = logfile
|
||||
# drop privileges when started as root
|
||||
if os.getuid() == 0:
|
||||
runas = config['runas']
|
||||
pw_entry = pwd.getpwnam(runas)
|
||||
d.uid = pw_entry[2]
|
||||
d.gid = pw_entry[3]
|
||||
os.chown(config['log_file'], d.uid, d.gid)
|
||||
d.open() # become daemon
|
||||
log = get_logger(filename=log_file, log_level=log_level)
|
||||
log.info('started')
|
||||
main()
|
||||
|
@ -1,5 +1,5 @@
|
||||
from django.conf.urls import *
|
||||
|
||||
urlpatterns = patterns('pyi2phosts.api.views',
|
||||
url(r'^all/$', 'all'),
|
||||
url(r'^all/$', 'all'),
|
||||
)
|
||||
|
@ -6,11 +6,11 @@ from pyi2phosts.postkey.models import i2phost
|
||||
from pyi2phosts.lib.utils import get_b32
|
||||
|
||||
def all(request):
|
||||
"""Return all hosts in { "b32": "last seen timestamp" } form. Implemented by zzz request. """
|
||||
# all hosts seen at least once
|
||||
queryset = i2phost.objects.exclude(last_seen=None)
|
||||
json_dict = {}
|
||||
for host in queryset:
|
||||
# pass last_seen to json in unixtime
|
||||
json_dict[get_b32(host.b64hash)] = host.last_seen.strftime("%s")
|
||||
return HttpResponse(json.dumps(json_dict), mimetype="application/json")
|
||||
"""Return all hosts in { "b32": "last seen timestamp" } form. Implemented by zzz request. """
|
||||
# all hosts seen at least once
|
||||
queryset = i2phost.objects.exclude(last_seen=None)
|
||||
json_dict = {}
|
||||
for host in queryset:
|
||||
# pass last_seen to json in unixtime
|
||||
json_dict[get_b32(host.b64hash)] = host.last_seen.strftime("%s")
|
||||
return HttpResponse(json.dumps(json_dict), mimetype="application/json")
|
||||
|
@ -4,7 +4,7 @@ from pyi2phosts.extsources.models import ExternalSource
|
||||
|
||||
|
||||
class ExternalSourceAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'url', 'description', 'last_success', 'last_modified', 'etag', 'active')
|
||||
list_editable = ['active']
|
||||
list_display = ('name', 'url', 'description', 'last_success', 'last_modified', 'etag', 'active')
|
||||
list_editable = ['active']
|
||||
|
||||
admin.site.register(ExternalSource, ExternalSourceAdmin)
|
||||
|
@ -3,14 +3,14 @@ from django.db import models
|
||||
from pyi2phosts.lib.validation import validate_i2purl
|
||||
|
||||
class ExternalSource(models.Model):
|
||||
name = models.CharField(max_length=128, unique=True)
|
||||
url = models.CharField(max_length=256, validators=[validate_i2purl])
|
||||
description = models.CharField(max_length=512, blank=True)
|
||||
last_modified = models.DateTimeField(null=True, blank=True)
|
||||
last_success = models.DateTimeField(null=True, blank=True)
|
||||
etag = models.CharField(max_length=256, blank=True)
|
||||
active = models.BooleanField(default=True)
|
||||
name = models.CharField(max_length=128, unique=True)
|
||||
url = models.CharField(max_length=256, validators=[validate_i2purl])
|
||||
description = models.CharField(max_length=512, blank=True)
|
||||
last_modified = models.DateTimeField(null=True, blank=True)
|
||||
last_success = models.DateTimeField(null=True, blank=True)
|
||||
etag = models.CharField(max_length=256, blank=True)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.conf.urls import *
|
||||
|
||||
urlpatterns = patterns('pyi2phosts.jump.views',
|
||||
(r'^([^$/]+)', 'jumper'),
|
||||
(r'', 'index'),
|
||||
(r'^([^$/]+)', 'jumper'),
|
||||
(r'', 'index'),
|
||||
|
||||
)
|
||||
|
@ -10,45 +10,45 @@ from pyi2phosts.postkey.models import i2phost
|
||||
from pyi2phosts.lib.validation import validate_hostname
|
||||
|
||||
def jumper(request, host):
|
||||
"""Actually do jumps."""
|
||||
try:
|
||||
hostname = validate_hostname(host)
|
||||
except ValidationError, e:
|
||||
return render_to_response('jump-error.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'error': e,
|
||||
}, context_instance=RequestContext(request))
|
||||
try:
|
||||
h = i2phost.objects.get(name=hostname)
|
||||
except i2phost.DoesNotExist:
|
||||
return render_to_response('jump-unknown.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
}, context_instance=RequestContext(request))
|
||||
if h.activated == True:
|
||||
key = h.b64hash
|
||||
else:
|
||||
return redirect('/search/?q=' + hostname)
|
||||
# begin forming url
|
||||
url = 'http://' + hostname
|
||||
# get params from requst string, e.g. from 'example.i2p/smth/1?a=b&c=d' get 'smth/1?a=b&c=d'
|
||||
pattern = host + r'/(.+)'
|
||||
m = re.search(pattern, request.get_full_path())
|
||||
if m:
|
||||
params = m.group(1)
|
||||
url += '/' + params
|
||||
# determine how we should pass i2paddresshelper
|
||||
# http://zzz.i2p/oldnews.html#jump
|
||||
if params.find('?') == -1:
|
||||
suffix = '?'
|
||||
else:
|
||||
suffix = '&'
|
||||
url += suffix + 'i2paddresshelper=' + key
|
||||
else:
|
||||
url += '/?i2paddresshelper=' + key
|
||||
return render_to_response('jump.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'url': url,
|
||||
}, context_instance=RequestContext(request))
|
||||
"""Actually do jumps."""
|
||||
try:
|
||||
hostname = validate_hostname(host)
|
||||
except ValidationError, e:
|
||||
return render_to_response('jump-error.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'error': e,
|
||||
}, context_instance=RequestContext(request))
|
||||
try:
|
||||
h = i2phost.objects.get(name=hostname)
|
||||
except i2phost.DoesNotExist:
|
||||
return render_to_response('jump-unknown.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
}, context_instance=RequestContext(request))
|
||||
if h.activated == True:
|
||||
key = h.b64hash
|
||||
else:
|
||||
return redirect('/search/?q=' + hostname)
|
||||
# begin forming url
|
||||
url = 'http://' + hostname
|
||||
# get params from requst string, e.g. from 'example.i2p/smth/1?a=b&c=d' get 'smth/1?a=b&c=d'
|
||||
pattern = host + r'/(.+)'
|
||||
m = re.search(pattern, request.get_full_path())
|
||||
if m:
|
||||
params = m.group(1)
|
||||
url += '/' + params
|
||||
# determine how we should pass i2paddresshelper
|
||||
# http://zzz.i2p/oldnews.html#jump
|
||||
if params.find('?') == -1:
|
||||
suffix = '?'
|
||||
else:
|
||||
suffix = '&'
|
||||
url += suffix + 'i2paddresshelper=' + key
|
||||
else:
|
||||
url += '/?i2paddresshelper=' + key
|
||||
return render_to_response('jump.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'url': url,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def index(request):
|
||||
return redirect('/')
|
||||
return redirect('/')
|
||||
|
@ -3,7 +3,7 @@ from pyi2phosts.lib.rss import LatestHostsFeed
|
||||
from pyi2phosts.latest.views import LatestHostsListsView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', LatestHostsListsView.as_view(), name='latest'),
|
||||
url(r'^rss/$', LatestHostsFeed(), name='latest-rss'),
|
||||
url(r'^$', LatestHostsListsView.as_view(), name='latest'),
|
||||
url(r'^rss/$', LatestHostsFeed(), name='latest-rss'),
|
||||
|
||||
)
|
||||
|
@ -6,25 +6,25 @@ from pyi2phosts.postkey.models import i2phost
|
||||
from pyi2phosts.lib.generic import LocalObjectList
|
||||
|
||||
def get_latest():
|
||||
now_date = datetime.datetime.utcnow()
|
||||
start_date = now_date - datetime.timedelta(days=settings.LATEST_DAY_COUNT)
|
||||
qs = i2phost.objects.filter(activated=True,
|
||||
date_added__range=(start_date, now_date)).order_by("-date_added")[:settings.LATEST_HOSTS_COUNT]
|
||||
return qs
|
||||
now_date = datetime.datetime.utcnow()
|
||||
start_date = now_date - datetime.timedelta(days=settings.LATEST_DAY_COUNT)
|
||||
qs = i2phost.objects.filter(activated=True,
|
||||
date_added__range=(start_date, now_date)).order_by("-date_added")[:settings.LATEST_HOSTS_COUNT]
|
||||
return qs
|
||||
|
||||
class LatestHostsListsView(LocalObjectList):
|
||||
""" Renders list of latest active hosts added """
|
||||
""" Renders list of latest active hosts added """
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LatestHostsListsView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
'day_count': settings.LATEST_DAY_COUNT,
|
||||
'hosts_count': settings.LATEST_HOSTS_COUNT
|
||||
})
|
||||
return context
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LatestHostsListsView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
'day_count': settings.LATEST_DAY_COUNT,
|
||||
'hosts_count': settings.LATEST_HOSTS_COUNT
|
||||
})
|
||||
return context
|
||||
|
||||
queryset = get_latest()
|
||||
template_name = 'latest.html'
|
||||
context_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
queryset = get_latest()
|
||||
template_name = 'latest.html'
|
||||
context_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
|
@ -9,58 +9,58 @@ from pyi2phosts.postkey.models import i2phost
|
||||
from pyi2phosts.postkey.templatetags import paginator
|
||||
|
||||
class LocalTemplateView(TemplateView):
|
||||
""" Renders some template with passing some local config variables """
|
||||
""" Renders some template with passing some local config variables """
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LocalTemplateView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
'domain': settings.DOMAIN,
|
||||
'b64': settings.MY_B64,
|
||||
'b32': get_b32(settings.MY_B64)
|
||||
})
|
||||
return context
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LocalTemplateView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
'domain': settings.DOMAIN,
|
||||
'b64': settings.MY_B64,
|
||||
'b32': get_b32(settings.MY_B64)
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class LocalObjectList(ListView):
|
||||
""" Renders some list of objects """
|
||||
""" Renders some list of objects """
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LocalObjectList, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
})
|
||||
return context
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(LocalObjectList, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'title': settings.SITE_NAME,
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class FaqView(LocalObjectList):
|
||||
""" Renders list of external sources for hosts.txt """
|
||||
""" Renders list of external sources for hosts.txt """
|
||||
|
||||
queryset = ExternalSource.objects.filter(active=True)
|
||||
template_name = 'faq.html'
|
||||
context_object_name = 'sources_list'
|
||||
queryset = ExternalSource.objects.filter(active=True)
|
||||
template_name = 'faq.html'
|
||||
context_object_name = 'sources_list'
|
||||
|
||||
|
||||
class HostsListsView(LocalObjectList):
|
||||
""" Renders list of active hosts """
|
||||
""" Renders list of active hosts """
|
||||
|
||||
def get_queryset(self):
|
||||
allowed_orders = ['name', 'last_seen', 'date_added']
|
||||
self.order_by = self.request.GET.get('order', 'name')
|
||||
if self.order_by not in allowed_orders:
|
||||
self.order_by = 'name'
|
||||
qs = super(HostsListsView, self).get_queryset()
|
||||
return qs.order_by(self.order_by)
|
||||
def get_queryset(self):
|
||||
allowed_orders = ['name', 'last_seen', 'date_added']
|
||||
self.order_by = self.request.GET.get('order', 'name')
|
||||
if self.order_by not in allowed_orders:
|
||||
self.order_by = 'name'
|
||||
qs = super(HostsListsView, self).get_queryset()
|
||||
return qs.order_by(self.order_by)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
""" we should pass order_by to template to not lose it while paginating """
|
||||
context = super(LocalObjectList, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'order': self.order_by,
|
||||
})
|
||||
return context
|
||||
def get_context_data(self, **kwargs):
|
||||
""" we should pass order_by to template to not lose it while paginating """
|
||||
context = super(LocalObjectList, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'order': self.order_by,
|
||||
})
|
||||
return context
|
||||
|
||||
queryset = i2phost.objects.filter(activated=True)
|
||||
template_name = 'browse.html'
|
||||
context_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
queryset = i2phost.objects.filter(activated=True)
|
||||
template_name = 'browse.html'
|
||||
context_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
|
@ -5,33 +5,33 @@ from pyi2phosts.postkey.models import i2phost
|
||||
from pyi2phosts.latest.views import get_latest
|
||||
|
||||
class AliveHostsFeed(Feed):
|
||||
""" Generate RSS feed with all alive hosts """
|
||||
""" Generate RSS feed with all alive hosts """
|
||||
|
||||
title = settings.DOMAIN + ' alive hosts'
|
||||
# FIXME: make this URL more dynamic
|
||||
link = 'http://' + settings.DOMAIN + '/browse/'
|
||||
description = 'All known active hosts inside I2P'
|
||||
title = settings.DOMAIN + ' alive hosts'
|
||||
# FIXME: make this URL more dynamic
|
||||
link = 'http://' + settings.DOMAIN + '/browse/'
|
||||
description = 'All known active hosts inside I2P'
|
||||
|
||||
def items(self):
|
||||
return i2phost.objects.filter(activated=True).order_by('name')
|
||||
def items(self):
|
||||
return i2phost.objects.filter(activated=True).order_by('name')
|
||||
|
||||
def item_title(self, item):
|
||||
return item.name
|
||||
def item_title(self, item):
|
||||
return item.name
|
||||
|
||||
def item_link(self, item):
|
||||
return 'http://' + item.name + '/?i2paddresshelper=' + item.b64hash
|
||||
def item_link(self, item):
|
||||
return 'http://' + item.name + '/?i2paddresshelper=' + item.b64hash
|
||||
|
||||
def item_description(self, item):
|
||||
return item.description
|
||||
def item_description(self, item):
|
||||
return item.description
|
||||
|
||||
|
||||
class LatestHostsFeed(AliveHostsFeed):
|
||||
""" Generate RSS feed with freshly added hosts """
|
||||
""" Generate RSS feed with freshly added hosts """
|
||||
|
||||
title = settings.DOMAIN + ' latest hosts'
|
||||
# FIXME: make this URL more dynamic
|
||||
link = 'http://' + settings.DOMAIN + '/latest/'
|
||||
description = 'Freshly added hosts'
|
||||
title = settings.DOMAIN + ' latest hosts'
|
||||
# FIXME: make this URL more dynamic
|
||||
link = 'http://' + settings.DOMAIN + '/latest/'
|
||||
description = 'Freshly added hosts'
|
||||
|
||||
def items(self):
|
||||
return get_latest()
|
||||
def items(self):
|
||||
return get_latest()
|
||||
|
@ -8,59 +8,59 @@ import base64
|
||||
from logging import handlers
|
||||
|
||||
def get_logger(filename=None, log_level='debug'):
|
||||
""" Prepare logger instance for our scripts """
|
||||
""" Prepare logger instance for our scripts """
|
||||
|
||||
# workaround for django
|
||||
if hasattr(logging, "web_logger"):
|
||||
return logging.web_logger
|
||||
# workaround for django
|
||||
if hasattr(logging, "web_logger"):
|
||||
return logging.web_logger
|
||||
|
||||
LEVELS = {
|
||||
'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL
|
||||
}
|
||||
level = LEVELS.get(log_level, logging.NOTSET)
|
||||
format = '%(asctime)s %(module)s:%(lineno)d[%(process)d] %(levelname)s: %(message)s'
|
||||
formatter = logging.Formatter(format)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(level)
|
||||
if filename:
|
||||
handler = logging.handlers.WatchedFileHandler(filename)
|
||||
else:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
LEVELS = {
|
||||
'debug': logging.DEBUG,
|
||||
'info': logging.INFO,
|
||||
'warning': logging.WARNING,
|
||||
'error': logging.ERROR,
|
||||
'critical': logging.CRITICAL
|
||||
}
|
||||
level = LEVELS.get(log_level, logging.NOTSET)
|
||||
format = '%(asctime)s %(module)s:%(lineno)d[%(process)d] %(levelname)s: %(message)s'
|
||||
formatter = logging.Formatter(format)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(level)
|
||||
if filename:
|
||||
handler = logging.handlers.WatchedFileHandler(filename)
|
||||
else:
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
# workaround for django
|
||||
logging.web_logger = logger
|
||||
# workaround for django
|
||||
logging.web_logger = logger
|
||||
|
||||
return logger
|
||||
return logger
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
""" Validate configobj config """
|
||||
validator = validate.Validator()
|
||||
results = config.validate(validator)
|
||||
if results != True:
|
||||
for (section_list, key, _) in configobj.flatten_errors(config, results):
|
||||
if key is not None:
|
||||
sys.stderr.write('The "%s" key in the section "%s" failed validation' %
|
||||
(key, ', '.join(section_list)))
|
||||
else:
|
||||
sys.stderr.write('The following section was missing:%s ' %
|
||||
', '.join(section_list))
|
||||
sys.exit(1)
|
||||
""" Validate configobj config """
|
||||
validator = validate.Validator()
|
||||
results = config.validate(validator)
|
||||
if results != True:
|
||||
for (section_list, key, _) in configobj.flatten_errors(config, results):
|
||||
if key is not None:
|
||||
sys.stderr.write('The "%s" key in the section "%s" failed validation' %
|
||||
(key, ', '.join(section_list)))
|
||||
else:
|
||||
sys.stderr.write('The following section was missing:%s ' %
|
||||
', '.join(section_list))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_b32(dest):
|
||||
""" Calculate base32 hash from base64 """
|
||||
try:
|
||||
raw_key = base64.b64decode(dest.encode('utf-8'), '-~')
|
||||
except TypeError:
|
||||
return 'corrupted_base64_hash'
|
||||
else:
|
||||
hash = hashlib.sha256(raw_key)
|
||||
b32 = base64.b32encode(hash.digest()).lower().replace('=', '')+'.b32.i2p'
|
||||
return b32
|
||||
""" Calculate base32 hash from base64 """
|
||||
try:
|
||||
raw_key = base64.b64decode(dest.encode('utf-8'), '-~')
|
||||
except TypeError:
|
||||
return 'corrupted_base64_hash'
|
||||
else:
|
||||
hash = hashlib.sha256(raw_key)
|
||||
b32 = base64.b32encode(hash.digest()).lower().replace('=', '')+'.b32.i2p'
|
||||
return b32
|
||||
|
@ -9,116 +9,116 @@ from pyi2phosts.postkey.models import i2phost
|
||||
|
||||
|
||||
def validate_hostname(data):
|
||||
"""
|
||||
Here we do hostname validation as described in
|
||||
http://www.i2p2.i2p/naming.html and some additional checks
|
||||
described in http://zzz.i2p/topics/739
|
||||
"""
|
||||
# convert hostname to lowercase and strip leading and trailing whitespaces
|
||||
data = data.lower().strip()
|
||||
# do lenght check here for avoiding django.db.utils.DatabaseError exceptions
|
||||
# when trying to add too long hostname with py-i2phosts-injector
|
||||
if len(data) > 67:
|
||||
raise ValidationError(_('Too long hostname (should be 67 chars max)'))
|
||||
# Must end with '.i2p'.
|
||||
if re.match(r'.*\.i2p$', data) == None:
|
||||
raise ValidationError(_('Hostname doesn\'t ends with .i2p'))
|
||||
# Base 32 hostnames (*.b32.i2p) are not allowed
|
||||
if re.match(r'.*\.b32\.i2p$', data):
|
||||
raise ValidationError(_('Base32 hostnames are not allowed'))
|
||||
# prevent common errors
|
||||
if re.match(r'\.i2p$', data):
|
||||
raise ValidationError(_('Incomplete hostname'))
|
||||
if re.match(r'^http:/', data):
|
||||
raise ValidationError(_('Do not paste full URL, just domain'))
|
||||
# Must not contain '..'
|
||||
if re.search(r'\.\.', data):
|
||||
raise ValidationError(_('".." in hostname'))
|
||||
# Allow only 4ld domains and below
|
||||
if data.count('.') > 3:
|
||||
raise ValidationError(_('Subdomains deeper than 4LD are not allowed'))
|
||||
# Must contain only [a-z] [0-9] '.' and '-'
|
||||
h = re.match(r'([a-z0-9.-]+)\.i2p$', data)
|
||||
if h == None:
|
||||
raise ValidationError(_('Illegal characters in hostname'))
|
||||
else:
|
||||
namepart = h.groups()[0]
|
||||
# Must not start with '.' or '-'
|
||||
if re.match(r'^\.|-', namepart):
|
||||
raise ValidationError(_('Hostname must not starts with "." or "-"'))
|
||||
# Must not contain '.-' or '-.' (as of 0.6.1.33)
|
||||
if re.search(r'(\.-)|(-\.)', namepart):
|
||||
raise ValidationError(_('Hostname contain ".-" or "-."'))
|
||||
# Must not contain '--' except in 'xn--' for IDN
|
||||
if re.search(r'(?<!^xn)--', namepart) and re.search(r'(?<!\.xn)--', namepart):
|
||||
raise ValidationError(_('Hostname contain "--" and it\'s not an IDN'))
|
||||
# Certain hostnames reserved for project use are not allowed
|
||||
if re.search(r'(^|\.)(proxy|router|console|b32|b64)$', namepart):
|
||||
raise ValidationError(_('Trying to use reserved hostname'))
|
||||
# Block various localhost.* in addition
|
||||
if re.match(r'^localhost($|\..*$)', namepart):
|
||||
raise ValidationError(_('localhost.* not allowed'))
|
||||
return data
|
||||
"""
|
||||
Here we do hostname validation as described in
|
||||
http://www.i2p2.i2p/naming.html and some additional checks
|
||||
described in http://zzz.i2p/topics/739
|
||||
"""
|
||||
# convert hostname to lowercase and strip leading and trailing whitespaces
|
||||
data = data.lower().strip()
|
||||
# do lenght check here for avoiding django.db.utils.DatabaseError exceptions
|
||||
# when trying to add too long hostname with py-i2phosts-injector
|
||||
if len(data) > 67:
|
||||
raise ValidationError(_('Too long hostname (should be 67 chars max)'))
|
||||
# Must end with '.i2p'.
|
||||
if re.match(r'.*\.i2p$', data) == None:
|
||||
raise ValidationError(_('Hostname doesn\'t ends with .i2p'))
|
||||
# Base 32 hostnames (*.b32.i2p) are not allowed
|
||||
if re.match(r'.*\.b32\.i2p$', data):
|
||||
raise ValidationError(_('Base32 hostnames are not allowed'))
|
||||
# prevent common errors
|
||||
if re.match(r'\.i2p$', data):
|
||||
raise ValidationError(_('Incomplete hostname'))
|
||||
if re.match(r'^http:/', data):
|
||||
raise ValidationError(_('Do not paste full URL, just domain'))
|
||||
# Must not contain '..'
|
||||
if re.search(r'\.\.', data):
|
||||
raise ValidationError(_('".." in hostname'))
|
||||
# Allow only 4ld domains and below
|
||||
if data.count('.') > 3:
|
||||
raise ValidationError(_('Subdomains deeper than 4LD are not allowed'))
|
||||
# Must contain only [a-z] [0-9] '.' and '-'
|
||||
h = re.match(r'([a-z0-9.-]+)\.i2p$', data)
|
||||
if h == None:
|
||||
raise ValidationError(_('Illegal characters in hostname'))
|
||||
else:
|
||||
namepart = h.groups()[0]
|
||||
# Must not start with '.' or '-'
|
||||
if re.match(r'^\.|-', namepart):
|
||||
raise ValidationError(_('Hostname must not starts with "." or "-"'))
|
||||
# Must not contain '.-' or '-.' (as of 0.6.1.33)
|
||||
if re.search(r'(\.-)|(-\.)', namepart):
|
||||
raise ValidationError(_('Hostname contain ".-" or "-."'))
|
||||
# Must not contain '--' except in 'xn--' for IDN
|
||||
if re.search(r'(?<!^xn)--', namepart) and re.search(r'(?<!\.xn)--', namepart):
|
||||
raise ValidationError(_('Hostname contain "--" and it\'s not an IDN'))
|
||||
# Certain hostnames reserved for project use are not allowed
|
||||
if re.search(r'(^|\.)(proxy|router|console|b32|b64)$', namepart):
|
||||
raise ValidationError(_('Trying to use reserved hostname'))
|
||||
# Block various localhost.* in addition
|
||||
if re.match(r'^localhost($|\..*$)', namepart):
|
||||
raise ValidationError(_('localhost.* not allowed'))
|
||||
return data
|
||||
|
||||
|
||||
def validate_b64hash(data, check_uniq=True):
|
||||
"""
|
||||
Base64 hash validation
|
||||
"""
|
||||
# strip leading and trailing whitespaces
|
||||
data = data.strip()
|
||||
length = len(data)
|
||||
# check for b32 address misuse
|
||||
if re.match(r'.*\.b32\.i2p$', data):
|
||||
raise ValidationError(_('You should paste base64 hash, not a base32!'))
|
||||
# fail if contains .i2p= (full foo.i2p=key)
|
||||
if re.search(r'\.i2p=', data):
|
||||
raise ValidationError(_('Do not paste full hosts.txt entry! Only base64 hash is needed'))
|
||||
# check for pasting router hash
|
||||
if length == 44:
|
||||
raise ValidationError(_('Do not paste router hash! Go to i2ptunnel page and \
|
||||
find a destination hash'))
|
||||
# Minimum key length 516 bytes
|
||||
if length < 516:
|
||||
raise ValidationError(_('Specified base64 hash is less than 516 bytes'))
|
||||
# Maximum key length 616 bytes
|
||||
if length > 616:
|
||||
raise ValidationError(_('Specified base64 hash is bigger than 616 bytes'))
|
||||
# keys with cert may ends with anything, so check is relaxed
|
||||
if length > 516 and re.match(r'[a-zA-Z0-9\-~=]+$', data) == None:
|
||||
raise ValidationError(_('Invalid characters in base64 hash'))
|
||||
# base64-validity test
|
||||
if length > 516:
|
||||
# we need temporary variable here to avoid modifying main "data"
|
||||
test_data = data
|
||||
# add pad-characters needed for proper decoding cos i2p does not
|
||||
for i in range(4):
|
||||
quanta, leftover = divmod(len(test_data), 4)
|
||||
if leftover:
|
||||
test_data += '='
|
||||
else:
|
||||
break
|
||||
# if more than 2 pad chars were added, raise an error
|
||||
if i > 2:
|
||||
raise ValidationError(_('Corrupted base64 hash'))
|
||||
# base64-i2p
|
||||
if length == 516 and re.match(r'[a-zA-Z0-9\-~]+AA$', data) == None:
|
||||
raise ValidationError(_('Invalid base64 hash'))
|
||||
# check ECDSA validity
|
||||
if length == 524 and re.match(r'[a-zA-Z0-9\-~]+AEAAEAAA==$', data) == None:
|
||||
"""
|
||||
Base64 hash validation
|
||||
"""
|
||||
# strip leading and trailing whitespaces
|
||||
data = data.strip()
|
||||
length = len(data)
|
||||
# check for b32 address misuse
|
||||
if re.match(r'.*\.b32\.i2p$', data):
|
||||
raise ValidationError(_('You should paste base64 hash, not a base32!'))
|
||||
# fail if contains .i2p= (full foo.i2p=key)
|
||||
if re.search(r'\.i2p=', data):
|
||||
raise ValidationError(_('Do not paste full hosts.txt entry! Only base64 hash is needed'))
|
||||
# check for pasting router hash
|
||||
if length == 44:
|
||||
raise ValidationError(_('Do not paste router hash! Go to i2ptunnel page and \
|
||||
find a destination hash'))
|
||||
# Minimum key length 516 bytes
|
||||
if length < 516:
|
||||
raise ValidationError(_('Specified base64 hash is less than 516 bytes'))
|
||||
# Maximum key length 616 bytes
|
||||
if length > 616:
|
||||
raise ValidationError(_('Specified base64 hash is bigger than 616 bytes'))
|
||||
# keys with cert may ends with anything, so check is relaxed
|
||||
if length > 516 and re.match(r'[a-zA-Z0-9\-~=]+$', data) == None:
|
||||
raise ValidationError(_('Invalid characters in base64 hash'))
|
||||
# base64-validity test
|
||||
if length > 516:
|
||||
# we need temporary variable here to avoid modifying main "data"
|
||||
test_data = data
|
||||
# add pad-characters needed for proper decoding cos i2p does not
|
||||
for i in range(4):
|
||||
quanta, leftover = divmod(len(test_data), 4)
|
||||
if leftover:
|
||||
test_data += '='
|
||||
else:
|
||||
break
|
||||
# if more than 2 pad chars were added, raise an error
|
||||
if i > 2:
|
||||
raise ValidationError(_('Corrupted base64 hash'))
|
||||
# base64-i2p
|
||||
if length == 516 and re.match(r'[a-zA-Z0-9\-~]+AA$', data) == None:
|
||||
raise ValidationError(_('Invalid base64 hash'))
|
||||
# check ECDSA validity
|
||||
if length == 524 and re.match(r'[a-zA-Z0-9\-~]+AEAAEAAA==$', data) == None:
|
||||
raise ValidationError(_('Invalid base64 ECDSA hash'))
|
||||
if check_uniq == True:
|
||||
# Avoid adding non-unique hashes
|
||||
qs = i2phost.objects.filter(b64hash=data)
|
||||
if qs.exists():
|
||||
raise ValidationError(_('Some host already have the same Base64 hash'))
|
||||
return data
|
||||
if check_uniq == True:
|
||||
# Avoid adding non-unique hashes
|
||||
qs = i2phost.objects.filter(b64hash=data)
|
||||
if qs.exists():
|
||||
raise ValidationError(_('Some host already have the same Base64 hash'))
|
||||
return data
|
||||
|
||||
|
||||
def validate_i2purl(data):
|
||||
""" Basic I2P URL validator """
|
||||
# convert to lowercase and strip leading and trailing whitespaces
|
||||
data = data.lower().strip()
|
||||
# check for http://, .i2p in domain and GET validity
|
||||
if re.match(r'^http://(?:.+?\.i2p)(?:/?|[/?]\S+)$', data) == None:
|
||||
raise ValidationError(_('Bad I2P url'))
|
||||
""" Basic I2P URL validator """
|
||||
# convert to lowercase and strip leading and trailing whitespaces
|
||||
data = data.lower().strip()
|
||||
# check for http://, .i2p in domain and GET validity
|
||||
if re.match(r'^http://(?:.+?\.i2p)(?:/?|[/?]\S+)$', data) == None:
|
||||
raise ValidationError(_('Bad I2P url'))
|
||||
|
@ -9,46 +9,46 @@ from pyi2phosts.lib.validation import validate_b64hash
|
||||
|
||||
|
||||
class i2phostAdminForm(forms.ModelForm):
|
||||
""" Custom form for editing hosts via admin interface """
|
||||
""" Custom form for editing hosts via admin interface """
|
||||
|
||||
def clean_name(self):
|
||||
"""Validate hostname"""
|
||||
data = self.cleaned_data['name']
|
||||
data = validate_hostname(data)
|
||||
return data
|
||||
def clean_name(self):
|
||||
"""Validate hostname"""
|
||||
data = self.cleaned_data['name']
|
||||
data = validate_hostname(data)
|
||||
return data
|
||||
|
||||
def clean_b64hash(self):
|
||||
"""Validate base64 hash"""
|
||||
data = self.cleaned_data['b64hash']
|
||||
data = validate_b64hash(data, check_uniq=False)
|
||||
return data
|
||||
def clean_b64hash(self):
|
||||
"""Validate base64 hash"""
|
||||
data = self.cleaned_data['b64hash']
|
||||
data = validate_b64hash(data, check_uniq=False)
|
||||
return data
|
||||
|
||||
|
||||
class i2phostAdmin(admin.ModelAdmin):
|
||||
def url(self, hostname):
|
||||
return '<a href="http://' + get_b32(hostname.b64hash) + '">b32</a>'
|
||||
def url(self, hostname):
|
||||
return '<a href="http://' + get_b32(hostname.b64hash) + '">b32</a>'
|
||||
|
||||
form = i2phostAdminForm
|
||||
url.allow_tags = True
|
||||
list_display = ('url', 'name', 'description', 'date_added', 'last_seen', 'expires',
|
||||
'activated', 'external')
|
||||
list_display_links = ['name']
|
||||
list_filter = ('activated', 'external', 'approved')
|
||||
search_fields = ('name', 'b64hash')
|
||||
ordering = ['-date_added']
|
||||
form = i2phostAdminForm
|
||||
url.allow_tags = True
|
||||
list_display = ('url', 'name', 'description', 'date_added', 'last_seen', 'expires',
|
||||
'activated', 'external')
|
||||
list_display_links = ['name']
|
||||
list_filter = ('activated', 'external', 'approved')
|
||||
search_fields = ('name', 'b64hash')
|
||||
ordering = ['-date_added']
|
||||
|
||||
|
||||
class PendingAdmin(i2phostAdmin):
|
||||
def queryset(self, request):
|
||||
qs = super(PendingAdmin, self).queryset(request)
|
||||
return qs.filter(approved=False)
|
||||
def queryset(self, request):
|
||||
qs = super(PendingAdmin, self).queryset(request)
|
||||
return qs.filter(approved=False)
|
||||
|
||||
def approve_selected(modeladmin, request, queryset):
|
||||
queryset.update(approved=True)
|
||||
def approve_selected(modeladmin, request, queryset):
|
||||
queryset.update(approved=True)
|
||||
|
||||
list_filter = []
|
||||
list_display = ('url', 'name', 'description', 'date_added', 'last_seen', 'expires', 'approved')
|
||||
actions = ['approve_selected']
|
||||
list_filter = []
|
||||
list_display = ('url', 'name', 'description', 'date_added', 'last_seen', 'expires', 'approved')
|
||||
actions = ['approve_selected']
|
||||
|
||||
|
||||
admin.site.register(i2phost, i2phostAdmin)
|
||||
|
@ -2,27 +2,27 @@ from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class i2phost(models.Model):
|
||||
# Hostname limit is 67 characters maximum, including the '.i2p'.
|
||||
name = models.CharField(_('I2P hostname'), max_length=67, unique=True)
|
||||
# Maximum key length 616 bytes (to account for certs up to 100 bytes).
|
||||
b64hash = models.CharField(_('Base 64 hash'), max_length=616)
|
||||
description = models.CharField(_('Description'), max_length=4096, blank=True)
|
||||
date_added = models.DateTimeField(null=True, blank=True)
|
||||
# Last time this host was up
|
||||
last_seen = models.DateTimeField(null=True, blank=True)
|
||||
# Scheduled expiration date
|
||||
expires = models.DateField(null=True, blank=True)
|
||||
# Not-activated hosts will not appear in exported hosts.txt
|
||||
activated = models.BooleanField(default=False)
|
||||
# Indicator for hosts added from external source
|
||||
external = models.BooleanField(default=False)
|
||||
# Not approved hosts will not appear in exported hosts.txt
|
||||
approved = models.BooleanField(default=False)
|
||||
# Hostname limit is 67 characters maximum, including the '.i2p'.
|
||||
name = models.CharField(_('I2P hostname'), max_length=67, unique=True)
|
||||
# Maximum key length 616 bytes (to account for certs up to 100 bytes).
|
||||
b64hash = models.CharField(_('Base 64 hash'), max_length=616)
|
||||
description = models.CharField(_('Description'), max_length=4096, blank=True)
|
||||
date_added = models.DateTimeField(null=True, blank=True)
|
||||
# Last time this host was up
|
||||
last_seen = models.DateTimeField(null=True, blank=True)
|
||||
# Scheduled expiration date
|
||||
expires = models.DateField(null=True, blank=True)
|
||||
# Not-activated hosts will not appear in exported hosts.txt
|
||||
activated = models.BooleanField(default=False)
|
||||
# Indicator for hosts added from external source
|
||||
external = models.BooleanField(default=False)
|
||||
# Not approved hosts will not appear in exported hosts.txt
|
||||
approved = models.BooleanField(default=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class PendingHost(i2phost):
|
||||
""" Proxy model needed for displaying not approved hosts in django admin separatelly """
|
||||
class Meta:
|
||||
proxy = True
|
||||
""" Proxy model needed for displaying not approved hosts in django admin separatelly """
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.conf.urls import *
|
||||
|
||||
urlpatterns = patterns('pyi2phosts.postkey.views',
|
||||
(r'^$', 'addkey'),
|
||||
(r'^success/', 'success'),
|
||||
(r'^subdomain/', 'subdomain'),
|
||||
(r'^$', 'addkey'),
|
||||
(r'^success/', 'success'),
|
||||
(r'^subdomain/', 'subdomain'),
|
||||
)
|
||||
|
@ -17,147 +17,147 @@ from pyi2phosts.lib.validation import validate_hostname
|
||||
from pyi2phosts.lib.validation import validate_b64hash
|
||||
|
||||
class AddForm(forms.ModelForm):
|
||||
"""
|
||||
This is our class for host-add form. It's based on django's ModelForm
|
||||
and uses our model "i2phost" (see postkey/models.py)
|
||||
"""
|
||||
class Meta:
|
||||
model = i2phost
|
||||
fields = ('name', 'b64hash', 'description')
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'size': '67'}),
|
||||
'b64hash': forms.Textarea(attrs={'rows': '1', 'cols': '100'}),
|
||||
'description': forms.Textarea(attrs={'rows': '2', 'cols': '72'})
|
||||
}
|
||||
def clean_name(self):
|
||||
"""Validate hostname"""
|
||||
data = self.cleaned_data['name']
|
||||
log.debug(u'hostname: %s', self.data['name'])
|
||||
data = validate_hostname(data)
|
||||
# Another set of reserved hostnames (suggested by zzz)
|
||||
if re.search(r'(^|\.)(i2p|i2p2|geti2p|mail|project|i2project|i2pproject|i2p-project).i2p$', data):
|
||||
raise forms.ValidationError(_('Trying to use hostname from additional reserved set'))
|
||||
return data
|
||||
def clean_b64hash(self):
|
||||
"""Validate base64 hash"""
|
||||
data = self.cleaned_data['b64hash']
|
||||
log.debug(u'hash: %s', self.data['b64hash'])
|
||||
data = validate_b64hash(data)
|
||||
return data
|
||||
def is_valid(self):
|
||||
"""Log validation errors"""
|
||||
is_valid = super(AddForm, self).is_valid()
|
||||
if not is_valid:
|
||||
for field in self.errors.keys():
|
||||
log.info('ValidationError: [%s]: \"%s\" %s',
|
||||
field, self.data[field], self.errors[field].as_text())
|
||||
return is_valid
|
||||
"""
|
||||
This is our class for host-add form. It's based on django's ModelForm
|
||||
and uses our model "i2phost" (see postkey/models.py)
|
||||
"""
|
||||
class Meta:
|
||||
model = i2phost
|
||||
fields = ('name', 'b64hash', 'description')
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'size': '67'}),
|
||||
'b64hash': forms.Textarea(attrs={'rows': '1', 'cols': '100'}),
|
||||
'description': forms.Textarea(attrs={'rows': '2', 'cols': '72'})
|
||||
}
|
||||
def clean_name(self):
|
||||
"""Validate hostname"""
|
||||
data = self.cleaned_data['name']
|
||||
log.debug(u'hostname: %s', self.data['name'])
|
||||
data = validate_hostname(data)
|
||||
# Another set of reserved hostnames (suggested by zzz)
|
||||
if re.search(r'(^|\.)(i2p|i2p2|geti2p|mail|project|i2project|i2pproject|i2p-project).i2p$', data):
|
||||
raise forms.ValidationError(_('Trying to use hostname from additional reserved set'))
|
||||
return data
|
||||
def clean_b64hash(self):
|
||||
"""Validate base64 hash"""
|
||||
data = self.cleaned_data['b64hash']
|
||||
log.debug(u'hash: %s', self.data['b64hash'])
|
||||
data = validate_b64hash(data)
|
||||
return data
|
||||
def is_valid(self):
|
||||
"""Log validation errors"""
|
||||
is_valid = super(AddForm, self).is_valid()
|
||||
if not is_valid:
|
||||
for field in self.errors.keys():
|
||||
log.info('ValidationError: [%s]: \"%s\" %s',
|
||||
field, self.data[field], self.errors[field].as_text())
|
||||
return is_valid
|
||||
|
||||
|
||||
class SubdomainVerifyForm(forms.Form):
|
||||
"""Form for displaying verification filename and code when verifying a subdomain"""
|
||||
filename = forms.CharField(label=_('Filename'), widget=forms.TextInput(attrs={
|
||||
'size': '20',
|
||||
'readonly': 'readonly',
|
||||
'onclick': 'this.select();',
|
||||
}))
|
||||
"""Form for displaying verification filename and code when verifying a subdomain"""
|
||||
filename = forms.CharField(label=_('Filename'), widget=forms.TextInput(attrs={
|
||||
'size': '20',
|
||||
'readonly': 'readonly',
|
||||
'onclick': 'this.select();',
|
||||
}))
|
||||
|
||||
def save_host(request):
|
||||
"""Function for saving hosts after validation or subdomain verification"""
|
||||
# avoid race conditions
|
||||
try:
|
||||
h = i2phost.objects.get(name=request.session['hostname'])
|
||||
except i2phost.DoesNotExist:
|
||||
host = i2phost(name=request.session['hostname'],
|
||||
b64hash=request.session['b64hash'],
|
||||
description=request.session['description'],
|
||||
date_added=datetime.datetime.utcnow())
|
||||
host.save()
|
||||
return redirect('pyi2phosts.postkey.views.success')
|
||||
else:
|
||||
log.warning('refusing to save already existed host: %s', request.session['hostname'])
|
||||
request.session.flush()
|
||||
return redirect('/')
|
||||
"""Function for saving hosts after validation or subdomain verification"""
|
||||
# avoid race conditions
|
||||
try:
|
||||
h = i2phost.objects.get(name=request.session['hostname'])
|
||||
except i2phost.DoesNotExist:
|
||||
host = i2phost(name=request.session['hostname'],
|
||||
b64hash=request.session['b64hash'],
|
||||
description=request.session['description'],
|
||||
date_added=datetime.datetime.utcnow())
|
||||
host.save()
|
||||
return redirect('pyi2phosts.postkey.views.success')
|
||||
else:
|
||||
log.warning('refusing to save already existed host: %s', request.session['hostname'])
|
||||
request.session.flush()
|
||||
return redirect('/')
|
||||
|
||||
def addkey(request):
|
||||
if request.method == 'POST':
|
||||
form = AddForm(request.POST)
|
||||
if form.is_valid():
|
||||
request.session['hostname'] = form.cleaned_data['name']
|
||||
request.session['b64hash'] = form.cleaned_data['b64hash']
|
||||
request.session['description'] = form.cleaned_data['description']
|
||||
if form.cleaned_data['name'].count('.') > 1:
|
||||
return redirect('pyi2phosts.postkey.views.subdomain')
|
||||
else:
|
||||
log.debug('submit is valid, saving')
|
||||
s = save_host(request)
|
||||
return s
|
||||
else:
|
||||
form = AddForm()
|
||||
return render_to_response('postkey.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
if request.method == 'POST':
|
||||
form = AddForm(request.POST)
|
||||
if form.is_valid():
|
||||
request.session['hostname'] = form.cleaned_data['name']
|
||||
request.session['b64hash'] = form.cleaned_data['b64hash']
|
||||
request.session['description'] = form.cleaned_data['description']
|
||||
if form.cleaned_data['name'].count('.') > 1:
|
||||
return redirect('pyi2phosts.postkey.views.subdomain')
|
||||
else:
|
||||
log.debug('submit is valid, saving')
|
||||
s = save_host(request)
|
||||
return s
|
||||
else:
|
||||
form = AddForm()
|
||||
return render_to_response('postkey.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
def success(request):
|
||||
if 'hostname' in request.session:
|
||||
hn = request.session['hostname']
|
||||
request.session.flush()
|
||||
return render_to_response('success_submission.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'hostname': hn,
|
||||
}, context_instance=RequestContext(request))
|
||||
else:
|
||||
return redirect('/')
|
||||
if 'hostname' in request.session:
|
||||
hn = request.session['hostname']
|
||||
request.session.flush()
|
||||
return render_to_response('success_submission.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'hostname': hn,
|
||||
}, context_instance=RequestContext(request))
|
||||
else:
|
||||
return redirect('/')
|
||||
|
||||
def subdomain(request):
|
||||
"""Subdomain verification"""
|
||||
if request.method == 'POST':
|
||||
form = SubdomainVerifyForm(request.POST)
|
||||
if form.is_valid():
|
||||
# do verification here, then redirect to success
|
||||
proxy_handler = urllib2.ProxyHandler({'http': settings.EEPROXY_URL})
|
||||
opener = urllib2.build_opener(proxy_handler)
|
||||
if 'topdomain' in request.session and 'v_filename' in request.session:
|
||||
url = 'http://' + request.session['topdomain'] + '/' + request.session['v_filename']
|
||||
else:
|
||||
log.warning('trying to call subdomain validation without a session')
|
||||
return redirect('/')
|
||||
log.info('starting http-verification of subdomain: %s', request.session['hostname'])
|
||||
try:
|
||||
log.debug('trying to open %s', url)
|
||||
resp = opener.open(url, timeout=60)
|
||||
except urllib2.URLError, e:
|
||||
if hasattr(e, 'reason'):
|
||||
log.warning('%s: failed to reach server, reason: %s', request.session['topdomain'], e.reason)
|
||||
elif hasattr(e, 'code'):
|
||||
log.warning('%s can\'t finish the request, error code: %s',
|
||||
request.session['topdomain'], e.code)
|
||||
return render_to_response('subdomain_http_verify_failure.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'code': e.code,
|
||||
}, context_instance=RequestContext(request))
|
||||
else:
|
||||
log.debug('subdomain verification success, saving host')
|
||||
s = save_host(request)
|
||||
return s
|
||||
else:
|
||||
# generate verification code and display info page to user
|
||||
v_filename = ''.join([random.choice(string.letters + string.digits) for x in xrange(16)])
|
||||
if 'hostname' in request.session:
|
||||
m = re.match('.+\.(.+\.i2p$)', request.session['hostname'])
|
||||
topdomain = m.group(1)
|
||||
else:
|
||||
return redirect('/')
|
||||
# save needed variables in session data because otherwise it will be lost
|
||||
request.session['v_filename'] = v_filename
|
||||
request.session['topdomain'] = topdomain
|
||||
form = SubdomainVerifyForm({'filename': v_filename})
|
||||
return render_to_response('subdomain_http_verify.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'hostname': request.session['hostname'],
|
||||
'topdomain': topdomain,
|
||||
'form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
"""Subdomain verification"""
|
||||
if request.method == 'POST':
|
||||
form = SubdomainVerifyForm(request.POST)
|
||||
if form.is_valid():
|
||||
# do verification here, then redirect to success
|
||||
proxy_handler = urllib2.ProxyHandler({'http': settings.EEPROXY_URL})
|
||||
opener = urllib2.build_opener(proxy_handler)
|
||||
if 'topdomain' in request.session and 'v_filename' in request.session:
|
||||
url = 'http://' + request.session['topdomain'] + '/' + request.session['v_filename']
|
||||
else:
|
||||
log.warning('trying to call subdomain validation without a session')
|
||||
return redirect('/')
|
||||
log.info('starting http-verification of subdomain: %s', request.session['hostname'])
|
||||
try:
|
||||
log.debug('trying to open %s', url)
|
||||
resp = opener.open(url, timeout=60)
|
||||
except urllib2.URLError, e:
|
||||
if hasattr(e, 'reason'):
|
||||
log.warning('%s: failed to reach server, reason: %s', request.session['topdomain'], e.reason)
|
||||
elif hasattr(e, 'code'):
|
||||
log.warning('%s can\'t finish the request, error code: %s',
|
||||
request.session['topdomain'], e.code)
|
||||
return render_to_response('subdomain_http_verify_failure.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'code': e.code,
|
||||
}, context_instance=RequestContext(request))
|
||||
else:
|
||||
log.debug('subdomain verification success, saving host')
|
||||
s = save_host(request)
|
||||
return s
|
||||
else:
|
||||
# generate verification code and display info page to user
|
||||
v_filename = ''.join([random.choice(string.letters + string.digits) for x in xrange(16)])
|
||||
if 'hostname' in request.session:
|
||||
m = re.match('.+\.(.+\.i2p$)', request.session['hostname'])
|
||||
topdomain = m.group(1)
|
||||
else:
|
||||
return redirect('/')
|
||||
# save needed variables in session data because otherwise it will be lost
|
||||
request.session['v_filename'] = v_filename
|
||||
request.session['topdomain'] = topdomain
|
||||
form = SubdomainVerifyForm({'filename': v_filename})
|
||||
return render_to_response('subdomain_http_verify.html', {
|
||||
'title': settings.SITE_NAME,
|
||||
'hostname': request.session['hostname'],
|
||||
'topdomain': topdomain,
|
||||
'form': form,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
log = get_logger(filename=settings.LOG_FILE, log_level=settings.LOG_LEVEL)
|
||||
|
@ -2,5 +2,5 @@ from django.conf.urls import *
|
||||
from pyi2phosts.search.views import SearchedHostsListsView
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', SearchedHostsListsView.as_view()),
|
||||
(r'^$', SearchedHostsListsView.as_view()),
|
||||
)
|
||||
|
@ -5,14 +5,14 @@ from pyi2phosts.lib.generic import HostsListsView
|
||||
|
||||
|
||||
class SearchedHostsListsView(HostsListsView):
|
||||
""" Renders list of hosts matching search request """
|
||||
""" Renders list of hosts matching search request """
|
||||
|
||||
def get_queryset(self):
|
||||
q = self.request.GET.get('q', '')
|
||||
fil = Q(name__icontains=q) | Q(b64hash__contains=q)
|
||||
queryset = i2phost.objects.filter(fil)
|
||||
return queryset
|
||||
def get_queryset(self):
|
||||
q = self.request.GET.get('q', '')
|
||||
fil = Q(name__icontains=q) | Q(b64hash__contains=q)
|
||||
queryset = i2phost.objects.filter(fil)
|
||||
return queryset
|
||||
|
||||
template_name = 'search_results.html'
|
||||
template_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
template_name = 'search_results.html'
|
||||
template_object_name = 'host_list'
|
||||
paginate_by = 40
|
||||
|
@ -37,9 +37,9 @@ TIME_ZONE = 'America/Chicago'
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
LANGUAGES = (
|
||||
('en', 'English'),
|
||||
('ru', 'Russian'),
|
||||
)
|
||||
('en', 'English'),
|
||||
('ru', 'Russian'),
|
||||
)
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
@ -139,6 +139,6 @@ EEPROXY_URL = 'http://127.0.0.1:4444'
|
||||
|
||||
# include local settings
|
||||
try:
|
||||
from local_settings import *
|
||||
from local_settings import *
|
||||
except ImportError:
|
||||
pass
|
||||
pass
|
||||
|
@ -1,133 +1,133 @@
|
||||
html, body {
|
||||
font-size: 12pt;
|
||||
background: #E6E6D1;
|
||||
color: #000000;
|
||||
font-size: 12pt;
|
||||
background: #E6E6D1;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
background-color: #AEB08D;
|
||||
color: #000;
|
||||
border: 1px solid #000;
|
||||
background-color: #AEB08D;
|
||||
color: #000;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
label {
|
||||
width: 8em;
|
||||
float: top;
|
||||
text-align: left;
|
||||
margin-right: 1em;
|
||||
display: block;
|
||||
width: 8em;
|
||||
float: top;
|
||||
text-align: left;
|
||||
margin-right: 1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
background: inherit;
|
||||
color: #6E735E;
|
||||
text-decoration: none;
|
||||
background: inherit;
|
||||
color: #6E735E;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:visited {
|
||||
background: inherit;
|
||||
color: #6E735E;
|
||||
text-decoration: none;
|
||||
background: inherit;
|
||||
color: #6E735E;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #000;
|
||||
background: inherit;
|
||||
color: #000;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
margin: 10px 0px 10px 0px;
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
margin: 10px 0px 10px 0px;
|
||||
}
|
||||
|
||||
tr:first-child {
|
||||
border: 1px solid #CCCCCC;
|
||||
font-weight: bold;
|
||||
border: 1px solid #CCCCCC;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr {
|
||||
border: 1px dashed #CCCCCC;
|
||||
border: 1px dashed #CCCCCC;
|
||||
}
|
||||
|
||||
td {
|
||||
max-width: 220px;
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
div.menu {
|
||||
float: left;
|
||||
margin: 0px 20px 20px 0px;
|
||||
padding: 10px 20px 20px 0px;
|
||||
border-left: solid 1px #CCCCCC;
|
||||
text-align: left;
|
||||
color: black;
|
||||
font-size: 8pt;
|
||||
clear: left; /* fixes a bug in Opera */
|
||||
width: 160px;
|
||||
float: left;
|
||||
margin: 0px 20px 20px 0px;
|
||||
padding: 10px 20px 20px 0px;
|
||||
border-left: solid 1px #CCCCCC;
|
||||
text-align: left;
|
||||
color: black;
|
||||
font-size: 8pt;
|
||||
clear: left; /* fixes a bug in Opera */
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.menu li {
|
||||
margin-left: .5em;
|
||||
margin-top: .4em;
|
||||
padding-left: .5em;
|
||||
line-height: 1.2;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
margin-left: .5em;
|
||||
margin-top: .4em;
|
||||
padding-left: .5em;
|
||||
line-height: 1.2;
|
||||
list-style-type: none;
|
||||
list-style-position: outside;
|
||||
}
|
||||
|
||||
.menu ol, ul {
|
||||
padding-left: 0em;
|
||||
margin-top: 0em;
|
||||
padding-top: 0em;
|
||||
padding-left: 0em;
|
||||
margin-top: 0em;
|
||||
padding-top: 0em;
|
||||
}
|
||||
|
||||
.errorlist {
|
||||
color: red;
|
||||
padding-left: 5em;
|
||||
color: red;
|
||||
padding-left: 5em;
|
||||
}
|
||||
|
||||
div.main {
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 22px 60px 20px 220px;
|
||||
text-align: justify;
|
||||
color: #000011;
|
||||
margin: 0px 0px 0px 0px;
|
||||
padding: 22px 60px 20px 220px;
|
||||
text-align: justify;
|
||||
color: #000011;
|
||||
}
|
||||
|
||||
div.main li {
|
||||
margin-left: 15px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
padding: 8px 0 0 0;
|
||||
font-size: 8pt;
|
||||
text-align: center;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
|
||||
div.search_host {
|
||||
position:absolute;
|
||||
right: 15px;
|
||||
position:absolute;
|
||||
right: 15px;
|
||||
}
|
||||
|
||||
div.search_host .input input {
|
||||
font-size: 10px;
|
||||
color: #6E735E;
|
||||
width: 200px;
|
||||
font-size: 10px;
|
||||
color: #6E735E;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
padding-top: 20px;
|
||||
padding-left: 100px;
|
||||
font-size: 8pt;
|
||||
padding-top: 20px;
|
||||
padding-left: 100px;
|
||||
font-size: 8pt;
|
||||
}
|
||||
.pager .page a {
|
||||
border: 1px solid #bbbbbb;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
padding: 0px 5px 0px 5px;
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
border: 1px solid #bbbbbb;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
padding: 0px 5px 0px 5px;
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
}
|
||||
.pager .current {
|
||||
border: 2px solid #444444;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
padding: 0px 5px 0px 5px;
|
||||
border: 2px solid #444444;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
padding: 0px 5px 0px 5px;
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
<html>
|
||||
|
||||
<head><title>FAIL</title></head>
|
||||
<head><title>FAIL</title></head>
|
||||
|
||||
<body bgcolor="white">
|
||||
<body bgcolor="white">
|
||||
|
||||
<center><h1>Non-I2P access denied</h1></center>
|
||||
<center><h1>Non-I2P access denied</h1></center>
|
||||
|
||||
<hr><center>py-i2phosts instance</center>
|
||||
<hr><center>py-i2phosts instance</center>
|
||||
|
||||
</body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 676 B |
@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center><h1>404 Not Found</h1></center>
|
||||
<center><h1>404 Not Found</h1></center>
|
||||
{% endblock %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center><h1>500 Internal server error</h1></center>
|
||||
<center><h1>500 Internal server error</h1></center>
|
||||
{% endblock %}
|
||||
|
@ -1,65 +1,65 @@
|
||||
{% load i18n %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
{% block title %}
|
||||
{{ title }}
|
||||
{% endblock %}
|
||||
</title>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="/static/base.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="search_host">
|
||||
<form action="/search/">
|
||||
<input class="input" name="q" maxlength="67" type="text" value="{% trans "Search host" %}"
|
||||
onblur="if (value == '') {value = '{% trans "Search host" %}'}" onfocus="if (value == '{% trans "Search host" %}')
|
||||
{value =''}" />
|
||||
<input type="submit" value="{% trans "Search" %}" />
|
||||
</form>
|
||||
</div>
|
||||
<head>
|
||||
<title>
|
||||
{% block title %}
|
||||
{{ title }}
|
||||
{% endblock %}
|
||||
</title>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="/static/base.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="search_host">
|
||||
<form action="/search/">
|
||||
<input class="input" name="q" maxlength="67" type="text" value="{% trans "Search host" %}"
|
||||
onblur="if (value == '') {value = '{% trans "Search host" %}'}" onfocus="if (value == '{% trans "Search host" %}')
|
||||
{value =''}" />
|
||||
<input type="submit" value="{% trans "Search" %}" />
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% block navigation %}
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<li><a href=/>{% trans "Home" %}</a></li>
|
||||
<li><a href={% url 'faq' %}>FAQ</a></li>
|
||||
<li><a href={% url 'latest' %}>{% trans "Browse latest hosts" %}</a></li>
|
||||
<li><a href={% url 'browse' %}>{% trans "Browse alive hosts" %}</a></li>
|
||||
<li><a href={% url 'pyi2phosts.postkey.views.addkey' %}>{% trans "Register a domain" %}</a></li>
|
||||
<li><a href={% url 'contacts' %}>{% trans "Contacts" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% block navigation %}
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<li><a href=/>{% trans "Home" %}</a></li>
|
||||
<li><a href={% url 'faq' %}>FAQ</a></li>
|
||||
<li><a href={% url 'latest' %}>{% trans "Browse latest hosts" %}</a></li>
|
||||
<li><a href={% url 'browse' %}>{% trans "Browse alive hosts" %}</a></li>
|
||||
<li><a href={% url 'pyi2phosts.postkey.views.addkey' %}>{% trans "Register a domain" %}</a></li>
|
||||
<li><a href={% url 'contacts' %}>{% trans "Contacts" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<form action="/i18n/setlang/" method="post">
|
||||
{% csrf_token %}
|
||||
{% for lang in LANGUAGES %}
|
||||
<input type="radio" value="{{ lang.0 }}" name="language" />
|
||||
<img src="/static/{{ lang.0 }}.png" width="16" height="11" alt="{{ lang.0 }}"/>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Set" />
|
||||
</form>
|
||||
<form action="/i18n/setlang/" method="post">
|
||||
{% csrf_token %}
|
||||
{% for lang in LANGUAGES %}
|
||||
<input type="radio" value="{{ lang.0 }}" name="language" />
|
||||
<img src="/static/{{ lang.0 }}.png" width="16" height="11" alt="{{ lang.0 }}"/>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Set" />
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="main">
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
<div class="main">
|
||||
{% block header %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
||||
{% block footer %}
|
||||
<hr>
|
||||
<div class="footer">
|
||||
{% block footer-addon %}
|
||||
{% endblock %}
|
||||
{% trans "powered-by" %}: <a href=http://py-i2phosts.i2p/>py-i2phosts</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</body>
|
||||
{% block footer %}
|
||||
<hr>
|
||||
<div class="footer">
|
||||
{% block footer-addon %}
|
||||
{% endblock %}
|
||||
{% trans "powered-by" %}: <a href=http://py-i2phosts.i2p/>py-i2phosts</a>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -11,10 +11,10 @@
|
||||
<table>
|
||||
<tr><td><a href="?order=name">{% trans "Host" %}</a></td><td><a href="?order=last_seen">{% trans "Last seen" %}</a></td><td><a href="?order=date_added">{% trans "Date added" %}</a></td><td>{% trans "Description" %}</td></tr>
|
||||
{% endblock table_header %}
|
||||
{% for host in host_list %}
|
||||
<tr><td><a href="http://{{ host.name }}/?i2paddresshelper={{host.b64hash}}">{{ host.name }}</a></td><td>{{ host.last_seen }}</td>
|
||||
<td>{{ host.date_added }}</td><td>{{ host.description }}</td></tr>
|
||||
{% endfor %}
|
||||
{% for host in host_list %}
|
||||
<tr><td><a href="http://{{ host.name }}/?i2paddresshelper={{host.b64hash}}">{{ host.name }}</a></td><td>{{ host.last_seen }}</td>
|
||||
<td>{{ host.date_added }}</td><td>{{ host.description }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% if is_paginated %}
|
||||
|
@ -5,17 +5,17 @@
|
||||
{% blocktrans %}
|
||||
<h3>Direct contact methods:</h3>
|
||||
<ul>
|
||||
<li>IRC: #i2p-dev, nick "slow". This is the fastest contact method and should be used first.</li>
|
||||
<li>Email: hiddenz@mail.i2p. You can send your message here, but don't expect a fast reply.</li>
|
||||
<li>IRC: #i2p-dev, nick "slow". This is the fastest contact method and should be used first.</li>
|
||||
<li>Email: hiddenz@mail.i2p. You can send your message here, but don't expect a fast reply.</li>
|
||||
</ul>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% blocktrans %}
|
||||
<h3>Public discussions about service, feedback, proposals and feature requests</h3>
|
||||
<ul>
|
||||
<li><a href="http://hiddenchan.i2p/d/res/4222.html">Thread at hiddenchan.i2p</a>: the best place for discussion (russian and english are allowed)</li>
|
||||
<li><a href="http://forum.i2p/viewtopic.php?t=5083">Thread at forum.i2p</a>: very rarely readable (english)</li>
|
||||
<li><a href="http://zzz.i2p/topics/733">py-i2phosts thread at zzz.i2p</a>: thread about py-i2phosts development, rarely readable (english)</li>
|
||||
<li><a href="http://hiddenchan.i2p/d/res/4222.html">Thread at hiddenchan.i2p</a>: the best place for discussion (russian and english are allowed)</li>
|
||||
<li><a href="http://forum.i2p/viewtopic.php?t=5083">Thread at forum.i2p</a>: very rarely readable (english)</li>
|
||||
<li><a href="http://zzz.i2p/topics/733">py-i2phosts thread at zzz.i2p</a>: thread about py-i2phosts development, rarely readable (english)</li>
|
||||
</ul>
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
||||
|
@ -4,24 +4,24 @@
|
||||
{% block content %}
|
||||
<h3>{% trans "How we are learning about new hosts" %}</h3>
|
||||
<ol>
|
||||
<li>{% trans "Pulling from external sources:" %}
|
||||
<ul>
|
||||
{% for source in sources_list %}
|
||||
<li>{{ source.url }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li>{% trans "Adding through our service." %}
|
||||
</li>
|
||||
<li>{% trans "Pulling from external sources:" %}
|
||||
<ul>
|
||||
{% for source in sources_list %}
|
||||
<li>{{ source.url }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
<li>{% trans "Adding through our service." %}
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
{% blocktrans %}
|
||||
<h3>Publishing requirements</h3>
|
||||
To get published a host must meet the following criteria:
|
||||
<ul>
|
||||
<li>Must have been added at least 3 days ago</li>
|
||||
<li>Must be up</li>
|
||||
<li>Must be approved by admin</li>
|
||||
<li>Must have been added at least 3 days ago</li>
|
||||
<li>Must be up</li>
|
||||
<li>Must be approved by admin</li>
|
||||
</ul>
|
||||
<p style="notice">Admin's approval isn't really necessary, it is only needed in
|
||||
order to eliminate possible hijacking and mass registration attempts.
|
||||
|
@ -2,55 +2,55 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% url 'faq' as faq_url %}
|
||||
{% blocktrans %}
|
||||
<h2>About</h2>
|
||||
<p>{{ title }} is a domain name registration service for I2P. Hostnames in I2P aren't
|
||||
globally unique. {{ title }} doesn't act as "central authority", it only provides a
|
||||
way to publish hosts as an easy means of access to them. You can read more about how
|
||||
I2P naming works in the <a href=http://www.i2p2.i2p/naming.html>official
|
||||
docs</a>.
|
||||
</p>
|
||||
{% url 'faq' as faq_url %}
|
||||
{% blocktrans %}
|
||||
<h2>About</h2>
|
||||
<p>{{ title }} is a domain name registration service for I2P. Hostnames in I2P aren't
|
||||
globally unique. {{ title }} doesn't act as "central authority", it only provides a
|
||||
way to publish hosts as an easy means of access to them. You can read more about how
|
||||
I2P naming works in the <a href=http://www.i2p2.i2p/naming.html>official
|
||||
docs</a>.
|
||||
</p>
|
||||
|
||||
<p>To find out how we're registering and publishing hosts, look at
|
||||
<a href={{ faq_url }}>FAQ</a> page.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
<p>To find out how we're registering and publishing hosts, look at
|
||||
<a href={{ faq_url }}>FAQ</a> page.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% blocktrans %}
|
||||
<h2>Addressbook service</h2>
|
||||
<p>
|
||||
To start getting new hostnames from {{ title }}, add this
|
||||
<a href=/export/alive-hosts.txt>subscription link</a> into your <a
|
||||
href=http://localhost:7657/susidns/subscriptions.jsp>router's
|
||||
addressbook</a>. Of course, you should <a
|
||||
href=http://localhost:7657/susidns/addressbook.jsp?book=private&hostname={{ domain }}&destination={{ b64 }}>add INR</a>'s destination before.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
{% blocktrans %}
|
||||
<h2>Addressbook service</h2>
|
||||
<p>
|
||||
To start getting new hostnames from {{ title }}, add this
|
||||
<a href=/export/alive-hosts.txt>subscription link</a> into your <a
|
||||
href=http://localhost:7657/susidns/subscriptions.jsp>router's
|
||||
addressbook</a>. Of course, you should <a
|
||||
href=http://localhost:7657/susidns/addressbook.jsp?book=private&hostname={{ domain }}&destination={{ b64 }}>add INR</a>'s destination before.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% url 'pyi2phosts.jump.views.jumper' 'example.i2p' as jump_url %}
|
||||
{% url 'pyi2phosts.jump.views.index' as jump_index %}
|
||||
{% blocktrans %}
|
||||
<h2>Jump service</h2><p>
|
||||
{{ title }} also provides a jump service. For accessing hosts through it,
|
||||
use urls like
|
||||
<a href="{{ jump_url }}">
|
||||
http://{{ domain }}{{ jump_url }}</a>.
|
||||
I2P since 0.8.3 gives possibility to add a custom jump-servers. Go to the i2ptunnel
|
||||
<a href="http://localhost:7657/i2ptunnel/edit?tunnel=0">eeproxy configuration page
|
||||
</a> and add <em>http://{{ domain }}{{ jump_index }}</em> to "Jump URL List" section.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
{% url 'pyi2phosts.jump.views.jumper' 'example.i2p' as jump_url %}
|
||||
{% url 'pyi2phosts.jump.views.index' as jump_index %}
|
||||
{% blocktrans %}
|
||||
<h2>Jump service</h2><p>
|
||||
{{ title }} also provides a jump service. For accessing hosts through it,
|
||||
use urls like
|
||||
<a href="{{ jump_url }}">
|
||||
http://{{ domain }}{{ jump_url }}</a>.
|
||||
I2P since 0.8.3 gives possibility to add a custom jump-servers. Go to the i2ptunnel
|
||||
<a href="http://localhost:7657/i2ptunnel/edit?tunnel=0">eeproxy configuration page
|
||||
</a> and add <em>http://{{ domain }}{{ jump_index }}</em> to "Jump URL List" section.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% url 'pyi2phosts.postkey.views.addkey' as addkey_url %}
|
||||
{% blocktrans %}
|
||||
<h2>Registration service</h2>
|
||||
<p>If you are running an eepsite or another service and want a human-readable domain name
|
||||
for them, consider <a href={{ addkey_url }}>registering it</a>.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
{% url 'pyi2phosts.postkey.views.addkey' as addkey_url %}
|
||||
{% blocktrans %}
|
||||
<h2>Registration service</h2>
|
||||
<p>If you are running an eepsite or another service and want a human-readable domain name
|
||||
for them, consider <a href={{ addkey_url }}>registering it</a>.
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block footer-addon %}
|
||||
<a href=http://{{ b32 }}>b32</a> | <a href=http://localhost:7657/susidns/addressbook.jsp?book=private&hostname={{ domain }}&destination={{ b64 }}>{% trans "add" %}</a> |
|
||||
<a href=http://{{ b32 }}>b32</a> | <a href=http://localhost:7657/susidns/addressbook.jsp?book=private&hostname={{ domain }}&destination={{ b64 }}>{% trans "add" %}</a> |
|
||||
{% endblock %}
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
<h2>{% trans "Domain name registration policy" %}</h2>
|
||||
<ul>
|
||||
<li>{% trans "Domain name registration is free." %}</li>
|
||||
<li>{% trans "Anyone can register a domain name." %}</li>
|
||||
<li>{% trans "Domain names are available on a 'first come, first serve' basis." %}</li>
|
||||
<li>{% trans "A domain name's destination must be active." %}
|
||||
{% trans "Inactive destinations cannot be published." %}</li>
|
||||
<li>{% trans "Domain name hoarding through mass registration (cybersquatting) is not allowed." %}</li>
|
||||
<li>{% trans "Domain name registrations will not be rejected based on content." %}</li>
|
||||
<li>{% trans "Domain name registrations will stop propagating after some period of inactivity." %}</li>
|
||||
<li>{% trans "Temporary or test sites should not be registered. Use b32 address instead." %}</li>
|
||||
<li>{% trans "Changing key for existing domains is prohibited." %}</li>
|
||||
<li>{% trans "Domain name registration is free." %}</li>
|
||||
<li>{% trans "Anyone can register a domain name." %}</li>
|
||||
<li>{% trans "Domain names are available on a 'first come, first serve' basis." %}</li>
|
||||
<li>{% trans "A domain name's destination must be active." %}
|
||||
{% trans "Inactive destinations cannot be published." %}</li>
|
||||
<li>{% trans "Domain name hoarding through mass registration (cybersquatting) is not allowed." %}</li>
|
||||
<li>{% trans "Domain name registrations will not be rejected based on content." %}</li>
|
||||
<li>{% trans "Domain name registrations will stop propagating after some period of inactivity." %}</li>
|
||||
<li>{% trans "Temporary or test sites should not be registered. Use b32 address instead." %}</li>
|
||||
<li>{% trans "Changing key for existing domains is prohibited." %}</li>
|
||||
</ul>
|
||||
|
@ -3,10 +3,10 @@
|
||||
|
||||
{% block content %}
|
||||
{% include "policy.html" %}
|
||||
<form action="/postkey/" method="post">
|
||||
{% csrf_token %}
|
||||
<h3>{% trans "Enter your domain details" %}</h3>
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Submit" %}" />
|
||||
</form>
|
||||
<form action="/postkey/" method="post">
|
||||
{% csrf_token %}
|
||||
<h3>{% trans "Enter your domain details" %}</h3>
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="{% trans "Submit" %}" />
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
@ -15,10 +15,10 @@ This file should be accessible via http://{{ topdomain }}/«filename»
|
||||
</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
<form action="/postkey/subdomain/" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="OK" />
|
||||
</form>
|
||||
<form action="/postkey/subdomain/" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="OK" />
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -13,18 +13,18 @@ from pyi2phosts.lib.generic import HostsListsView
|
||||
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', LocalTemplateView.as_view(template_name='index.html'), name='index'),
|
||||
url(r'^contacts/$', LocalTemplateView.as_view(template_name='contacts.html'), name='contacts'),
|
||||
url(r'^faq/$', FaqView.as_view(), name='faq'),
|
||||
url(r'^browse/$', HostsListsView.as_view(), name='browse'),
|
||||
url(r'^browse/rss/$', AliveHostsFeed(), name='browse-rss'),
|
||||
url(r'^$', LocalTemplateView.as_view(template_name='index.html'), name='index'),
|
||||
url(r'^contacts/$', LocalTemplateView.as_view(template_name='contacts.html'), name='contacts'),
|
||||
url(r'^faq/$', FaqView.as_view(), name='faq'),
|
||||
url(r'^browse/$', HostsListsView.as_view(), name='browse'),
|
||||
url(r'^browse/rss/$', AliveHostsFeed(), name='browse-rss'),
|
||||
|
||||
(r'^latest/', include('pyi2phosts.latest.urls')),
|
||||
(r'^search/$', include('pyi2phosts.search.urls')),
|
||||
(r'^postkey/', include('pyi2phosts.postkey.urls')),
|
||||
(r'^jump/', include('pyi2phosts.jump.urls')),
|
||||
(r'^api/', include('pyi2phosts.api.urls')),
|
||||
(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
(r'^latest/', include('pyi2phosts.latest.urls')),
|
||||
(r'^search/$', include('pyi2phosts.search.urls')),
|
||||
(r'^postkey/', include('pyi2phosts.postkey.urls')),
|
||||
(r'^jump/', include('pyi2phosts.jump.urls')),
|
||||
(r'^api/', include('pyi2phosts.api.urls')),
|
||||
(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
# Example:
|
||||
# (r'^pyi2phosts.', include('pyi2phosts.foo.urls')),
|
||||
|
||||
@ -37,5 +37,5 @@ urlpatterns = patterns('',
|
||||
)
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += patterns('', (r'static/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': settings.MEDIA_ROOT, 'show_indexes':True}))
|
||||
urlpatterns += patterns('', (r'static/(?P<path>.*)$', 'django.views.static.serve',
|
||||
{'document_root': settings.MEDIA_ROOT, 'show_indexes':True}))
|
||||
|
28
setup.py
28
setup.py
@ -8,28 +8,28 @@ setup(
|
||||
author_email='hiddenz@mail.i2p',
|
||||
url='http://py-i2phosts.i2p/',
|
||||
packages=['pyi2phosts',
|
||||
'pyi2phosts.postkey',
|
||||
'pyi2phosts.postkey.templatetags',
|
||||
'pyi2phosts.jump',
|
||||
'pyi2phosts.extsources',
|
||||
'pyi2phosts.lib',
|
||||
'pyi2phosts.search',
|
||||
'pyi2phosts.latest'],
|
||||
'pyi2phosts.postkey',
|
||||
'pyi2phosts.postkey.templatetags',
|
||||
'pyi2phosts.jump',
|
||||
'pyi2phosts.extsources',
|
||||
'pyi2phosts.lib',
|
||||
'pyi2phosts.search',
|
||||
'pyi2phosts.latest'],
|
||||
package_dir = {'': ''},
|
||||
package_data = {
|
||||
'pyi2phosts': ['templates/*.html', 'static/*', 'locale/*/*/*']},
|
||||
'pyi2phosts': ['templates/*.html', 'static/*', 'locale/*/*/*']},
|
||||
scripts=['bin/py-i2phosts-master', 'bin/py-i2phosts-builder', 'bin/py-i2phosts-checker',
|
||||
'bin/py-i2phosts-fetcher', 'bin/py-i2phosts-injector', 'bin/py-i2phosts-maint'],
|
||||
'bin/py-i2phosts-fetcher', 'bin/py-i2phosts-injector', 'bin/py-i2phosts-maint'],
|
||||
data_files=[('/etc/py-i2phosts', ['conf/master.conf', 'conf/checker.conf', 'conf/fetcher.conf',
|
||||
'conf/maintainer.conf', 'conf/builder.conf', 'conf/common.conf', 'conf/injector.conf'],),
|
||||
('/var/log/py-i2phosts', ['.placeholder'],),
|
||||
('/var/run/py-i2phosts', ['.placeholder'],),],
|
||||
'conf/maintainer.conf', 'conf/builder.conf', 'conf/common.conf', 'conf/injector.conf'],),
|
||||
('/var/log/py-i2phosts', ['.placeholder'],),
|
||||
('/var/run/py-i2phosts', ['.placeholder'],),],
|
||||
classifiers=[
|
||||
'Development Status :: 4 - Beta',
|
||||
'Environment :: Console',
|
||||
'Environment :: Console',
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'Intended Audience :: System Administrators',
|
||||
'Intended Audience :: System Administrators',
|
||||
'License :: OSI Approved :: GPL License',
|
||||
'Operating System :: Linux',
|
||||
'Programming Language :: Python',
|
||||
|
Loading…
x
Reference in New Issue
Block a user