Browse Source

Convert tabs to spaces

pull/1/head
Hidden Z 9 years ago
parent
commit
6e3862d7ab
  1. 32
      bin/py-i2phosts-builder
  2. 98
      bin/py-i2phosts-checker
  3. 166
      bin/py-i2phosts-fetcher
  4. 108
      bin/py-i2phosts-injector
  5. 152
      bin/py-i2phosts-maint
  6. 200
      bin/py-i2phosts-master
  7. 2
      pyi2phosts/api/urls.py
  8. 16
      pyi2phosts/api/views.py
  9. 4
      pyi2phosts/extsources/admin.py
  10. 18
      pyi2phosts/extsources/models.py
  11. 4
      pyi2phosts/jump/urls.py
  12. 80
      pyi2phosts/jump/views.py
  13. 4
      pyi2phosts/latest/urls.py
  14. 36
      pyi2phosts/latest/views.py
  15. 80
      pyi2phosts/lib/generic.py
  16. 40
      pyi2phosts/lib/rss.py
  17. 92
      pyi2phosts/lib/utils.py
  18. 212
      pyi2phosts/lib/validation.py
  19. 58
      pyi2phosts/postkey/admin.py
  20. 42
      pyi2phosts/postkey/models.py
  21. 6
      pyi2phosts/postkey/urls.py
  22. 262
      pyi2phosts/postkey/views.py
  23. 2
      pyi2phosts/search/urls.py
  24. 18
      pyi2phosts/search/views.py
  25. 10
      pyi2phosts/settings.py
  26. 144
      pyi2phosts/static-common/base.css
  27. 10
      pyi2phosts/static-common/inproxy.html
  28. BIN
      pyi2phosts/static-common/rss-grey-18.png
  29. 2
      pyi2phosts/templates/404.html
  30. 2
      pyi2phosts/templates/500.html
  31. 108
      pyi2phosts/templates/base.html
  32. 8
      pyi2phosts/templates/browse.html
  33. 10
      pyi2phosts/templates/contacts.html
  34. 24
      pyi2phosts/templates/faq.html
  35. 88
      pyi2phosts/templates/index.html
  36. 20
      pyi2phosts/templates/policy.html
  37. 12
      pyi2phosts/templates/postkey.html
  38. 10
      pyi2phosts/templates/subdomain_http_verify.html
  39. 28
      pyi2phosts/urls.py
  40. 28
      setup.py

32
bin/py-i2phosts-builder

@ -7,32 +7,32 @@ import configobj @@ -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) @@ -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) @@ -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()

98
bin/py-i2phosts-checker

@ -10,34 +10,34 @@ import time @@ -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) @@ -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(':') @@ -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')

166
bin/py-i2phosts-fetcher

@ -15,32 +15,32 @@ import socket @@ -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) @@ -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) @@ -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()

108
bin/py-i2phosts-injector

@ -10,34 +10,34 @@ from django.core.exceptions import ValidationError @@ -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 @@ -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()

152
bin/py-i2phosts-maint

@ -10,37 +10,37 @@ import configobj @@ -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) @@ -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()

200
bin/py-i2phosts-master

@ -13,119 +13,125 @@ import threading @@ -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_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()
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)
def run_fetcher():
fetcher = FetcherThread()
fetcher.start()
return fetcher
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()
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) @@ -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()

2
pyi2phosts/api/urls.py

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
from django.conf.urls import *
urlpatterns = patterns('pyi2phosts.api.views',
url(r'^all/$', 'all'),
url(r'^all/$', 'all'),
)

16
pyi2phosts/api/views.py

@ -6,11 +6,11 @@ from pyi2phosts.postkey.models import i2phost @@ -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
pyi2phosts/extsources/admin.py

@ -4,7 +4,7 @@ from pyi2phosts.extsources.models import ExternalSource @@ -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)

18
pyi2phosts/extsources/models.py

@ -3,14 +3,14 @@ from django.db import models @@ -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

4
pyi2phosts/jump/urls.py

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
from django.conf.urls import *
urlpatterns = patterns('pyi2phosts.jump.views',
(r'^([^$/]+)', 'jumper'),
(r'', 'index'),
(r'^([^$/]+)', 'jumper'),
(r'', 'index'),
)

80
pyi2phosts/jump/views.py

@ -10,45 +10,45 @@ from pyi2phosts.postkey.models import i2phost @@ -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('/')

4
pyi2phosts/latest/urls.py

@ -3,7 +3,7 @@ from pyi2phosts.lib.rss import LatestHostsFeed @@ -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'),
)

36
pyi2phosts/latest/views.py

@ -6,25 +6,25 @@ from pyi2phosts.postkey.models import i2phost @@ -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

80
pyi2phosts/lib/generic.py

@ -9,58 +9,58 @@ from pyi2phosts.postkey.models import i2phost @@ -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

40
pyi2phosts/lib/rss.py

@ -5,33 +5,33 @@ from pyi2phosts.postkey.models import i2phost @@ -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()

92
pyi2phosts/lib/utils.py

@ -8,59 +8,59 @@ import base64 @@ -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

212
pyi2phosts/lib/validation.py

@ -9,116 +9,116 @@ from pyi2phosts.postkey.models import i2phost @@ -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'))

58
pyi2phosts/postkey/admin.py

@ -9,46 +9,46 @@ from pyi2phosts.lib.validation import validate_b64hash @@ -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)

42
pyi2phosts/postkey/models.py

@ -2,27 +2,27 @@ from django.db import models @@ -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

6
pyi2phosts/postkey/urls.py

@ -1,7 +1,7 @@ @@ -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'),
)

262
pyi2phosts/postkey/views.py

@ -17,147 +17,147 @@ from pyi2phosts.lib.validation import validate_hostname @@ -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
pyi2phosts/search/urls.py

@ -2,5 +2,5 @@ from django.conf.urls import * @@ -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()),
)

18
pyi2phosts/search/views.py

@ -5,14 +5,14 @@ from pyi2phosts.lib.generic import HostsListsView @@ -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

10
pyi2phosts/settings.py

@ -37,9 +37,9 @@ TIME_ZONE = 'America/Chicago' @@ -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' @@ -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

144
pyi2phosts/static-common/base.css

@ -1,133 +1,133 @@ @@ -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;
}

10
pyi2phosts/static-common/inproxy.html

@ -1,13 +1,13 @@ @@ -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>

BIN
pyi2phosts/static-common/rss-grey-18.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 676 B

2
pyi2phosts/templates/404.html

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block content %}
<center><h1>404 Not Found</h1></center>
<center><h1>404 Not Found</h1></center>
{% endblock %}

2
pyi2phosts/templates/500.html

@ -1,5 +1,5 @@ @@ -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 %}

108
pyi2phosts/templates/base.html

@ -1,65 +1,65 @@ @@ -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>

8
pyi2phosts/templates/browse.html

@ -11,10 +11,10 @@ @@ -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 %}

10
pyi2phosts/templates/contacts.html

@ -5,17 +5,17 @@ @@ -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 %}

24
pyi2phosts/templates/faq.html

@ -4,24 +4,24 @@ @@ -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.

88
pyi2phosts/templates/index.html

@ -2,55 +2,55 @@ @@ -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 %}

20
pyi2phosts/templates/policy.html

@ -2,14 +2,14 @@ @@ -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>

12
pyi2phosts/templates/postkey.html

@ -3,10 +3,10 @@ @@ -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 %}

10
pyi2phosts/templates/subdomain_http_verify.html

@ -15,10 +15,10 @@ This file should be accessible via http://{{ topdomain }}/&laquo;filename&raquo; @@ -15,10 +15,10 @@ This file should be accessible via http://{{ topdomain }}/&laquo;filename&raquo;
</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 %}

28
pyi2phosts/urls.py

@ -13,18 +13,18 @@ from pyi2phosts.lib.generic import HostsListsView @@ -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'),
(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')),
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')),
# Example:
# (r'^pyi2phosts.', include('pyi2phosts.foo.urls')),
@ -37,5 +37,5 @@ urlpatterns = patterns('', @@ -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

@ -8,28 +8,28 @@ setup( @@ -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…
Cancel
Save